Merge lp:~marmyshev/openlp/presentation into lp:openlp

Proposed by Dmitriy Marmyshev
Status: Superseded
Proposed branch: lp:~marmyshev/openlp/presentation
Merge into: lp:openlp
Diff against target: 1182 lines (+996/-20)
11 files modified
.bzrignore (+1/-0)
openlp/core/lib/pluginmanager.py (+0/-5)
openlp/core/ui/firsttimeform.py (+1/-4)
openlp/core/ui/firsttimewizard.py (+5/-11)
openlp/plugins/presentations/lib/keynotemaccontroller.py (+362/-0)
openlp/plugins/presentations/lib/powerpointmaccontroller.py (+399/-0)
openlp/plugins/presentations/lib/presentationtab.py (+12/-0)
openlp/plugins/presentations/presentationplugin.py (+12/-0)
scripts/check_dependencies.py (+18/-0)
tests/functional/openlp_plugins/presentations/test_keynotemaccontroller.py (+93/-0)
tests/functional/openlp_plugins/presentations/test_powepointmaccontroller.py (+93/-0)
To merge this branch: bzr merge lp:~marmyshev/openlp/presentation
Reviewer Review Type Date Requested Status
matysek (community) Approve
Tim Bentley Pending
Dmitriy Marmyshev Pending
Andreas Preikschat Pending
Raoul Snyman Pending
Review via email: mp+185973@code.launchpad.net

This proposal supersedes a proposal from 2013-04-06.

This proposal has been superseded by a proposal from 2013-10-24.

Description of the change

Add support to load presentations with PowerPoint and Keynote on Mac OS X.

To post a comment you must log in.
Revision history for this message
Andreas Preikschat (googol-deactivatedaccount) wrote : Posted in a previous version of this proposal

Please don't commit uncommented things. Please remove lines 10+11, 86, 112 and more.

112 + #except pywintypes.com_error:
113 + except:

Don't commit the uncommented line. You should catch the generic exception (I don't know why you commented except pywintypes.com_error out). Also you should log that you cought an exception.

Line 123: Should be openlp_settings = Settings(u'openlp.org', u'OpenLP')
We use u'' for strings. And you missed the space after the comma.

Line 127: You have two spaces there (should be just one)

Line 128 (and some more): Don't use <>, rather use != for inequality.

And please add some more comments. Regards

PS: Unable to test the code here, as I don't have a MAC

review: Needs Fixing
Revision history for this message
Tim Bentley (trb143) wrote : Posted in a previous version of this proposal

Will need to be approved by matysek_

Revision history for this message
matysek (mzibricky) wrote : Posted in a previous version of this proposal

> Will need to be approved by matysek_

I will need to test it.

Could you please add the 'appscript' osx dependency to the following script?

./scripts/check_dependencies.py

Thanks

Revision history for this message
matysek (mzibricky) wrote : Posted in a previous version of this proposal

I tested the branch and trying to load a presentation in openlp. MS powerpoint is started with openlp.

These are the issues I get:
- when the user quits manually powerpoint before exiting openlp - powerpoint is started again after openlp exits.
- when trying to load a ppt presentation - .ppt file is loaded into powerpoint but new presentation item is not added to openlp and I get the following exception:

cat openlp_trace.txt
 --- Exception Traceback ---
Traceback (most recent call last):
  File "/Users/martin/Documents/openlp/bug-836574/openlp/core/lib/mediamanageritem.py", line 334, in onFileClick
    self.validateAndLoad(files)
  File "/Users/martin/Documents/openlp/bug-836574/openlp/core/lib/mediamanageritem.py", line 383, in validateAndLoad
    self.loadList(full_list)
  File "/Users/martin/Documents/openlp/bug-836574/openlp/plugins/presentations/lib/mediaitem.py", line 188, in loadList
    doc.load_presentation()
  File "/Users/martin/Documents/openlp/bug-836574/openlp/plugins/presentations/lib/powerpointmaccontroller.py", line 153, in load_presentation
    self.create_thumbnails()
  File "/Users/martin/Documents/openlp/bug-836574/openlp/plugins/presentations/lib/powerpointmaccontroller.py", line 175, in create_thumbnails
    for filename in os.listdir(temp_dir):
OSError: [Errno 2] No such file or directory: '/Users/martin/Library/Application Support/openlp/Data/presentations/thumbnails/Chapter08.ppt/Chapter08.ppt'

--- System information ---
Platform: Darwin-11.4.2-x86_64-i386-64bit

--- Library Versions ---
Python: 2.7.3
Qt4: 4.8.4
Phonon: 4.6.0
PyQt4: 4.9.6
QtWebkit: 534.34
SQLAlchemy: 0.7.9
SQLAlchemy Migrate: 0.7.2
BeautifulSoup: 3.2.1
lxml: 2.3.2
Chardet: 2.0.1
PyEnchant: 1.6.5
PySQLite: -
Mako: 0.7.3
pyUNO bridge: -

Revision history for this message
matysek (mzibricky) : Posted in a previous version of this proposal
review: Needs Fixing
Revision history for this message
Andreas Preikschat (googol-deactivatedaccount) wrote : Posted in a previous version of this proposal

Just a note: when you are ready with your changes (fixed) which were requested then resubmit your proposal (upper-left corner -> Resubmit proposal). Then the developers know that your code base is again read to be reviewed.

Keep it up :)

Revision history for this message
matysek (mzibricky) wrote : Posted in a previous version of this proposal

Today I tested the r2118 - the temp_dir check and relaunching ppt are fixed.

There are some other issues:

- when opening a presentation the preview of the 1st slide is not generated in openlp. Last time it was created.
- What is the use case scenario when the powerpoint should be started?
  - Should it start directly with openlp when enabled and powerpoint is installed?
  - Or start it later when .ppt file is imported?
  - now it sometimes start with openlp
  - when powerpoint is started with openlp it is put above openlp window and openlp is thus hidden
- powerpoint and openlp icon is not visible in the dock bar - last time there were visible both.
- when a .ppt file is opened and I click on the preview button - powerpoint window takes focus and is put above openlp. But then in openlp i get message like

  "The presentation /Users/martin/Downloads/lecture22.ppt is incomplete, please reload."

Revision history for this message
Dmitriy Marmyshev (marmyshev) wrote : Posted in a previous version of this proposal

Yes, matysek, the work with PPT on Mac OS is really really hard.

There are some other issues with it:
for example, if your are in slideshow mode (with presenter view) and switch
to another app - PPT hide slideshow window from second screen. or if you
send command to PPT "Next slide" or "Go to slide N" the PPT wont do this
until you activate the PPT app.
If you use "full screen" mode (without presenter view) on second screen the
PPT app change global settings of display to "Mirror displays" and after
slideshow exits the PPT app change displays back to "extention" mode.
As it seems, the behavior of PPT will be different then on Windows platform.
I also analyzed other applications (like ProPresenter, Mediashout, new
EasyWorship) they have the same issue. They solve it in two ways:
1st - convert PPT file to its own slide's format and forget about MS PPT
forever.
2nd - use MS PPT app on Mac as launched app from their app - by sending
command (as i do) to MS PPT to start slideshow with .ppt file from service
schedule. and all controlling continuing in MS PPT, after MS PPT finish
slideshow the controlling of second screen returns back to their app.

I think, the best we can do - is the 2nd way.

Almost the same is for Keynote app controller.

So, I want to change the behavior of mac-controllers and then will propose
to merge again.

On Tue, Feb 12, 2013 at 2:03 AM, matysek <email address hidden> wrote:

> Today I tested the r2118 - the temp_dir check and relaunching ppt are
> fixed.
>
> There are some other issues:
>
> - when opening a presentation the preview of the 1st slide is not
> generated in openlp. Last time it was created.
> - What is the use case scenario when the powerpoint should be started?
> - Should it start directly with openlp when enabled and powerpoint is
> installed?
> - Or start it later when .ppt file is imported?
> - now it sometimes start with openlp
> - when powerpoint is started with openlp it is put above openlp window
> and openlp is thus hidden
> - powerpoint and openlp icon is not visible in the dock bar - last time
> there were visible both.
> - when a .ppt file is opened and I click on the preview button -
> powerpoint window takes focus and is put above openlp. But then in openlp i
> get message like
>
> "The presentation /Users/martin/Downloads/lecture22.ppt is incomplete,
> please reload."
> --
> https://code.launchpad.net/~marmyshev/openlp/presentation/+merge/143389
> You are the owner of lp:~marmyshev/openlp/presentation.
>

Revision history for this message
Jonathan Corwin (j-corwin) wrote : Posted in a previous version of this proposal

You may have tried this, but is it possible to re-activate the PowerPoint window prior to sending the commands? There looks to be some "Activate" applescript command to do this?

Revision history for this message
Dmitriy Marmyshev (marmyshev) wrote : Posted in a previous version of this proposal

Yes, now I use "Activate()" command for changing slides in PPT, but this
looks ugly.
This is not understandable for user's mind.

And also Keynote has the same problem as PPT - if you activate another app
during slideshow it hides slideshow window from second screen.

On Wed, Feb 13, 2013 at 2:58 PM, Jonathan Corwin <email address hidden> wrote:

> You may have tried this, but is it possible to re-activate the PowerPoint
> window prior to sending the commands? There looks to be some "Activate"
> applescript command to do this?
> --
> https://code.launchpad.net/~marmyshev/openlp/presentation/+merge/143389
> You are the owner of lp:~marmyshev/openlp/presentation.
>

Revision history for this message
Andreas Preikschat (googol-deactivatedaccount) wrote : Posted in a previous version of this proposal

Any progress on this?

Revision history for this message
Andreas Preikschat (googol-deactivatedaccount) wrote : Posted in a previous version of this proposal

- You have a conflict in 'openlp/plugins/presentations/lib/presentationtab.py'

This
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
should be
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
We moved from 80 characters to 120.

Please do not add commented lines (e. g. 207, 221, ...).

review: Needs Fixing
Revision history for this message
Andreas Preikschat (googol-deactivatedaccount) wrote : Posted in a previous version of this proposal

If you need help or have any questions in regard to my comments feel free to ask. :)

Revision history for this message
Andreas Preikschat (googol-deactivatedaccount) wrote : Posted in a previous version of this proposal

Please do not do this:
55 +from appscript import *
rather do
import appscript

59 +
60 +log = logging.getLogger(__name__)
61 +
62 +class KeynoteController(PresentationController)
Before and after the "log = " should be two blank lines

106 + if self.process and len(self.process.slideshows()) > 0:
When dealing with lists do "if not my_list" instead of "if len(my_list) > 0"

387 + text = ''
391 + text += shape.TextFrame.TextRange.Text + '\n'
If not explicit wanted do not use strings; use unicode strings: u''

review: Needs Fixing
Revision history for this message
matysek (mzibricky) wrote : Posted in a previous version of this proposal

I'm testing it with macoffice 2011 but:
- the macoffice is not recognized by openl
- all the presentation controllers are greyed out in properties
- in the log there is a text like

