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

Proposed by Dmitriy Marmyshev
Status: Rejected
Rejected by: Tim Bentley
Proposed branch: lp:~marmyshev/openlp/presentation
Merge into: lp:openlp
Diff against target: 1187 lines (+994/-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 (+360/-0)
openlp/plugins/presentations/lib/powerpointmaccontroller.py (+397/-0)
openlp/plugins/presentations/lib/presentationtab.py (+14/-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
Raoul Snyman Needs Fixing
Tim Bentley Pending
Andreas Preikschat Pending
matysek Pending
Dmitriy Marmyshev Pending
Review via email: mp+192798@code.launchpad.net

This proposal supersedes 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 : Posted in a previous version of this proposal

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

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

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 : Posted in a previous version of this proposal
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 : Posted in a previous version of this proposal
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 : Posted in a previous version of this proposal

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 : Posted in a previous version of this proposal

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 : Posted in a previous version of this proposal

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 : Posted in a previous version of this proposal

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 : Posted in a previous version of this proposal

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 : Posted in a previous version of this proposal

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 : Posted in a previous version of this proposal

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 : Posted in a previous version of this proposal

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 : Posted in a previous version of this proposal

> 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?

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

matysek:
Please aproove again - I just added tests and checking version of appscript

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

Phill:
We need to investigate more why PPT 14.0 is not working well. I guess we could find problem and fix it later.

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

It still works.

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

matysek: please approve it in actual proposal version :)

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

Hi Dmitriy,

One last thing: we've recently changed the way you import the "patch" and "MagicMock" objects. Please see the other tests for how to do it.

http://ci.openlp.org/view/Branch/job/Branch-02-Functional-Tests/8/console

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

When testing the latest version thumbnails are generated but when I click preview button to see the preview of the presentation in OpenLP then I get this traceback:

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 187
Error in sys.excepthook:
RuntimeError: super-class __init__() of type ExceptionForm was never called

Original exception was:
Traceback (most recent call last):
  File "/Users/martin/Projects/openlp/bug-836574/openlp/core/lib/mediamanageritem.py", line 486, in on_preview_click
    self.preview_controller.add_service_item(service_item)
  File "/Users/martin/Projects/openlp/bug-836574/openlp/core/ui/slidecontroller.py", line 689, in add_service_item
    self._process_item(item, slide_no)
  File "/Users/martin/Projects/openlp/bug-836574/openlp/core/ui/slidecontroller.py", line 736, in _process_item
    '%s_start' % service_item.name.lower(), [service_item, self.is_live, self.hide_mode(), slideno])
  File "/Users/martin/Projects/openlp/bug-836574/openlp/core/lib/registry.py", line 159, in execute
    result = function(*args, **kwargs)
  File "/Users/martin/Projects/openlp/bug-836574/openlp/plugins/presentations/lib/messagelistener.py", line 319, in startup
    if self.handler == self.media_item.Automatic:

review: Needs Fixing
Revision history for this message
Raoul Snyman (raoul-snyman) wrote :

At least one of your import mock statements is incorrect, please see the other tests for how to correctly import mock. Also, our CI server is a Linux box, so it appears that the "appscript" module is not available. Please set the test to skip if it is not running on a Mac.

http://ci.openlp.org/view/Branch/job/Branch-02-Functional-Tests/10/console

review: Needs Fixing
Revision history for this message
Tomas Groth (tomasgroth) wrote :

Is there any chance of you fixing the tests and merge it with recent trunk updates?
This would be a very nice addition to the 2.2 release!

Revision history for this message
Tim Bentley (trb143) wrote :

Too old and out of date.
If this is still a problem please fix and resubmit.

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