2013-03-23 13:22:37,125 presentationplugin INFO Presentations Initialising
2013-03-23 13:22:37,140 openlp.plugins.presentations.lib.keynotemaccontroller DEBUG check_available
2013-03-23 13:22:37,140 openlp.plugins.presentations.lib.impresscontroller DEBUG check_available
2013-03-23 13:22:37,141 openlp.plugins.presentations.lib.powerpointmaccontroller DEBUG check_available
2013-03-23 13:22:37,141 openlp.plugins.presentations.lib.pptviewcontroller DEBUG check_available
2013-03-23 13:22:37,142 openlp.core.ui.mediadockmanager DEBUG Inserting Presentations dock
2013-03-23 13:22:37,145 openlp.plugins.presentations.lib.keynotemaccontroller DEBUG start_process
2013-03-23 13:22:37,146 presentationplugin WARNING Failed to start controller process
2013-03-23 13:22:37,146 openlp.plugins.presentations.lib.powerpointmaccontroller DEBUG start_process
2013-03-23 13:22:37,146 presentationplugin WARNING Failed to start controller process
2013-03-23 13:22:37,148 openlp.core.lib.pluginmanager INFO Initialisation Complete for presentations

review: Needs Information
Revision history for this message
matysek (mzibricky) wrote : Posted in a previous version of this proposal

It's probably not working because with our latest style fixes you replaced
  from appscript import *
with
  import appscript

However, you should then also add prefix appscript to the objects you use from that module like

  appscript.app
  appscript.k
  appscript.CantLaunchApplicationError

463 + def check_available(self):
464 + """
465 + PowerPoint is able to run on this machine
466 + """
467 + log.debug(u'check_available')
468 + return True

Is there a better way to check availability of powerpoint than only return true?
- probably try to create process by appscript.app() and catch for error appscript.ApplicationNotFoundError

When I add .ppt file to openlp and click on the 'preview'button I get message like

  'The Presentation file.ppt is incomplete, please reload.'

review: Needs Fixing
Revision history for this message
Dmitriy Marmyshev (marmyshev) wrote : Posted in a previous version of this proposal

I returned back the import. Probably it's temperary.
Please, try all other behaviors of controllers.

Revision history for this message
Andreas Preikschat (googol-deactivatedaccount) wrote : Posted in a previous version of this proposal

The presentation plugin has been disabled in other places too, you have to enable it there as well.

openlp/core/ui/firsttimeform.py
openlp/core/ui/firsttimewizard.py

(Or search for "TODO Presentation plugin is not yet working on Mac OS X." in the source).

review: Needs Fixing
Revision history for this message
Andreas Preikschat (googol-deactivatedaccount) wrote : Posted in a previous version of this proposal

168 + except appscript.CantLaunchApplicationError:
169 + pass
Please log that the app could not be launched.

285 + except appscript.CommandError:
286 + pass
Please log your error (u'Could not close the presentation')

299 + if len(windows) == 0:
302 + if len(slideshows) == 0:
Just do "if my_list"

304 + except:
305 + return False
Please add a log message. Probably you should log the whole exception, so it is available when debugging.

Lines 550-560: Is this code needed? If not, remove it. If you need it for "information purpose" then rather add it to the methods docs or add a comment.

Can you explain lines 607-613. "Could not create tmp dir"? There is a function (check_directory_exists) which can be used to check/create dirs.

608 + self.presentation.save(in_ = thumbnail_folder, as_ = appscript.k.save_as_PNG)
Should be self.presentation.save(in_=thumbnail_folder, as_=appscript.k.save_as_PNG)
(No spaces around the "=" sign after keyword arguments.)

837 + text = u''
838 + for idx in range(len(shapes)):
839 + shape = shapes[idx + 1]
840 + if shape.has_text_frame():
841 + text += shape.text_frame.text_range.content() + '\n'
842 + return text
Better do:
return u'\n'.join([shape.text_frame.text_range.content() for shape in shapes if shape.has_text_frame()])
But please test if it works, I haven't tested it. Also your function has a '\n' at the end (mine does not), is this wanted?

913 + u'PresentationModeUseSecondary': u'',
914 + u'PresentationModeEnableFeedbackDisplay': False,
915 + u'PresentationModePlayWellWithOthers': False,
What are these needed for? Why do they not follow the section/key_name conversion?

Thanks for your code, if you have any questions to my comment feel free to contact me (see my profile, or just ask here). I don't want to scare you off, but we have to make sure that the code works as best as possible (we have hardly any MAC devs and the presentation plugin was always a bit our sorrow plugin ;) ).

review: Needs Fixing
Revision history for this message
Dmitriy Marmyshev (marmyshev) wrote : Posted in a previous version of this proposal

299 + if len(windows) == 0:
302 + if len(slideshows) == 0:
> Just do "if my_list"

windows could be not "None" but could has 0 items and "if windows:" returns true - because of appscript reference.

550-560 - I still didnt decide how to solve the probleme of changing settins by user. This is in progress.

607-613: PPT for mac when you save slides as PNG creates dir for slides with same name as file name if the full path of presentation contains non-latin symbols. Probably it's not needed to log, but the algorithm support current work of MS PPT.

837-842: I dont know why this subs are exist in controllers for Windows PPT. I just added the same for support. I didnt find any references to this in other code.

913-915: is needed for setting up values for Keynote. this is because Core devs has changes Settins() system. I need this just in keynotecontroller, may be you could advise how to place it there and append to __default_settings__ of the plugin? I will need to append some setting keys with the same way in pptmaccontroller.

Agree with all other comments to my code. Will fix it soon.

Revision history for this message
Andreas Preikschat (googol-deactivatedaccount) wrote : Posted in a previous version of this proposal

> 299 + if len(windows) == 0:
> 302 + if len(slideshows) == 0:
> > Just do "if my_list"
>
> windows could be not "None" but could has 0 items and "if windows:" returns
> true - because of appscript reference.

if not my_list:
    print('if')
else:
    print('else')
When the list is empty then this will print "if". I am not sure what you mean with "windows could not be None". (Btw, I said you should write "if my_list" which is wrong, it should be "if not my_list")

Or do you mean windows being None causing an exception being caught returning False?

> 913-915: is needed for setting up values for Keynote. this is because Core
> devs has changes Settins() system. I need this just in keynotecontroller, may
> be you could advise how to place it there and append to __default_settings__
> of the plugin? I will need to append some setting keys with the same way in
> pptmaccontroller.

Just add your settings to the dict, as you already did. Just give it decent names; please no cryptic ones ;)

Revision history for this message
Dmitriy Marmyshev (marmyshev) wrote : Posted in a previous version of this proposal

> 299 + if len(windows) == 0:
> 302 + if len(slideshows) == 0:

I mean, that because of third part module "appscript" and because of Microsoft's coders, the varaible "windows" could be a list, sometimes could a None and sometimes could be something else (like const appscript.k.missing_value). So "if len(windows) == 0:" returns false ONLY when if windows - is a list and not empty. I think this is easier. Also Keynote always returns a list even empty list. I copied for simularity with keynote controller. May be you have better idea? Ready to use it! :)

> Just add your settings to the dict, as you already did. Just give it decent names; please no cryptic ones ;)

Sorry, What do you mean? Should I write a letter to Apple Developrers of Keynote with advise to use more dicent names for thier setting's varaibles? :) (178-193)

Revision history for this message
Dmitriy Marmyshev (marmyshev) wrote : Posted in a previous version of this proposal

304-305 not needed to log.

Revision history for this message
matysek (mzibricky) wrote : Posted in a previous version of this proposal

I cannot test it properly since I cannot open a presentation file:
- when I click on 'Select a new Presentation' button, then
- 'Select Presentation(s)' dialog is opened but immediately disappears
- and there is no time to select any file in that window.
- when the powerpoint integration is disabled then the dialog does not disappear anymore and keeps opened

My guess would be that something is stealing focus of the openlp window or pyqt generates any signals that causes this dialog to disappear.

What is working:
- powerpoint is started when openlp is started
- powerpoint is stopped when openlp is stopped
- if powerpoint is disabled in openl then it is not started

review: Needs Fixing
Revision history for this message
matysek (mzibricky) wrote : Posted in a previous version of this proposal

I'm not sure if the issue with the 'select presentations' dialog is specific to the osx powerpoint code or to the presentation plugin in general.

Revision history for this message
Dmitriy Marmyshev (marmyshev) wrote : Posted in a previous version of this proposal

I didnt touch anything in the plugin. I just added new controllers.
I always used DnD and this worked fine.

Just now, I've tried to click "load new presentations" on the toolbar - and
now it doesnt works - the gialog always hides.

On Sun, Apr 7, 2013 at 12:49 AM, matysek <email address hidden> wrote:

> I'm not sure if the issue with the 'select presentations' dialog is
> specific to the osx powerpoint code or to the presentation plugin in
> general.
> --
> https://code.launchpad.net/~marmyshev/openlp/presentation/+merge/157509
> You are the owner of lp:~marmyshev/openlp/presentation.
>

Revision history for this message
Dmitriy Marmyshev (marmyshev) wrote : Posted in a previous version of this proposal

try this:
1. launch OpenLP
2. turn off Presentation plugin
3. Quit app
4. Launch OpenLP
5. turn on Presentation plugin
6. Add some presentations for tests, using "Select presentations" dialog.

This should works for now.

On Sun, Apr 7, 2013 at 12:49 AM, matysek <email address hidden> wrote:

> I'm not sure if the issue with the 'select presentations' dialog is
> specific to the osx powerpoint code or to the presentation plugin in
> general.
> --
> https://code.launchpad.net/~marmyshev/openlp/presentation/+merge/157509
> You are the owner of lp:~marmyshev/openlp/presentation.
>

Revision history for this message
matysek (mzibricky) wrote : Posted in a previous version of this proposal

> I didnt touch anything in the plugin. I just added new controllers.
> I always used DnD and this worked fine.

What is it DnD?

> Just now, I've tried to click "load new presentations" on the toolbar - and
> now it doesnt works - the gialog always hides.

Then this should be an issue of the presentation plugin as a whole.

Revision history for this message
matysek (mzibricky) wrote : Posted in a previous version of this proposal

> try this:
> 1. launch OpenLP
> 2. turn off Presentation plugin
> 3. Quit app
> 4. Launch OpenLP
> 5. turn on Presentation plugin
> 6. Add some presentations for tests, using "Select presentations" dialog.
>
> This should works for now.

These instructions work.

How the integration works (MacOffice 2011):
- preview of presentation file is not generated
- when user double clicks on ppt item in openlp then a powerpoint window is opened
- the user can see the presentation in powerpoint and use directly powerpoint to control the presentation.
- powerpoint will show the presentation on second screen and thus override the openlp content on the 2nd screen
- when the user double clicks on another ppt file then another new powerpoint window is opened
- when openlp is closed then all powerpoind windows are automatically closed with openlp.

One small issue that when the user double clicks a ppt item in openlp then the error 'The presentation /filename is incomplete, please reload.'.
- this is probably caused by not generating presentation preview.

Otherwise it seems stable and working properly. I suggest merging this code.

review: Approve
Revision history for this message
Dmitriy Marmyshev (marmyshev) wrote : Posted in a previous version of this proposal

> What is it DnD?
>
>
DnD = Drag and Drop - actually it is from the trunk code - there is
procedure "activateDnD()" for list_view

Revision history for this message
Dmitriy Marmyshev (marmyshev) wrote : Posted in a previous version of this proposal

> One small issue that when the user double clicks a ppt item in openlp then
> the error 'The presentation /filename is incomplete, please reload.'.
> - this is probably caused by not generating presentation preview.
>
>
Can you explane more detailly about this? Did you reload it just once and
everything become good? or this issue reproduce EVERYTIME when you add new
presentation?

Revision history for this message
matysek (mzibricky) wrote : Posted in a previous version of this proposal

> > One small issue that when the user double clicks a ppt item in openlp then
> > the error 'The presentation /filename is incomplete, please reload.'.
> > - this is probably caused by not generating presentation preview.
> Can you explane more detailly about this? Did you reload it just once and
> everything become good? or this issue reproduce EVERYTIME when you add new
> presentation?

It is reproducible every time. How to reproduce:
- click on 'Load a new presentation'
- select a .ppt file and it will create a new item in openlp
- double click that item - the .ppt file get's loaded into powerpoint and openlp will show that message.

Revision history for this message
Raoul Snyman (raoul-snyman) wrote : Posted in a previous version of this proposal

> try this:
> 1. launch OpenLP
> 2. turn off Presentation plugin
> 3. Quit app
> 4. Launch OpenLP
> 5. turn on Presentation plugin
> 6. Add some presentations for tests, using "Select presentations" dialog.

Are our users going to have to do this? If so, you need to find a solution, we can't expect our users to have to run workarounds for a bug that we should be able to fix.

review: Needs Information
Revision history for this message
matysek (mzibricky) wrote : Posted in a previous version of this proposal

>
> Are our users going to have to do this? If so, you need to find a solution, we
> can't expect our users to have to run workarounds for a bug that we should be
> able to fix.

No, our users should not do that. It's just seems that this issue is not caused by Dmitriy's code but by instability of the trunk.

Revision history for this message
Dmitriy Marmyshev (marmyshev) wrote : Posted in a previous version of this proposal

Of course, no!

Who can help with this bag? Because I have no clue.

On Mon, Apr 8, 2013 at 4:10 PM, Raoul Snyman <email address hidden> wrote:

> Review: Needs Information
>
> > try this:
> > 1. launch OpenLP
> > 2. turn off Presentation plugin
> > 3. Quit app
> > 4. Launch OpenLP
> > 5. turn on Presentation plugin
> > 6. Add some presentations for tests, using "Select presentations" dialog.
>
> Are our users going to have to do this? If so, you need to find a
> solution, we can't expect our users to have to run workarounds for a bug
> that we should be able to fix.
> --
> https://code.launchpad.net/~marmyshev/openlp/presentation/+merge/157509
> You are the owner of lp:~marmyshev/openlp/presentation.
>

Revision history for this message
matysek (mzibricky) wrote : Posted in a previous version of this proposal

> Who can help with this bag? Because I have no clue.

Are you speaking about the disappearing dialog, the error message or both?

Regarding the disappearing dialog - after I applied your instructions it seems gone - not sure what happened. I'll have to double check.

Revision history for this message
Dmitriy Marmyshev (marmyshev) wrote : Posted in a previous version of this proposal

Guys, should i do here something else for merging it?

review: Needs Resubmitting
Revision history for this message
matysek (mzibricky) wrote : Posted in a previous version of this proposal

> Guys, should i do here something else for merging it?

From my point of view:
- Are you able to reproduce the issue 'The presentation /file/name is incomplete, please reload.'?
  - if yes - are you able to fix it?
  - if not - I will have to look at it what' wrong
- the disappearing dialog is not directly related to your code - it should be fixed but not in this pull request

Other than that I'm fine with merging it.

Revision history for this message
matysek (mzibricky) wrote : Posted in a previous version of this proposal

Related bug reports: #1168495 #1168493

Revision history for this message
Dmitriy Marmyshev (marmyshev) wrote : Posted in a previous version of this proposal

The issue 'The presentation /file/name is incomplete, please reload.' cant
be reproduce on my system because it works fine with creating icons for
presentation.
608-639 are responsible for this issue, I guess.

On Fri, Apr 12, 2013 at 9:50 PM, matysek <email address hidden> wrote:

> > Guys, should i do here something else for merging it?
>
> >From my point of view:
> - Are you able to reproduce the issue 'The presentation /file/name is
> incomplete, please reload.'?
> - if yes - are you able to fix it?
> - if not - I will have to look at it what' wrong
> - the disappearing dialog is not directly related to your code - it should
> be fixed but not in this pull request
>
> Other than that I'm fine with merging it.
> --
> https://code.launchpad.net/~marmyshev/openlp/presentation/+merge/157509
> You are the owner of lp:~marmyshev/openlp/presentation.
>

Revision history for this message
matysek (mzibricky) wrote : Posted in a previous version of this proposal

Ok, I will try to look at the create_thumbnails().
What documents do you use for testing?
What is your office version? 2011?

Revision history for this message
Dmitriy Marmyshev (marmyshev) wrote : Posted in a previous version of this proposal

I use *.key, *.ppt and *.pptx documents.
I use MS Office 2011 for Mac with localized interface.
I use both latin and non-latin characters in full file names.
I use both english and translated interface of OpenLP.

and create_thumbnails() works good.

On Fri, Apr 12, 2013 at 11:17 PM, matysek <email address hidden> wrote:

> Ok, I will try to look at the create_thumbnails().
> What documents do you use for testing?
> What is your office version? 2011?
> --
> https://code.launchpad.net/~marmyshev/openlp/presentation/+merge/157509
> You are the owner of lp:~marmyshev/openlp/presentation.
>

Revision history for this message
matysek (mzibricky) wrote : Posted in a previous version of this proposal

> I use *.key, *.ppt and *.pptx documents.
> I use MS Office 2011 for Mac with localized interface.
> I use both latin and non-latin characters in full file names.
> I use both english and translated interface of OpenLP.
>
> and create_thumbnails() works good.

The following function does not create PNG files.

  self.presentation.save(in_=thumbnail_folder, as_=appscript.k.save_as_PNG)

The following directory and its parent directories are just empty (lecture22.ppt is the name of the presentation file)

~/Library/Application\Support/openlp/Data/presentations/thumbnails/lecture22.ppt/lecture22.ppt

Revision history for this message
matysek (mzibricky) wrote : Posted in a previous version of this proposal

Dmitriy,

what appscript version do you use? 1.0.o or 1.0.1?

Mine is 1.0.0.

Should I try the 1.0.1?

Could this bugfix version make a difference with generating thumbnails?

Revision history for this message
Andreas Preikschat (googol-deactivatedaccount) wrote : Posted in a previous version of this proposal

Are you still working on this?

Revision history for this message
matysek (mzibricky) wrote : Posted in a previous version of this proposal

> Are you still working on this?

We are stuck on getting the thumbnails working.
- dmitriys - osx 10.8 - working
- matysek - osx 10.7 - not working

Recently I haven'd had much time for debugging this.

Revision history for this message
Raoul Snyman (raoul-snyman) wrote : Posted in a previous version of this proposal

Hey Dmitriy, a lot of our users would really benefit from this feature (lots of them have asked for it), are you going to try to figure out that last bug?

Revision history for this message
Andreas Preikschat (googol-deactivatedaccount) wrote : Posted in a previous version of this proposal

> Hey Dmitriy, a lot of our users would really benefit from this feature (lots
> of them have asked for it), are you going to try to figure out that last bug?

+1

Revision history for this message
Tim Bentley (trb143) wrote : Posted in a previous version of this proposal

Please resubmit due to the age of this request and additionally it will need converting to Python 3 as on 1/9/2013 truck will be converted.

review: Needs Resubmitting
Revision history for this message
Andreas Preikschat (googol-deactivatedaccount) wrote : Posted in a previous version of this proposal

In what part of the code does the thumbnail problem lays? Lines?

review: Needs Information
Revision history for this message
Dmitriy Marmyshev (marmyshev) wrote : Posted in a previous version of this proposal

Lines 589-628 - this is procedure to create thumbnail.

MS PPT can save all slides to PNG at ones. And used it before. But somehow this doesnt work stabale on different systems (I think global settings of showing file extentions, system language, MS Office lanuage are affecting) - thumbnail paths are different. This code i just commented.

So I was going to implement second option of PPT - to save each slide to PNG in cycle. But I still cant find solution so select current slide (or change selection) in PPT by applescript bridge (appscript module). Line 597.

Can you help with this?

Revision history for this message
Andreas Preikschat (googol-deactivatedaccount) wrote : Posted in a previous version of this proposal

So the problem with the current code is, that you cannot set the current slide?
And the problem with the old (commented) code is, that it does not always work?

Can you post (pastebin) the doc strings for self.presentation and the doc string of "slides".

Did you see that appscript is not developed and supported anymore? http://appscript.sourceforge.net/

Revision history for this message
Dmitriy Marmyshev (marmyshev) wrote : Posted in a previous version of this proposal

1. yes.
2. yes.
3. they are appscript objects. Can you specify what exactly do you need? I use "Microsoft PowerPoint dictionary" in Aplle script editor to find commands to use in appscript module.
4. Yes. But it works for now, and ScriptingBridge from Python works awfull. The other problem of this was discribed upper - that MS PPT for Apple doesnt have any programm interface except apple script. Appscript is just one of briges between python and apple scriping system.

Revision history for this message
Andreas Preikschat (googol-deactivatedaccount) wrote : Posted in a previous version of this proposal

> 3. they are appscript objects. Can you specify what exactly do you need? I use
> "Microsoft PowerPoint dictionary" in Aplle script editor to find commands to
> use in appscript module.
I need help(self.presentation) and help(self.presentation.slides())

Revision history for this message
Dmitriy Marmyshev (marmyshev) wrote :

I think now this should work well with thumbnails for PPT.

Revision history for this message
Dmitriy Marmyshev (marmyshev) wrote :

Can anybody test this on different OSX systems?

interesting to test this:
1. on "full English lang" systems - OS is English and MS PPT is English
2. and on "not English lang at all" - OS is some other lang and MS PPT is the same.

Revision history for this message
matysek (mzibricky) wrote :
Download full text (4.2 KiB)

I get the following stacktrace when trying to run openlp from this branch:

$ python openlp.py
WARNING: bool Phonon::FactoryPrivate::createBackend() phonon backend plugin could not be loaded
Traceback (most recent call last):
  File "/opt/openlp_2_2/Library/Frameworks/Python.framework/Versions/3.3/lib/python3.3/logging/__init__.py", line 937, in emit
    msg = self.format(record)
  File "/opt/openlp_2_2/Library/Frameworks/Python.framework/Versions/3.3/lib/python3.3/logging/__init__.py", line 808, in format
    return fmt.format(record)
  File "/opt/openlp_2_2/Library/Frameworks/Python.framework/Versions/3.3/lib/python3.3/logging/__init__.py", line 554, in format
    record.exc_text = self.formatException(record.exc_info)
  File "/opt/openlp_2_2/Library/Frameworks/Python.framework/Versions/3.3/lib/python3.3/logging/__init__.py", line 504, in formatException
    traceback.print_exception(ei[0], ei[1], tb, None, sio)
  File "/opt/openlp_2_2/Library/Frameworks/Python.framework/Versions/3.3/lib/python3.3/traceback.py", line 156, in print_exception
    for value, tb in values:
  File "/opt/openlp_2_2/Library/Frameworks/Python.framework/Versions/3.3/lib/python3.3/traceback.py", line 122, in _iter_chain
    context = exc.__context__
AttributeError: 'NoneType' object has no attribute '__context__'
Logged from file __init__.py, line 186
Error in sys.excepthook:
RuntimeError: super-class __init__() of type ExceptionForm was never called

Original exception was:
Traceback (most recent call last):
  File "openlp.py", line 45, in <module>
    main()
  File "/Users/martin/Projects/openlp/presentation/openlp/core/__init__.py", line 326, in main
    sys.exit(application.run(qt_args))
  File "/Users/martin/Projects/openlp/presentation/openlp/core/__init__.py", line 137, in run
    Registry().execute('bootstrap_initialise')
  File "/Users/martin/Projects/openlp/presentation/openlp/core/lib/registry.py", line 159, in execute
    result = function(*args, **kwargs)
  File "/Users/martin/Projects/openlp/presentation/openlp/core/lib/pluginmanager.py", line 86, in bootstrap_initialise
    self.initialise_plugins()
  File "/Users/martin/Projects/openlp/presentation/openlp/core/lib/pluginmanager.py", line 201, in initialise_plugins
    plugin.initialise()
  File "/Users/martin/Projects/openlp/presentation/openlp/plugins/presentations/presentationplugin.py", line 91, in initialise
    super(PresentationPlugin, self).initialise()
  File "/Users/martin/Projects/openlp/presentation/openlp/core/lib/plugin.py", line 296, in initialise
    self.media_item.initialise()
  File "/Users/martin/Projects/openlp/presentation/openlp/plugins/presentations/lib/mediaitem.py", line 127, in initialise
    self.populate_display_types()
  File "/Users/martin/Projects/openlp/presentation/openlp/plugins/presentations/lib/mediaitem.py", line 137, in populate_display_types
    if self.controllers[item].enabled():
  File "/Users/martin/Projects/openlp/presentation/openlp/plugins/presentations/lib/presentationcontroller.py", line 380, in enabled
    return self.is_available()
  File "/Users/martin/Projects/openlp/presentation/openlp/plugins/presentations/lib/presentationcontroller.py", line 386, ...

Read more...

review: Needs Information
Revision history for this message
Dmitriy Marmyshev (marmyshev) wrote :
Download full text (4.8 KiB)

Do you have appscript for Python 3.3? afrter I have installed Py-33 env - I
re-install appscript 1.0.1 for python33
And do you have MS PPT 2011 on mac?

Because self.check_available() works fine on my system.

On Fri, Sep 20, 2013 at 2:02 AM, matysek <email address hidden> wrote:

> Review: Needs Information
>
> I get the following stacktrace when trying to run openlp from this branch:
>
> $ python openlp.py
> WARNING: bool Phonon::FactoryPrivate::createBackend() phonon backend
> plugin could not be loaded
> Traceback (most recent call last):
> File
> "/opt/openlp_2_2/Library/Frameworks/Python.framework/Versions/3.3/lib/python3.3/logging/__init__.py",
> line 937, in emit
> msg = self.format(record)
> File
> "/opt/openlp_2_2/Library/Frameworks/Python.framework/Versions/3.3/lib/python3.3/logging/__init__.py",
> line 808, in format
> return fmt.format(record)
> File
> "/opt/openlp_2_2/Library/Frameworks/Python.framework/Versions/3.3/lib/python3.3/logging/__init__.py",
> line 554, in format
> record.exc_text = self.formatException(record.exc_info)
> File
> "/opt/openlp_2_2/Library/Frameworks/Python.framework/Versions/3.3/lib/python3.3/logging/__init__.py",
> line 504, in formatException
> traceback.print_exception(ei[0], ei[1], tb, None, sio)
> File
> "/opt/openlp_2_2/Library/Frameworks/Python.framework/Versions/3.3/lib/python3.3/traceback.py",
> line 156, in print_exception
> for value, tb in values:
> File
> "/opt/openlp_2_2/Library/Frameworks/Python.framework/Versions/3.3/lib/python3.3/traceback.py",
> line 122, in _iter_chain
> context = exc.__context__
> AttributeError: 'NoneType' object has no attribute '__context__'
> Logged from file __init__.py, line 186
> Error in sys.excepthook:
> RuntimeError: super-class __init__() of type ExceptionForm was never called
>
> Original exception was:
> Traceback (most recent call last):
> File "openlp.py", line 45, in <module>
> main()
> File
> "/Users/martin/Projects/openlp/presentation/openlp/core/__init__.py", line
> 326, in main
> sys.exit(application.run(qt_args))
> File
> "/Users/martin/Projects/openlp/presentation/openlp/core/__init__.py", line
> 137, in run
> Registry().execute('bootstrap_initialise')
> File
> "/Users/martin/Projects/openlp/presentation/openlp/core/lib/registry.py",
> line 159, in execute
> result = function(*args, **kwargs)
> File
> "/Users/martin/Projects/openlp/presentation/openlp/core/lib/pluginmanager.py",
> line 86, in bootstrap_initialise
> self.initialise_plugins()
> File
> "/Users/martin/Projects/openlp/presentation/openlp/core/lib/pluginmanager.py",
> line 201, in initialise_plugins
> plugin.initialise()
> File
> "/Users/martin/Projects/openlp/presentation/openlp/plugins/presentations/presentationplugin.py",
> line 91, in initialise
> super(PresentationPlugin, self).initialise()
> File
> "/Users/martin/Projects/openlp/presentation/openlp/core/lib/plugin.py",
> line 296, in initialise
> self.media_item.initialise()
> File
> "/Users/martin/Projects/openlp/presentation/openlp/plugins/presentations/lib/mediaitem.py",
> line 127, in initialise
> self.populate_display_types()
> File
>...

Read more...

Revision history for this message
matysek (mzibricky) wrote :

I was using appscript 1.0.0. It seems like this version does not work properly with python 3.3. I upgraded to 1.0.1 and openlp is now working. I'm sorry for that. So this exception is now gone.

A few notes:
- presentation previews are not generated when a new presentation is added to openlp
- when I try to show presentation preview in openlp it complains with message:

  "The presentation file.ppt is incomplete, please reload."

What code is responsible for creating the preview from the ppt file?

Revision history for this message
Dmitriy Marmyshev (marmyshev) wrote :

Creating preview calls from line: 597
and the code doing it is in lines: 601-638

Generating of preview should start on adding PPT to OpenLP (line: 597). But if you had file already added befor without generated preview you need to delete it and add it again.

Can you go step-by-step to the procedure "create_thumbnails" and tell me where it fails?

Revision history for this message
matysek (mzibricky) wrote :

It turns out that the following line does not create any .png files:

  self.presentation.save(in_=thumbnail_folder, as_=appscript.k.save_as_PNG)

Btw, I'm testing a .ppt file if that makes a difference. Did you test only pptx files?

Revision history for this message
matysek (mzibricky) wrote :

My Powerpoint version is:

Microsoft PowerPoint for Mac 2011
Version 14.0
Latest Installed Update: 14.0.0

Revision history for this message
matysek (mzibricky) wrote :

Is there any documentation or any other way what all functions/objects are provided by appscript to controll PowerPoint?

Revision history for this message
Dmitriy Marmyshev (marmyshev) wrote :

I have PowerPoint 14.1 (110310)
Latest installed update 14.1.0

I dont want to beleave that 14.1.0 and 14.0.0 is such big difference that "save" command doesnt work.

You can look in apple script commands here:
Open "AppleScript Editor" app
Menu: File - Open Dictionary... then select "Microsoft PowerPoint"
type save command and look into the syntax.

What i have in syntax:

save specifier: the object to save
   [in Macintosh path/Posix path]: the file in which to save the object
   [as save ast presentation/save as template/.../save as PNG/... and etc.]: the file type of the document in which to save the data

so, command on applescript, something like this:

tell presentation
   save in <path> as save as PNG
end tell

converts to call in appscript bridge as:

presentation.save(in_=thumbnail_folder, as_=appscript.k.save_as_PNG)

What do you have in syntax?

Revision history for this message
matysek (mzibricky) wrote :

On my system there is:

save v : Save an object
save specifier : the object to save
[in Macintosh path/Posix path] : the file in which to save the object
[as save as presentation/save as template/save as RTF/save as show/save as default/save as HTML/save as movie/save as package/save as PDF/save as Open XML presentation/save as Open XML presentation macro enabled/save as Open XML show/save as Open XML show macro enabled/save as Open XML template/save as Open XML template macro enabled/save as Open XML theme/save as GIF/save as JPG/save as PNG/save as BMP/save as TIF] : the file type of the document in which to save the data

It looks the same as yours.

Is it possible to update macoffice to 14.1 without additional costs?

Revision history for this message
matysek (mzibricky) wrote :

I upgraded macoffice to version 14.1 and now the presentation.save(in_=thumbnail_folder, as_=appscript.k.save_as_PNG) is working and thumbnails are generated properly.

We should mention in the manual that macoffice 14.0 is not working and upgrade to 14.1 is recommended.

I thus confirm that ppt integration on osx is working.

review: Approve
Revision history for this message
Phill (phill-ridout) wrote :

> We should mention in the manual that macoffice 14.0 is not working and upgrade
> to 14.1 is recommended.

I've added a bug report for this: https://bugs.launchpad.net/openlp/+bug/1244242
A comment like this could easily get forgotten about, so if you notice something like this in the future please raise a bug report and use "Target to Series" to target it to the documentation series.

Is 14.x the minimum version, or is this a regression and the plugin works with earlier versions as well?

lp:~marmyshev/openlp/presentation updated
2138. By Dmitriy Marmyshev

Tests

2139. By Dmitriy Marmyshev

Added check_ver for appscript module

2140. By Dmitriy Marmyshev

Trunk

2141. By Dmitriy Marmyshev

cleanups.

2142. By Dmitriy Marmyshev

Trunk

2143. By Dmitriy Marmyshev

tests fixes

Unmerged revisions

2143. By Dmitriy Marmyshev

tests fixes

2142. By Dmitriy Marmyshev

Trunk

2141. By Dmitriy Marmyshev

cleanups.

2140. By Dmitriy Marmyshev

Trunk

2139. By Dmitriy Marmyshev

Added check_ver for appscript module

2138. By Dmitriy Marmyshev

Tests

2137. By Dmitriy Marmyshev

thumbnail fixes

2136. By Dmitriy Marmyshev

Trunk

2135. By Dmitriy Marmyshev

fixes unicode & bzr eric5 ignore

2134. By Dmitriy Marmyshev

Trunk

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '.bzrignore'
2--- .bzrignore 2013-09-14 02:42:12 +0000
3+++ .bzrignore 2013-10-24 16:44:10 +0000
4@@ -2,6 +2,7 @@
5 *.*~
6 \#*\#
7 *.eric4project
8+*.eric5project
9 *.ropeproject
10 *.e4*
11 .eric4project
12
13=== modified file 'openlp/core/lib/pluginmanager.py'
14--- openlp/core/lib/pluginmanager.py 2013-10-13 13:51:13 +0000
15+++ openlp/core/lib/pluginmanager.py 2013-10-24 16:44:10 +0000
16@@ -94,11 +94,6 @@
17 present_plugin_dir = os.path.join(self.base_path, 'presentations')
18 log.debug('finding plugins in %s at depth %d', str(self.base_path), start_depth)
19 for root, dirs, files in os.walk(self.base_path):
20- if sys.platform == 'darwin' and root.startswith(present_plugin_dir):
21- # TODO Presentation plugin is not yet working on Mac OS X.
22- # For now just ignore it. The following code will ignore files from the presentation plugin directory
23- # and thereby never import the plugin.
24- continue
25 for name in files:
26 if name.endswith('.py') and not name.startswith('__'):
27 path = os.path.abspath(os.path.join(root, name))
28
29=== modified file 'openlp/core/ui/firsttimeform.py'
30--- openlp/core/ui/firsttimeform.py 2013-10-13 21:07:28 +0000
31+++ openlp/core/ui/firsttimeform.py 2013-10-24 16:44:10 +0000
32@@ -414,10 +414,7 @@
33 self._increment_progress_bar(translate('OpenLP.FirstTimeWizard', 'Enabling selected plugins...'))
34 self._set_plugin_status(self.songs_check_box, 'songs/status')
35 self._set_plugin_status(self.bible_check_box, 'bibles/status')
36- # TODO Presentation plugin is not yet working on Mac OS X.
37- # For now just ignore it.
38- if sys.platform != 'darwin':
39- self._set_plugin_status(self.presentation_check_box, 'presentations/status')
40+ self._set_plugin_status(self.presentation_check_box, 'presentations/status')
41 self._set_plugin_status(self.image_check_box, 'images/status')
42 self._set_plugin_status(self.media_check_box, 'media/status')
43 self._set_plugin_status(self.remote_check_box, 'remotes/status')
44
45=== modified file 'openlp/core/ui/firsttimewizard.py'
46--- openlp/core/ui/firsttimewizard.py 2013-10-13 21:07:28 +0000
47+++ openlp/core/ui/firsttimewizard.py 2013-10-24 16:44:10 +0000
48@@ -93,13 +93,10 @@
49 self.image_check_box.setChecked(True)
50 self.image_check_box.setObjectName('image_check_box')
51 self.plugin_layout.addWidget(self.image_check_box)
52- # TODO Presentation plugin is not yet working on Mac OS X.
53- # For now just ignore it.
54- if sys.platform != 'darwin':
55- self.presentation_check_box = QtGui.QCheckBox(self.plugin_page)
56- self.presentation_check_box.setChecked(True)
57- self.presentation_check_box.setObjectName('presentation_check_box')
58- self.plugin_layout.addWidget(self.presentation_check_box)
59+ self.presentation_check_box = QtGui.QCheckBox(self.plugin_page)
60+ self.presentation_check_box.setChecked(True)
61+ self.presentation_check_box.setObjectName('presentation_check_box')
62+ self.plugin_layout.addWidget(self.presentation_check_box)
63 self.media_check_box = QtGui.QCheckBox(self.plugin_page)
64 self.media_check_box.setChecked(True)
65 self.media_check_box.setObjectName('media_check_box')
66@@ -219,10 +216,7 @@
67 self.custom_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Custom Slides'))
68 self.bible_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Bible'))
69 self.image_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Images'))
70- # TODO Presentation plugin is not yet working on Mac OS X.
71- # For now just ignore it.
72- if sys.platform != 'darwin':
73- self.presentation_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Presentations'))
74+ self.presentation_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Presentations'))
75 self.media_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Media (Audio and Video)'))
76 self.remote_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Allow remote access'))
77 self.song_usage_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Monitor Song Usage'))
78
79=== added file 'openlp/plugins/presentations/lib/keynotemaccontroller.py'
80--- openlp/plugins/presentations/lib/keynotemaccontroller.py 1970-01-01 00:00:00 +0000
81+++ openlp/plugins/presentations/lib/keynotemaccontroller.py 2013-10-24 16:44:10 +0000
82@@ -0,0 +1,362 @@
83+# -*- coding: utf-8 -*-
84+# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
85+
86+###############################################################################
87+# OpenLP - Open Source Lyrics Projection #
88+# --------------------------------------------------------------------------- #
89+# Copyright (c) 2008-2012 Raoul Snyman #
90+# Portions copyright (c) 2008-2012 Tim Bentley, Gerald Britton, Jonathan #
91+# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
92+# Meinert Jordan, Armin Köhler, Eric Ludin, Edwin Lunando, Brian T. Meyer, #
93+# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
94+# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
95+# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, #
96+# Frode Woldsund, Martin Zibricky #
97+# --------------------------------------------------------------------------- #
98+# This program is free software; you can redistribute it and/or modify it #
99+# under the terms of the GNU General Public License as published by the Free #
100+# Software Foundation; version 2 of the License. #
101+# #
102+# This program is distributed in the hope that it will be useful, but WITHOUT #
103+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
104+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
105+# more details. #
106+# #
107+# You should have received a copy of the GNU General Public License along #
108+# with this program; if not, write to the Free Software Foundation, Inc., 59 #
109+# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
110+###############################################################################
111+
112+import os
113+#import sys
114+import logging
115+#import mactypes
116+
117+import appscript
118+
119+from openlp.core.lib import Settings, ScreenList
120+from .presentationcontroller import PresentationController, PresentationDocument
121+
122+
123+log = logging.getLogger(__name__)
124+
125+
126+class KeynoteController(PresentationController):
127+ """
128+ Class to control interactions with KeyNote Presentations
129+ It creates the runtime Environment , Loads the and Closes the Presentation
130+ As well as triggering the correct activities based on the users input
131+ """
132+ log.info('KeynoteController loaded')
133+
134+ def __init__(self, plugin):
135+ """
136+ Initialise the class
137+ """
138+ log.debug('Initialising')
139+ super(KeynoteController, self).__init__(plugin, 'Keynote', KeynoteDocument)
140+ self.supports = ['key']
141+ self.process = None
142+
143+ def check_available(self):
144+ """
145+ KeyNote is able to run on this machine
146+ """
147+ log.debug('check_available')
148+ try:
149+ self.process = appscript.app('Keynote')
150+ except appscript.ApplicationNotFoundError:
151+ return False
152+ self.kill()
153+ return True
154+
155+ def start_process(self):
156+ """
157+ Loads KeyNote process
158+ """
159+ log.debug('start_process')
160+ if not self.process or not self.process.isrunning():
161+ self.process = appscript.app('Keynote')
162+ self.process.relaunchmode = 'limited' #'always'
163+ self.process.launch()
164+ self.apply_app_settings()
165+
166+ def kill(self):
167+ """
168+ Called at system exit to clean up any running presentations
169+ """
170+ log.debug('Kill Keynote')
171+ while self.docs and self.process.isrunning():
172+ self.docs[0].close_presentation()
173+ if self.process is None or not self.process.isrunning():
174+ return
175+ try:
176+ total = self.process.slideshows()
177+ if self.process and total != appscript.k.missing_value and len(total) > 0:
178+ return
179+ self.process.quit(saving = appscript.k.ask)
180+ except appscript.CantLaunchApplicationError:
181+ log.debug('Kill Keynote failed')
182+ self.process = None
183+
184+ def apply_app_settings(self):
185+ """
186+ Apply settings for Keynote
187+ PresentationModeEnableFeedbackDisplay = True if in settings of OpenLP show presenter view = True
188+ PresentationModeUseSecondary = 1 if OpenLP monitor for output = Screen 2
189+ """
190+ openlp_settings = Settings('openlp.org','OpenLP')
191+ keynote_settings = Settings('apple', 'iWork.Keynote')
192+ use_secondary = int(keynote_settings.value('PresentationModeUseSecondary'))
193+ monitor = openlp_settings.value( 'core/monitor')
194+ override_position = openlp_settings.value( 'core/override position')
195+ if not override_position and use_secondary != monitor:
196+ keynote_settings.setValue('PresentationModeUseSecondary', monitor)
197+ elif override_position and use_secondary != 0:
198+ keynote_settings.setValue('PresentationModeUseSecondary', '0')
199+ show_presenter_view = openlp_settings.value(self.plugin.settings_section + '/show presenter view')
200+ keynote_feedback_display = keynote_settings.value('PresentationModeEnableFeedbackDisplay')
201+ if show_presenter_view != keynote_feedback_display:
202+ keynote_settings.setValue('PresentationModeEnableFeedbackDisplay', show_presenter_view)
203+ play_well_with_others = keynote_settings.value('PresentationModePlayWellWithOthers')
204+ if not play_well_with_others:
205+ keynote_settings.setValue('PresentationModePlayWellWithOthers', True)
206+
207+
208+class KeynoteDocument(PresentationDocument):
209+ """
210+ Class which holds information and controls a single presentation
211+ """
212+
213+ def __init__(self, controller, presentation):
214+ """
215+ Constructor, store information about the file and initialise
216+ """
217+ log.debug('Init Presentation Keynote')
218+ super(KeynoteDocument, self).__init__(controller, presentation)
219+ self.presentation = None
220+
221+ def load_presentation(self):
222+ """
223+ Called when a presentation is added to the SlideController.
224+ Opens the Keynote file using the process created earlier.
225+ """
226+ log.debug('load_presentation')
227+ if not self.controller.process or not self.controller.process.isrunning():
228+ self.controller.start_process()
229+ try:
230+ self.controller.process.open(self.filepath)
231+ except appscript.CommandError:
232+ log.debug('Keynote open failed')
233+ return False
234+ slideshows = self.controller.process.slideshows()
235+ for slideshow in slideshows:
236+ path = slideshow.path()
237+ if self.filepath == path:
238+ self.presentation = slideshow
239+ self.create_thumbnails()
240+ return True
241+ self.presentation = None
242+ return False
243+
244+ def create_thumbnails(self):
245+ """
246+ Create the thumbnail images for the current presentation.
247+ """
248+ log.debug('create_thumbnails')
249+ if self.check_thumbnails():
250+ return
251+ thumbnail_folder = self.get_thumbnail_folder()
252+ if not os.path.exists(thumbnail_folder):
253+ os.makedirs(thumbnail_folder)
254+ try:
255+ self.controller.process.open(self.filepath)
256+ except appscript.CommandError:
257+ log.debug('KeyNote open failed')
258+ return
259+ temp_dir = self.controller.thumbnail_folder
260+ keystroke = os.path.join(temp_dir, 'temp' + self.controller.thumbnail_prefix)
261+ self.controller.process.activate()
262+ appscript.app('System Events').processes['Keynote'].menu_bars[1].menu_bar_items[3].menus.menu_items[11].click()
263+ appscript.app('System Events').processes['Keynote'].windows[1].sheets[1].tool_bars.buttons[4].click()
264+ appscript.app('System Events').processes['Keynote'].windows[1].sheets[1].radio_groups[1].radio_buttons[1].click()
265+ appscript.app('System Events').processes['Keynote'].windows[1].sheets[1].pop_up_buttons[1].click()
266+ appscript.app('System Events').processes['Keynote'].windows[1].sheets[1].pop_up_buttons[1]\
267+ .menus.menu_items[2].click()
268+ appscript.app('System Events').processes['Keynote'].windows[1].sheets[1].buttons[2].click()
269+ appscript.app('System Events').processes['Keynote'].keystroke(keystroke)
270+ appscript.app('System Events').processes['Keynote'].key_code(36)
271+ appscript.app('System Events').processes['Keynote'].windows[1].sheets[1].buttons[1].click()
272+ self.controller.plugin._main_window.activateWindow()
273+ slide_no = 0
274+ for filename in os.listdir(temp_dir):
275+ full_filename = os.path.join(temp_dir, filename)
276+ if not os.path.isfile(full_filename) or not filename.endswith('.png') or filename == 'icon.png':
277+ continue
278+ slide_no = slide_no + 1
279+ if not filename.startswith(self.controller.thumbnail_prefix):
280+ path = os.path.join(thumbnail_folder,
281+ self.controller.thumbnail_prefix + str(slide_no) + '.png')
282+ try:
283+ os.rename(full_filename, path)
284+ except:
285+ open(path,'w').write(open(full_filename,'r').read())
286+ os.unlink(full_filename)
287+
288+ def close_presentation(self):
289+ """
290+ Close presentation and clean up objects. This is triggered by a new
291+ object being added to SlideController or OpenLP being shut down.
292+ """
293+ log.debug('close_presentation')
294+ if self.presentation:
295+ try:
296+ self.presentation.close()
297+ except appscript.CommandError:
298+ log.debug('Could not close the presentation')
299+ self.presentation = None
300+ self.controller.remove_doc(self)
301+
302+ def is_loaded(self):
303+ """
304+ Returns ``True`` if a presentation is loaded.
305+ """
306+ log.debug('is_loaded')
307+ try:
308+ if not self.controller.process.isrunning():
309+ return False
310+ windows = self.controller.process.windows()
311+ if len(windows) == 0:
312+ return False
313+ slideshows = self.controller.process.slideshows()
314+ if len(slideshows) == 0:
315+ return False
316+ except:
317+ return False
318+ for slideshow in slideshows:
319+ path = slideshow.path()
320+ if self.filepath == path:
321+ return True
322+ return False
323+
324+ def is_active(self):
325+ """
326+ Returns ``True`` if a presentation is currently active.
327+ """
328+ log.debug('is_active')
329+ if not self.is_loaded():
330+ return False
331+ try:
332+ if not self.presentation.playing():
333+ return False
334+ except appscript.CommandError:
335+ return False
336+ return True
337+
338+ def unblank_screen(self):
339+ """
340+ Unblanks (restores) the presentation.
341+ """
342+ log.debug('unblank_screen')
343+ if self.is_blank():
344+ self.presentation.start_presentation()
345+
346+ def blank_screen(self):
347+ """
348+ Blanks the screen.
349+ """
350+ log.debug('blank_screen')
351+ self.presentation.stop_slideshow()
352+
353+ def is_blank(self):
354+ """
355+ Returns ``True`` if screen is blank.
356+ """
357+ log.debug('is_blank')
358+ if self.is_active():
359+ return not self.presentation.playing()
360+ else:
361+ return False
362+
363+ def stop_presentation(self):
364+ """
365+ Stops the current presentation and hides the output.
366+ """
367+ log.debug('stop_presentation')
368+ self.presentation.stop_slideshow()
369+
370+
371+ def start_presentation(self):
372+ """
373+ Starts a presentation from the beginning.
374+ """
375+ log.debug('start_presentation')
376+ if not self.is_active():
377+ self.controller.apply_app_settings()
378+ try:
379+ self.presentation.start()
380+ except appscript.CommandError:
381+ return
382+ rect = ScreenList().current['size']
383+ top = rect.y()
384+ height = rect.height()
385+ left_position = rect.x()
386+ width = rect.width()
387+ self.controller.process.windows[1].bounds.set([left_position, top, left_position + width, top + height])
388+
389+ def get_slide_number(self):
390+ """
391+ Returns the current slide number.
392+ """
393+ log.debug('get_slide_number')
394+ return self.presentation.current_slide().slide_number()
395+
396+ def get_slide_count(self):
397+ """
398+ Returns total number of slides.
399+ """
400+ log.debug('get_slide_count')
401+ slides = self.presentation.slides()
402+ return len(slides)
403+
404+ def goto_slide(self, slideno):
405+ """
406+ Moves to a specific slide in the presentation.
407+ """
408+ log.debug('goto_slide')
409+ slide = self.presentation.slides()[slideno-1]
410+ self.presentation.show(slide)
411+
412+ def next_step(self):
413+ """
414+ Triggers the next effect of slide on the running presentation.
415+ """
416+ log.debug('next_step')
417+ self.presentation.show_next()
418+ if self.get_slide_number() > self.get_slide_count():
419+ self.previous_step()
420+
421+ def previous_step(self):
422+ """
423+ Triggers the previous slide on the running presentation.
424+ """
425+ log.debug('previous_step')
426+ self.presentation.show_previous()
427+
428+ def get_slide_text(self, slide_no):
429+ """
430+ Returns the text on the slide.
431+
432+ ``slide_no``
433+ The slide the text is required for, starting at 1.
434+ """
435+ return self.presentation.slides[slide_no].body
436+
437+ def get_slide_notes(self, slide_no):
438+ """
439+ Returns the text on the slide.
440+
441+ ``slide_no``
442+ The slide the notes are required for, starting at 1.
443+ """
444+ return self.presentation.slides[slide_no].notes
445
446=== added file 'openlp/plugins/presentations/lib/powerpointmaccontroller.py'
447--- openlp/plugins/presentations/lib/powerpointmaccontroller.py 1970-01-01 00:00:00 +0000
448+++ openlp/plugins/presentations/lib/powerpointmaccontroller.py 2013-10-24 16:44:10 +0000
449@@ -0,0 +1,399 @@
450+# -*- coding: utf-8 -*-
451+# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
452+
453+###############################################################################
454+# OpenLP - Open Source Lyrics Projection #
455+# --------------------------------------------------------------------------- #
456+# Copyright (c) 2008-2012 Raoul Snyman #
457+# Portions copyright (c) 2008-2012 Tim Bentley, Gerald Britton, Jonathan #
458+# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
459+# Meinert Jordan, Armin Köhler, Eric Ludin, Edwin Lunando, Brian T. Meyer, #
460+# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
461+# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
462+# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, #
463+# Frode Woldsund, Martin Zibricky #
464+# --------------------------------------------------------------------------- #
465+# This program is free software; you can redistribute it and/or modify it #
466+# under the terms of the GNU General Public License as published by the Free #
467+# Software Foundation; version 2 of the License. #
468+# #
469+# This program is distributed in the hope that it will be useful, but WITHOUT #
470+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
471+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
472+# more details. #
473+# #
474+# You should have received a copy of the GNU General Public License along #
475+# with this program; if not, write to the Free Software Foundation, Inc., 59 #
476+# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
477+###############################################################################
478+
479+import os
480+import sys
481+import logging
482+import shutil
483+#import mactypes
484+
485+import appscript
486+
487+from openlp.core.lib import Settings, ScreenList
488+from .presentationcontroller import PresentationController, PresentationDocument
489+
490+
491+log = logging.getLogger(__name__)
492+
493+
494+class PowerpointController(PresentationController):
495+ """
496+ Class to control interactions with PowerPoint Presentations
497+ It creates the runtime Environment , Loads the and Closes the Presentation
498+ As well as triggering the correct activities based on the users input
499+ """
500+ log.info(u'PowerpointController loaded')
501+
502+ def __init__(self, plugin):
503+ """
504+ Initialise the class
505+ """
506+ log.debug('Initialising')
507+ super(PowerpointController, self).__init__(plugin, 'Powerpoint', PowerpointDocument)
508+ self.supports = ['ppt', 'pps', 'pptx', 'ppsx']
509+ self.process = None
510+
511+ def check_available(self):
512+ """
513+ PowerPoint is able to run on this machine
514+ """
515+ log.debug('check_available')
516+ try:
517+ self.process = appscript.app(id='com.microsoft.powerpoint')
518+ except appscript.ApplicationNotFoundError:
519+ return False
520+ self.kill()
521+ return True
522+
523+ def start_process(self):
524+ """
525+ Loads PowerPoint process
526+ """
527+ log.debug('start_process')
528+ if not self.process or not self.process.isrunning():
529+ self.process = appscript.app(id='com.microsoft.powerpoint')
530+ self.process.relaunchmode = 'limited' #'always'
531+ self.process.launch()
532+ self.apply_app_settings()
533+
534+ def kill(self):
535+ """
536+ Called at system exit to clean up any running presentations
537+ """
538+ log.debug('Kill Powerpoint')
539+ while self.docs and self.process.isrunning():
540+ self.docs[0].close_presentation()
541+ if self.process is None or not self.process.isrunning():
542+ return
543+ try:
544+ total = self.process.presentations()
545+ if self.process and total != appscript.k.missing_value and len(total) > 0:
546+ return
547+ self.process.quit(saving = appscript.k.ask)
548+ except appscript.CantLaunchApplicationError:
549+ log.debug('Kill Keynote failed')
550+ self.process = None
551+
552+ def apply_app_settings(self):
553+ """
554+ Apply settings for PowerPoint
555+ 14\Options\Options\Save graphics screen heigth = 240
556+ 14\Options\Options\Save graphics screen width = 320
557+ 14\Options\Options\Save only current slide graphics = 0
558+
559+ """
560+ #TODO: add settings for different versions of MS PPT
561+ pass
562+
563+
564+class PowerpointDocument(PresentationDocument):
565+ """
566+ Class which holds information and controls a single presentation
567+ """
568+
569+ def __init__(self, controller, presentation):
570+ """
571+ Constructor, store information about the file and initialise
572+ """
573+ log.debug('Init Presentation Powerpoint')
574+ super(PowerpointDocument, self).__init__(controller, presentation)
575+ self.presentation = None
576+
577+ def load_presentation(self):
578+ """
579+ Called when a presentation is added to the SlideController.
580+ Opens the PowerPoint file using the process created earlier.
581+ """
582+ log.debug('load_presentation')
583+ if not self.controller.process or not self.controller.process.isrunning():
584+ self.controller.start_process()
585+ try:
586+ self.controller.process.open(self.filepath)
587+ except appscript.CommandError:
588+ log.debug('PPT open failed')
589+ return False
590+ presentations = self.controller.process.presentations()
591+ for presentation in presentations:
592+ full_name = presentation.full_name()
593+ full_name = full_name.replace('Macintosh HD', '')
594+ full_name = full_name.replace(':', '/')
595+ if self.filepath == full_name:
596+ self.presentation = presentation
597+ self.create_thumbnails()
598+ return True
599+ return False
600+
601+ def create_thumbnails(self):
602+ """
603+ Create the thumbnail images for the current presentation.
604+ """
605+ log.debug('create_thumbnails')
606+ if self.check_thumbnails():
607+ return
608+ thumbnail_folder = self.get_thumbnail_folder()
609+ self.presentation.save(in_=thumbnail_folder, as_=appscript.k.save_as_PNG)
610+ slide_no = 0
611+ self.move_thumbnails(thumbnail_folder, slide_no, thumbnail_folder)
612+
613+ def move_thumbnails(self, thumbnail_folder, slide_no, temp_dir):
614+ """
615+ Move the thumbnail images from subdirectories to right path.
616+ """
617+ if not os.path.isdir(temp_dir):
618+ return
619+ for filename in os.listdir(temp_dir):
620+ full_filename = os.path.join(temp_dir, filename)
621+ if os.path.isdir(full_filename):
622+ self.move_thumbnails(thumbnail_folder, slide_no, full_filename)
623+ elif os.path.isfile(full_filename) and filename.endswith('.png') and not filename == 'icon.png':
624+ slide_no = slide_no + 1
625+ new_name = os.path.join(thumbnail_folder, self.controller.thumbnail_prefix + str(slide_no) + '.png')
626+ if full_filename == new_name \
627+ or full_filename.startswith(os.path.join(thumbnail_folder, self.controller.thumbnail_prefix)):
628+ continue
629+ try:
630+ os.rename(full_filename, new_name)
631+ except:
632+ open(new_name,'w').write(open(full_filename,'r').read())
633+ os.unlink(full_filename)
634+ if temp_dir != thumbnail_folder:
635+ try:
636+ shutil.rmtree(temp_dir)
637+ except:
638+ pass
639+
640+ def close_presentation(self):
641+ """
642+ Close presentation and clean up objects. This is triggered by a new
643+ object being added to SlideController or OpenLP being shut down.
644+ """
645+ log.debug('close_presentation')
646+ if self.presentation:
647+ try:
648+ self.presentation.close()
649+ except appscript.CommandError:
650+ log.debug('Could not close the presentation')
651+ self.presentation = None
652+ self.controller.remove_doc(self)
653+
654+ def is_loaded(self):
655+ """
656+ Returns ``True`` if a presentation is loaded.
657+ """
658+ log.debug('is_loaded')
659+ try:
660+ if not self.controller.process.isrunning():
661+ return False
662+ if len(self.controller.process.document_windows()) == 0:
663+ return False
664+ presentations = self.controller.process.presentations()
665+ if len(presentations) == 0:
666+ return False
667+ except:
668+ return False
669+ for presentation in presentations:
670+ full_name = presentation.full_name()
671+ full_name = full_name.replace('Macintosh HD', '')
672+ full_name = full_name.replace(':', '/')
673+ if self.filepath == full_name:
674+ return True
675+ return False
676+
677+ def is_active(self):
678+ """
679+ Returns ``True`` if a presentation is currently active.
680+ """
681+ log.debug('is_active')
682+ if not self.is_loaded():
683+ return False
684+ try:
685+ slide_show_window = self.presentation.slide_show_window
686+ if slide_show_window is None:
687+ return False
688+ slide_state = self.presentation.slide_show_window.slideshow_view.slide_state()
689+ if slide_state is None or slide_state == appscript.k.missing_value:
690+ return False
691+ except:
692+ return False
693+ return True
694+
695+ def unblank_screen(self):
696+ """
697+ Unblanks (restores) the presentation.
698+ """
699+ log.debug('unblank_screen')
700+ if self.is_blank():
701+ settings = self.presentation.slide_show_settings
702+ self.presentation.slide_show_window.slideshow_view.slide_state.set(appscript.k.slide_show_state_running)
703+ self.presentation.slide_show_window.slideshow_view.go_to_next_slide()
704+ self.presentation.slide_show_window.slideshow_view.go_to_previous_slide()
705+
706+ def blank_screen(self):
707+ """
708+ Blanks the screen.
709+ """
710+ log.debug('blank_screen')
711+ self.presentation.slide_show_window.slideshow_view.slide_state.set(appscript.k.slide_show_state_black_screen)
712+
713+ def is_blank(self):
714+ """
715+ Returns ``True`` if screen is blank.
716+ """
717+ log.debug('is_blank')
718+ if self.is_active() and self.presentation.slide_show_window.slideshow_view\
719+ .slide_state() is appscript.k.slide_show_state_black_screen:
720+ return True
721+ else:
722+ return False
723+
724+ def stop_presentation(self):
725+ """
726+ Stops the current presentation and hides the output.
727+ """
728+ log.debug('stop_presentation')
729+ self.presentation.slide_show_window.slideshow_view.exit_slide_show()
730+
731+
732+ def start_presentation(self):
733+ """
734+ Starts a presentation from the beginning.
735+ """
736+ log.debug('start_presentation')
737+ rect = ScreenList().current['size']
738+ ppt_settings = self.presentation.slide_show_settings
739+ openlp_settings = Settings("openlp.org","OpenLP")
740+ show_presenter_view = openlp_settings.value(self.controller.plugin.settings_section + '/show presenter view')
741+ override_position = openlp_settings.value( 'general/override position')
742+ if show_presenter_view:
743+ ppt_settings.show_type.set(appscript.k.slide_show_type_presenter)
744+ elif override_position:
745+ ppt_settings.show_type.set(appscript.k.slide_show_type_window)
746+ else:
747+ ppt_settings.show_type.set(appscript.k.slide_show_type_kiosk)
748+ #ppt_settings.show_type.set(appscript.k.slide_show_type_speaker)
749+ ppt_window = ppt_settings.run_slide_show()
750+ #TODO: set slideshow state = running
751+# if ppt_window.slideshow_view.slide_state() != appscript.k.slide_show_state_running:
752+# ppt_window.slideshow_view.slide_state.set(appscript.k.slide_show_state_running)
753+ if not ppt_window or show_presenter_view:
754+ return
755+ if override_position:
756+ top = float(rect.y())
757+ height = float(rect.height())
758+ left_position = float(rect.x())
759+ width = float(rect.width())
760+ ppt_window.top.set(top)
761+ ppt_window.height.set(height)
762+ ppt_window.left_position.set(left_position)
763+ ppt_window.width.set(width)
764+ else:
765+ #TODO: set output monitor for fullscreen mode
766+ pass
767+
768+ def get_slide_number(self):
769+ """
770+ Returns the current slide number.
771+ """
772+ log.debug('get_slide_number')
773+ return self.presentation.slide_show_window.slideshow_view.current_show_position()
774+
775+ def get_slide_count(self):
776+ """
777+ Returns total number of slides.
778+ """
779+ log.debug('get_slide_count')
780+ return len(self.presentation.slides())
781+
782+ def goto_slide(self, slideno):
783+ """
784+ Moves to a specific slide in the presentation.
785+ """
786+ log.debug('goto_slide')
787+ #FIXME: this works. but needs to fix this dumb code
788+ while self.get_slide_number() != slideno:
789+ if self.get_slide_number() < slideno:
790+ self.presentation.slide_show_window.slideshow_view.go_to_next_slide()
791+ else:
792+ self.presentation.slide_show_window.slideshow_view.go_to_previous_slide()
793+ self.controller.process.activate()
794+
795+ def next_step(self):
796+ """
797+ Triggers the next effect of slide on the running presentation.
798+ """
799+ log.debug('next_step')
800+ #TODO: check if slideshow stoped then restart it from current position
801+ if self.is_active():
802+ self.presentation.slide_show_window.slideshow_view.go_to_next_slide()
803+ self.controller.process.activate()
804+ if self.get_slide_number() > self.get_slide_count():
805+ self.previous_step()
806+
807+ def previous_step(self):
808+ """
809+ Triggers the previous slide on the running presentation.
810+ """
811+ log.debug('previous_step')
812+ #TODO: check if slideshow stoped then restart it from current position
813+ if self.is_active():
814+ self.presentation.slide_show_window.slideshow_view.go_to_previous_slide()
815+ self.controller.process.activate()
816+
817+ def get_slide_text(self, slide_no):
818+ """
819+ Returns the text on the slide.
820+
821+ ``slide_no``
822+ The slide the text is required for, starting at 1.
823+ """
824+ return _get_text_from_shapes(self.presentation.slides[slide_no-1].shapes)
825+
826+ def get_slide_notes(self, slide_no):
827+ """
828+ Returns the text on the slide.
829+
830+ ``slide_no``
831+ The slide the notes are required for, starting at 1.
832+ """
833+ return _get_text_from_shapes(
834+ self.presentation.slides[slide_no].notes_page.shapes())
835+
836+def _get_text_from_shapes(shapes):
837+ """
838+ Returns any text extracted from the shapes on a presentation slide.
839+
840+ ``shapes``
841+ A set of shapes to search for text.
842+ """
843+ text = ''
844+ for idx in range(len(shapes)):
845+ shape = shapes[idx + 1]
846+ if shape.has_text_frame():
847+ text += shape.text_frame.text_range.content() + '\n'
848+ return text
849
850=== modified file 'openlp/plugins/presentations/lib/presentationtab.py'
851--- openlp/plugins/presentations/lib/presentationtab.py 2013-10-13 20:36:42 +0000
852+++ openlp/plugins/presentations/lib/presentationtab.py 2013-10-24 16:44:10 +0000
853@@ -71,6 +71,11 @@
854 self.override_app_check_box = QtGui.QCheckBox(self.advanced_group_box)
855 self.override_app_check_box.setObjectName('override_app_check_box')
856 self.advanced_layout.addWidget(self.override_app_check_box)
857+ self.show_presenter_view_check_box = QtGui.QCheckBox(self.advanced_group_box)
858+ self.show_presenter_view_check_box.setObjectName(u'show_presenter_view_check_box')
859+ self.advanced_layout.addWidget(self.show_presenter_view_check_box)
860+ if not sys.platform.startswith('darwin'):
861+ self.show_presenter_view_check_box.setVisible(False)
862 self.left_layout.addWidget(self.advanced_group_box)
863 self.left_layout.addStretch()
864 self.right_layout.addStretch()
865@@ -87,6 +92,8 @@
866 self.advanced_group_box.setTitle(UiStrings().Advanced)
867 self.override_app_check_box.setText(
868 translate('PresentationPlugin.PresentationTab', 'Allow presentation application to be overridden'))
869+ self.show_presenter_view_check_box.setText(translate('PresentationPlugin.PresentationTab',
870+ 'Show Presenter View'))
871
872 def set_controller_text(self, checkbox, controller):
873 if checkbox.isEnabled():
874@@ -103,6 +110,7 @@
875 checkbox = self.presenter_check_boxes[controller.name]
876 checkbox.setChecked(Settings().value(self.settings_section + '/' + controller.name))
877 self.override_app_check_box.setChecked(Settings().value(self.settings_section + '/override app'))
878+ self.show_presenter_view_check_box.setChecked(Settings().value(self.settings_section + '/show presenter view'))
879
880 def save(self):
881 """
882@@ -128,6 +136,10 @@
883 if Settings().value(setting_key) != self.override_app_check_box.checkState():
884 Settings().setValue(setting_key, self.override_app_check_box.checkState())
885 changed = True
886+ setting_key = self.settings_section + '/show presenter view'
887+ if Settings().value(setting_key) != self.show_presenter_view_check_box.checkState():
888+ Settings().setValue(setting_key, self.show_presenter_view_check_box.isChecked())
889+ changed = True
890 if changed:
891 self.settings_form.register_post_process('mediaitem_suffix_reset')
892 self.settings_form.register_post_process('mediaitem_presentation_rebuild')
893
894=== modified file 'openlp/plugins/presentations/presentationplugin.py'
895--- openlp/plugins/presentations/presentationplugin.py 2013-10-13 21:07:28 +0000
896+++ openlp/plugins/presentations/presentationplugin.py 2013-10-24 16:44:10 +0000
897@@ -31,6 +31,7 @@
898 formats.
899 """
900 import os
901+import sys
902 import logging
903
904 from PyQt4 import QtCore
905@@ -48,6 +49,11 @@
906 'presentations/Impress': QtCore.Qt.Checked,
907 'presentations/Powerpoint': QtCore.Qt.Checked,
908 'presentations/Powerpoint Viewer': QtCore.Qt.Checked,
909+ 'presentations/Keynote': QtCore.Qt.Checked,
910+ 'presentations/show presenter view': QtCore.Qt.Checked,
911+ 'PresentationModeUseSecondary': '',
912+ 'PresentationModeEnableFeedbackDisplay': False,
913+ 'PresentationModePlayWellWithOthers': False,
914 'presentations/presentations files': []
915 }
916
917@@ -125,6 +131,12 @@
918 controller_dir = os.path.join(AppLocation.get_directory(AppLocation.PluginsDir), 'presentations', 'lib')
919 for filename in os.listdir(controller_dir):
920 if filename.endswith('controller.py') and not filename == 'presentationcontroller.py':
921+ if not sys.platform.startswith('darwin') and filename == 'powerpointmaccontroller.py':
922+ continue
923+ if not sys.platform.startswith('darwin') and filename == 'keynotemaccontroller.py':
924+ continue
925+ if not sys.platform.startswith('win') and filename == 'powerpointcontroller.py':
926+ continue
927 path = os.path.join(controller_dir, filename)
928 if os.path.isfile(path):
929 module_name = 'openlp.plugins.presentations.lib.' + os.path.splitext(filename)[0]
930
931=== modified file 'scripts/check_dependencies.py'
932--- scripts/check_dependencies.py 2013-10-03 19:56:12 +0000
933+++ scripts/check_dependencies.py 2013-10-24 16:44:10 +0000
934@@ -47,6 +47,7 @@
935 nose = None
936
937 IS_WIN = sys.platform.startswith('win')
938+IS_OSX = sys.platform.startswith('darwin')
939
940
941 VERS = {
942@@ -57,6 +58,8 @@
943 # pyenchant 1.6 required on Windows
944 'enchant': '1.6' if IS_WIN else '1.3'
945 }
946+if IS_OSX:
947+ VERS.update({'appscript': '1.0.0'})
948
949 # pywin32
950 WIN32_MODULES = [
951@@ -67,6 +70,11 @@
952 'icu',
953 ]
954
955+# OSX
956+OSX_MODULES = [
957+ 'appscript',
958+ 'mactypes',
959+]
960 MODULES = [
961 'PyQt4',
962 'PyQt4.QtCore',
963@@ -177,6 +185,12 @@
964 check_vers(enchant.__version__, VERS['enchant'], 'enchant')
965 except ImportError:
966 print_vers_fail(VERS['enchant'], 'enchant')
967+ if IS_OSX:
968+ try:
969+ import appscript
970+ check_vers(appscript.__version__, VERS['appscript'], 'appscript')
971+ except ImportError:
972+ print_vers_fail(VERS['appscript'], 'appscript')
973
974
975 def print_enchant_backends_and_languages():
976@@ -228,6 +242,10 @@
977 print('Checking for Windows specific modules...')
978 for m in WIN32_MODULES:
979 check_module(m)
980+ if IS_OSX:
981+ print('Checking for OS X specific modules...')
982+ for m in OSX_MODULES:
983+ check_module(m)
984 verify_versions()
985 print_qt_image_formats()
986 print_enchant_backends_and_languages()
987
988=== added file 'tests/functional/openlp_plugins/presentations/test_keynotemaccontroller.py'
989--- tests/functional/openlp_plugins/presentations/test_keynotemaccontroller.py 1970-01-01 00:00:00 +0000
990+++ tests/functional/openlp_plugins/presentations/test_keynotemaccontroller.py 2013-10-24 16:44:10 +0000
991@@ -0,0 +1,93 @@
992+"""
993+This module contains tests for the KeynoteController class of the Presentations plugin.
994+"""
995+
996+from unittest import TestCase
997+
998+from mock import MagicMock
999+import os
1000+from openlp.plugins.presentations.lib.keynotemaccontroller import KeynoteController, KeynoteDocument
1001+
1002+RESOURCES_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..', 'resources'))
1003+
1004+# TODO: write tests for classes
1005+# - KeynoteController
1006+# - KeynoteDocument
1007+
1008+class TestKeynoteController(TestCase):
1009+ """
1010+ Test the KeynoteController class
1011+ """
1012+ def setUp(self):
1013+ """
1014+ Set up the KeynoteController
1015+ """
1016+ presentation_controller = MagicMock()
1017+ presentation_controller.settings_section = 'presentations'
1018+ self.keynote_controller = KeynoteController(presentation_controller)
1019+ self.file_name = os.path.join(RESOURCES_PATH,'test.key')
1020+ self.doc = KeynoteDocument(self.keynote_controller,self.file_name)
1021+ self.doc.presentation_deleted()
1022+
1023+ def kill_test(self):
1024+ """
1025+ Test running the kill() method with an KeynoteController
1026+ """
1027+ # GIVEN: A KeynoteController instance and a list of documents
1028+ presentation_controller = MagicMock()
1029+ presentation_controller.settings_section = 'presentations'
1030+ keynote_controller = KeynoteController(presentation_controller)
1031+ key_document = MagicMock()
1032+ keynote_controller.docs.append(key_document)
1033+ # WHEN: we run kill()
1034+ keynote_controller.kill()
1035+ # THEN: all docs should be deleted from controller
1036+ assert keynote_controller.docs.count() == 0, 'The keynote_controller should close all documents.'
1037+ assert keynote_controller.process in None, 'The keynote_controller should shout down KeyNote application.'
1038+
1039+ def verify_installation_test(self):
1040+ """
1041+ Test the installation of KeyNote
1042+ """
1043+ # GIVEN: A boolean value set to true
1044+ # WHEN: We "convert" it to a bool
1045+ is_installed = self.keynote_controller.check_available()
1046+ # THEN: We should get back a True bool
1047+ assert is_installed is True, u'The result of check_available() should be True'
1048+
1049+ def start_process_test(self):
1050+ """
1051+ Test the start_process() of KeyNote
1052+ """
1053+ # GIVEN: A boolean value set to true
1054+ # WHEN: We "convert" it to a bool
1055+ self.keynote_controller.start_process()
1056+ # THEN: The process should not be None
1057+ assert self.keynote_controller.process is not None, u'The result of start_process() should be True'
1058+
1059+ def load_presentation_test(self):
1060+ """
1061+ Test loading a document in KeyNote
1062+ """
1063+ # GIVEN: the filename
1064+ self.doc = KeynoteDocument(self.keynote_controller,self.file_name)
1065+ # WHEN: loading the filename
1066+ self.doc.load_presentation()
1067+ result = self.doc.is_loaded()
1068+ # THEN: result should be true
1069+ assert result is True, u'The KeyNote should load document'
1070+ assert self.keynote_controller.docs.count() != 0, 'The keynote_controller.docs should have one document.'
1071+
1072+ def close_presentation_test(self):
1073+ """
1074+ Test closing a document in KeyNote
1075+ """
1076+ # GIVEN: loading the filename
1077+ self.doc = KeynoteDocument(self.keynote_controller,self.file_name)
1078+ self.doc.load_presentation()
1079+ # WHEN: closing the filename
1080+ self.doc.close_presentation()
1081+ result = self.doc.is_loaded()
1082+ # THEN: result should be true
1083+ assert result is False, u'The KeyNote should close document'
1084+ assert self.keynote_controller.docs.count() == 0, 'The keynote_controller.docs should have 0 documents.'
1085
1086=== added file 'tests/functional/openlp_plugins/presentations/test_powepointmaccontroller.py'
1087--- tests/functional/openlp_plugins/presentations/test_powepointmaccontroller.py 1970-01-01 00:00:00 +0000
1088+++ tests/functional/openlp_plugins/presentations/test_powepointmaccontroller.py 2013-10-24 16:44:10 +0000
1089@@ -0,0 +1,93 @@
1090+"""
1091+This module contains tests for the PowerpointController class of the Presentations plugin.
1092+"""
1093+
1094+from unittest import TestCase
1095+
1096+from mock import MagicMock
1097+import os
1098+from openlp.plugins.presentations.lib.powerpointmaccontroller import PowerpointController, PowerpointDocument
1099+
1100+RESOURCES_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..', 'resources'))
1101+
1102+# TODO: write tests for classes
1103+# - PowerpointController
1104+# - PowerpointDocument
1105+
1106+class TestPowerpointController(TestCase):
1107+ """
1108+ Test the PowerpointController class
1109+ """
1110+ def setUp(self):
1111+ """
1112+ Set up the PowerpointController
1113+ """
1114+ presentation_controller = MagicMock()
1115+ presentation_controller.settings_section = 'presentations'
1116+ self.powerpoint_controller = PowerpointController(presentation_controller)
1117+ self.file_name = os.path.join(RESOURCES_PATH,'test.pptx')
1118+ self.doc = PowerpointDocument(self.powerpoint_controller,self.file_name)
1119+ self.doc.presentation_deleted()
1120+
1121+ def kill_test(self):
1122+ """
1123+ Test running the kill() method with an PowerpointController
1124+ """
1125+ # GIVEN: A PowerpointController instance and a list of documents
1126+ presentation_controller = MagicMock()
1127+ presentation_controller.settings_section = 'presentations'
1128+ powerpoint_controller = PowerpointController(presentation_controller)
1129+ ppt_document = MagicMock()
1130+ powerpoint_controller.docs.append(ppt_document)
1131+ # WHEN: we run kill()
1132+ powerpoint_controller.kill()
1133+ # THEN: all docs should be deleted from controller
1134+ assert powerpoint_controller.docs.count() == 0, 'The powerpoint_controller should close all documents.'
1135+ assert powerpoint_controller.process in None, 'The powerpoint_controller should shout down PPT application.'
1136+
1137+ def verify_installation_test(self):
1138+ """
1139+ Test the installation of PPT
1140+ """
1141+ # GIVEN: A boolean value set to true
1142+ # WHEN: We "convert" it to a bool
1143+ is_installed = self.powerpoint_controller.check_available()
1144+ # THEN: We should get back a True bool
1145+ assert is_installed is True, u'The result of check_available() should be True'
1146+
1147+ def start_process_test(self):
1148+ """
1149+ Test the start_process() of PPT
1150+ """
1151+ # GIVEN: A boolean value set to true
1152+ # WHEN: We "convert" it to a bool
1153+ self.powerpoint_controller.start_process()
1154+ # THEN: The process should not be None
1155+ assert self.powerpoint_controller.process is not None, u'The result of start_process() should be True'
1156+
1157+ def load_presentation_test(self):
1158+ """
1159+ Test loading a document in PPT
1160+ """
1161+ # GIVEN: the filename
1162+ self.doc = PowerpointDocument(self.powerpoint_controller,self.file_name)
1163+ # WHEN: loading the filename
1164+ self.doc.load_presentation()
1165+ result = self.doc.is_loaded()
1166+ # THEN: result should be true
1167+ assert result is True, u'The PPT should load document'
1168+ assert self.powerpoint_controller.docs.count() != 0, 'The powerpoint_controller.docs should have one document.'
1169+
1170+ def close_presentation_test(self):
1171+ """
1172+ Test closing a document in PPT
1173+ """
1174+ # GIVEN: loading the filename
1175+ self.doc = PowerpointDocument(self.powerpoint_controller,self.file_name)
1176+ self.doc.load_presentation()
1177+ # WHEN: closing the filename
1178+ self.doc.close_presentation()
1179+ result = self.doc.is_loaded()
1180+ # THEN: result should be true
1181+ assert result is False, u'The PPT should close document'
1182+ assert self.powerpoint_controller.docs.count() == 0, 'The powerpoint_controller.docs should have 0 documents.'