def get_vlc(): """ In order to make this module more testable, we have to wrap the VLC import inside a method. We do this so that we can mock out the VLC module entirely. :return: The "vlc" module, or None """ if 'openlp.core.ui.media.vendor.vlc' in sys.modules: # If VLC has already been imported, no need to do all the stuff below again return sys.modules['openlp.core.ui.media.vendor.vlc'] is_vlc_available = False try: if is_macosx(): # Newer versions of VLC on OS X need this. See https://forum.videolan.org/viewtopic.php?t=124521 os.environ['VLC_PLUGIN_PATH'] = '/Applications/VLC.app/Contents/MacOS/plugins' # On Windows when frozen in PyInstaller, we need to blank SetDllDirectoryW to allow loading of the VLC dll. # This is due to limitations (by desgin) in PyInstaller. SetDllDirectoryW original value is restored once # VLC has been imported. if is_win(): buffer_size = 1024 dll_directory = ctypes.create_unicode_buffer(buffer_size) new_buffer_size = ctypes.windll.kernel32.GetDllDirectoryW(buffer_size, dll_directory) dll_directory = ''.join(dll_directory[:new_buffer_size]).replace('\0', '') log.debug('Original DllDirectory: %s' % dll_directory) ctypes.windll.kernel32.SetDllDirectoryW(None) from openlp.core.ui.media.vendor import vlc if is_win(): ctypes.windll.kernel32.SetDllDirectoryW(dll_directory) is_vlc_available = bool(vlc.get_default_instance()) except (ImportError, NameError, NotImplementedError): pass except OSError as e: if is_win(): if not isinstance(e, WindowsError) and e.winerror != 126: raise else: pass if is_vlc_available: try: VERSION = vlc.libvlc_get_version().decode('UTF-8') except: VERSION = '0.0.0' # LooseVersion does not work when a string contains letter and digits (e. g. 2.0.5 Twoflower). # http://bugs.python.org/issue14894 if LooseVersion(VERSION.split()[0]) < LooseVersion('1.1.0'): is_vlc_available = False log.debug('VLC could not be loaded, because the vlc version is too old: %s' % VERSION) if is_vlc_available: return vlc else: return None
def test_pylint(self): """ Test for pylint errors """ # Test if this file is specified in the arguments, if not skip the test. in_argv = False for arg in sys.argv: if arg.endswith('test_pylint.py') or arg.endswith('test_pylint'): in_argv = True break if not in_argv: raise SkipTest('test_pylint.py not specified in arguments - skipping tests using pylint.') # GIVEN: Some checks to disable and enable, and the pylint script disabled_checks = 'import-error,no-member' enabled_checks = 'missing-format-argument-key,unused-format-string-argument,bad-format-string' if is_win() or 'arch' in platform.dist()[0].lower(): pylint_script = 'pylint' else: pylint_script = 'pylint3' # WHEN: Running pylint (pylint_stdout, pylint_stderr) = \ lint.py_run('openlp --errors-only --disable={disabled} --enable={enabled} ' '--reports=no --output-format=parseable'.format(disabled=disabled_checks, enabled=enabled_checks), return_std=True, script=pylint_script) stdout = pylint_stdout.read() stderr = pylint_stderr.read() filtered_stdout = self._filter_tolerated_errors(stdout) print(filtered_stdout) print(stderr) # THEN: The output should be empty self.assertTrue(filtered_stdout == '', 'PyLint should find no errors')
def start_ooo(self): """ Start OpenOffice.org process TODO: The presentation/Impress plugin may already have it running """ if is_win(): self.start_ooo_process() self.desktop = self.ooo_manager.createInstance('com.sun.star.frame.Desktop') else: context = uno.getComponentContext() resolver = context.ServiceManager.createInstanceWithContext('com.sun.star.bridge.UnoUrlResolver', context) uno_instance = None loop = 0 while uno_instance is None and loop < 5: try: uno_instance = get_uno_instance(resolver) except NoConnectException: time.sleep(0.1) log.exception("Failed to resolve uno connection") self.start_ooo_process() loop += 1 else: manager = uno_instance.ServiceManager self.desktop = manager.createInstanceWithContext("com.sun.star.frame.Desktop", uno_instance) return raise Exception('Unable to start LibreOffice')
def hide_display(self, mode=HideMode.Screen): """ Hide the display by making all layers transparent Store the images so they can be replaced when required :param mode: How the screen is to be hidden """ self.log_debug('hide_display mode = {mode:d}'.format(mode=mode)) if self.screens.display_count == 1: # Only make visible if setting enabled. if not Settings().value('core/display on monitor'): return if mode == HideMode.Screen: self.frame.evaluateJavaScript('show_blank("desktop");') self.setVisible(False) elif mode == HideMode.Blank or self.initial_fame: self.frame.evaluateJavaScript('show_blank("black");') else: self.frame.evaluateJavaScript('show_blank("theme");') if mode != HideMode.Screen: if self.isHidden(): self.setVisible(True) self.web_view.setVisible(True) # Workaround for bug #1531319, should not be needed with PyQt 5.6. if is_win(): self.shake_web_view() self.hide_mode = mode
def alert(self, text, location): """ Display an alert. :param text: The text to be displayed. :param location: Where on the screen is the text to be displayed """ # First we convert <>& marks to html variants, then apply # formattingtags, finally we double all backslashes for JavaScript. text_prepared = expand_tags(html.escape(text)).replace('\\', '\\\\').replace('\"', '\\\"') if self.height() != self.screen['size'].height() or not self.isVisible(): shrink = True js = 'show_alert("{text}", "{top}")'.format(text=text_prepared, top='top') else: shrink = False js = 'show_alert("{text}", "")'.format(text=text_prepared) height = self.frame.evaluateJavaScript(js) if shrink: if text: alert_height = int(height) self.resize(self.width(), alert_height) self.setVisible(True) if location == AlertLocation.Middle: self.move(self.screen['size'].left(), (self.screen['size'].height() - alert_height) // 2) elif location == AlertLocation.Bottom: self.move(self.screen['size'].left(), self.screen['size'].height() - alert_height) else: self.setVisible(False) self.setGeometry(self.screen['size']) # Workaround for bug #1531319, should not be needed with PyQt 5.6. if is_win(): self.shake_web_view()
def setup(self, display): """ Set up the media player """ vlc = get_vlc() display.vlc_widget = QtGui.QFrame(display) display.vlc_widget.setFrameStyle(QtGui.QFrame.NoFrame) # creating a basic vlc instance command_line_options = '--no-video-title-show' if not display.has_audio: command_line_options += ' --no-audio --no-video-title-show' if Settings().value('advanced/hide mouse') and display.controller.is_live: command_line_options += ' --mouse-hide-timeout=0' display.vlc_instance = vlc.Instance(command_line_options) # creating an empty vlc media player display.vlc_media_player = display.vlc_instance.media_player_new() display.vlc_widget.resize(display.size()) display.vlc_widget.raise_() display.vlc_widget.hide() # The media player has to be 'connected' to the QFrame. # (otherwise a video would be displayed in it's own window) # This is platform specific! # You have to give the id of the QFrame (or similar object) # to vlc, different platforms have different functions for this. win_id = int(display.vlc_widget.winId()) if is_win(): display.vlc_media_player.set_hwnd(win_id) elif is_macosx(): # We have to use 'set_nsobject' since Qt4 on OSX uses Cocoa # framework and not the old Carbon. display.vlc_media_player.set_nsobject(win_id) else: # for Linux/*BSD using the X Server display.vlc_media_player.set_xwindow(win_id) self.has_own_widget = True
def kill(self): """ Called at system exit to clean up any running presentations. """ log.debug('Kill OpenOffice') while self.docs: self.docs[0].close_presentation() desktop = None try: if not is_win(): desktop = self.get_uno_desktop() else: desktop = self.get_com_desktop() except: log.warning('Failed to find an OpenOffice desktop to terminate') if not desktop: return docs = desktop.getComponents() cnt = 0 if docs.hasElements(): list_elements = docs.createEnumeration() while list_elements.hasMoreElements(): doc = list_elements.nextElement() if doc.getImplementationName() != 'com.sun.star.comp.framework.BackingComp': cnt += 1 if cnt > 0: log.debug('OpenOffice not terminated as docs are still open') else: try: desktop.terminate() log.debug('OpenOffice killed') except: log.warning('Failed to terminate OpenOffice')
def load(self, display): """ Load a video into VLC :param display: The display where the media is :return: """ vlc = get_vlc() log.debug('load vid in Vlc Controller') controller = display.controller volume = controller.media_info.volume file_path = str(controller.media_info.file_info.absoluteFilePath()) path = os.path.normcase(file_path) # create the media if controller.media_info.media_type == MediaType.CD: if is_win(): path = '/' + path display.vlc_media = display.vlc_instance.media_new_location('cdda://' + path) display.vlc_media_player.set_media(display.vlc_media) display.vlc_media_player.play() # Wait for media to start playing. In this case VLC actually returns an error. self.media_state_wait(display, vlc.State.Playing) # If subitems exists, this is a CD audio_cd_tracks = display.vlc_media.subitems() if not audio_cd_tracks or audio_cd_tracks.count() < 1: return False display.vlc_media = audio_cd_tracks.item_at_index(controller.media_info.title_track) else: display.vlc_media = display.vlc_instance.media_new_path(path) # put the media in the media player display.vlc_media_player.set_media(display.vlc_media) # parse the metadata of the file display.vlc_media.parse() self.volume(display, volume) return True
def dropEvent(self, event): """ Receive drop event, check if it is a file or internal object and process it if it is. :param event: Handle of the event pint passed """ # If we are on Windows, OpenLP window will not be set on top. For example, user can drag images to Library and # the folder stays on top of the group creation box. This piece of code fixes this issue. if is_win(): self.setWindowState(self.windowState() & ~QtCore.Qt.WindowMinimized | QtCore.Qt.WindowActive) self.setWindowState(QtCore.Qt.WindowNoState) if event.mimeData().hasUrls(): event.setDropAction(QtCore.Qt.CopyAction) event.accept() files = [] for url in event.mimeData().urls(): local_file = url.toLocalFile() if os.path.isfile(local_file): files.append(local_file) elif os.path.isdir(local_file): listing = os.listdir(local_file) for file_name in listing: files.append(os.path.join(local_file, file_name)) Registry().execute('%s_dnd' % self.mime_data_text, {'files': files, 'target': self.itemAt(event.pos())}) elif self.allow_internal_dnd: event.setDropAction(QtCore.Qt.CopyAction) event.accept() Registry().execute('%s_dnd_internal' % self.mime_data_text, self.itemAt(event.pos())) else: event.ignore()
def load_presentation(self): """ Called when a presentation is added to the SlideController. It builds the environment, starts communcations with the background OpenOffice task started earlier. If OpenOffice is not present is is started. Once the environment is available the presentation is loaded and started. """ log.debug('Load Presentation OpenOffice') if is_win(): desktop = self.controller.get_com_desktop() if desktop is None: self.controller.start_process() desktop = self.controller.get_com_desktop() url = 'file:///' + self.file_path.replace('\\', '/').replace(':', '|').replace(' ', '%20') else: desktop = self.controller.get_uno_desktop() url = uno.systemPathToFileUrl(self.file_path) if desktop is None: return False self.desktop = desktop properties = [] properties.append(self.create_property('Hidden', True)) properties = tuple(properties) try: self.document = desktop.loadComponentFromURL(url, '_blank', 0, properties) except: log.warning('Failed to load presentation %s' % url) return False self.presentation = self.document.getPresentation() self.presentation.Display = ScreenList().current['number'] + 1 self.control = None self.create_thumbnails() self.create_titles_and_notes() return True
def setUp(): if not is_win(): # Wine creates a sharing violation during tests. Ignore. try: os.remove(tmpfile) except: pass
def create_thumbnails(self): """ Create thumbnail images for presentation. """ log.debug('create thumbnails OpenOffice') if self.check_thumbnails(): return if is_win(): thumb_dir_url = 'file:///' + self.get_temp_folder().replace('\\', '/') \ .replace(':', '|').replace(' ', '%20') else: thumb_dir_url = uno.systemPathToFileUrl(self.get_temp_folder()) properties = [] properties.append(self.create_property('FilterName', 'impress_png_Export')) properties = tuple(properties) doc = self.document pages = doc.getDrawPages() if not pages: return if not os.path.isdir(self.get_temp_folder()): os.makedirs(self.get_temp_folder()) for index in range(pages.getCount()): page = pages.getByIndex(index) doc.getCurrentController().setCurrentPage(page) url_path = '%s/%s.png' % (thumb_dir_url, str(index + 1)) path = os.path.join(self.get_temp_folder(), str(index + 1) + '.png') try: doc.storeToURL(url_path, properties) self.convert_thumbnail(path, index + 1) delete_file(path) except ErrorCodeIOException as exception: log.exception('ERROR! ErrorCodeIOException %d' % exception.ErrCode) except: log.exception('%s - Unable to store openoffice preview' % path)
def setup_vlc(self): """ Setup VLC instance and mediaplayer """ self.vlc_instance = vlc.Instance() # creating an empty vlc media player self.vlc_media_player = self.vlc_instance.media_player_new() # The media player has to be 'connected' to the QFrame. # (otherwise a video would be displayed in it's own window) # This is platform specific! # You have to give the id of the QFrame (or similar object) # to vlc, different platforms have different functions for this. win_id = int(self.preview_frame.winId()) if is_win(): self.vlc_media_player.set_hwnd(win_id) elif is_macosx(): # We have to use 'set_nsobject' since Qt4 on OSX uses Cocoa # framework and not the old Carbon. self.vlc_media_player.set_nsobject(win_id) else: # for Linux using the X Server self.vlc_media_player.set_xwindow(win_id) self.vlc_media = None # Setup timer every 100 ms to update position self.timer = QtCore.QTimer(self) self.timer.timeout.connect(self.update_position) self.timer.start(100) self.find_optical_devices() self.audio_cd = False self.audio_cd_tracks = None
def check_available(self): """ PPT Viewer is able to run on this machine. """ log.debug('check_available') if not is_win(): return False return self.check_installed()
def check_available(self): """ Impress is able to run on this machine. """ log.debug('check_available') if is_win(): return self.get_com_servicemanager() is not None else: return uno_available
def preview(self): """ Generates a preview of the image displayed. """ was_visible = self.isVisible() self.application.process_events() # We must have a service item to preview. if self.is_live and hasattr(self, 'service_item'): # Wait for the fade to finish before geting the preview. # Important otherwise preview will have incorrect text if at all! if self.service_item.theme_data and self.service_item.theme_data.display_slide_transition: # Workaround for bug #1531319, should not be needed with PyQt 5.6. if is_win(): fade_shake_timer = QtCore.QTimer(self) fade_shake_timer.setInterval(25) fade_shake_timer.timeout.connect(self.shake_web_view) fade_shake_timer.start() while not self.frame.evaluateJavaScript('show_text_completed()'): self.application.process_events() # Workaround for bug #1531319, should not be needed with PyQt 5.6. if is_win(): fade_shake_timer.stop() # Wait for the webview to update before getting the preview. # Important otherwise first preview will miss the background ! while not self.web_loaded: self.application.process_events() # if was hidden keep it hidden if self.is_live: if self.hide_mode: self.hide_display(self.hide_mode) # Only continue if the visibility wasn't changed during method call. elif was_visible == self.isVisible(): # Single screen active if self.screens.display_count == 1: # Only make visible if setting enabled. if Settings().value('core/display on monitor'): self.setVisible(True) else: self.setVisible(True) # Workaround for bug #1531319, should not be needed with PyQt 5.6. if is_win(): self.shake_web_view() return self.grab()
def check_installed(self): """ Check the viewer is installed. :return: True if program to open PDF-files was found, otherwise False. """ log.debug('check_installed Pdf') self.mudrawbin = '' self.mutoolbin = '' self.gsbin = '' self.also_supports = [] # Use the user defined program if given if Settings().value('presentations/enable_pdf_program'): pdf_program = Settings().value('presentations/pdf_program') program_type = self.process_check_binary(pdf_program) if program_type == 'gs': self.gsbin = pdf_program elif program_type == 'mudraw': self.mudrawbin = pdf_program elif program_type == 'mutool': self.mutoolbin = pdf_program else: # Fallback to autodetection application_path = AppLocation.get_directory(AppLocation.AppDir) if is_win(): # for windows we only accept mudraw.exe or mutool.exe in the base folder application_path = AppLocation.get_directory(AppLocation.AppDir) if os.path.isfile(os.path.join(application_path, 'mudraw.exe')): self.mudrawbin = os.path.join(application_path, 'mudraw.exe') elif os.path.isfile(os.path.join(application_path, 'mutool.exe')): self.mutoolbin = os.path.join(application_path, 'mutool.exe') else: DEVNULL = open(os.devnull, 'wb') # First try to find mudraw self.mudrawbin = which('mudraw') # if mudraw isn't installed, try mutool if not self.mudrawbin: self.mutoolbin = which('mutool') # Check we got a working mutool if not self.mutoolbin or self.process_check_binary(self.mutoolbin) != 'mutool': self.gsbin = which('gs') # Last option: check if mudraw or mutool is placed in OpenLP base folder if not self.mudrawbin and not self.mutoolbin and not self.gsbin: application_path = AppLocation.get_directory(AppLocation.AppDir) if os.path.isfile(os.path.join(application_path, 'mudraw')): self.mudrawbin = os.path.join(application_path, 'mudraw') elif os.path.isfile(os.path.join(application_path, 'mutool')): self.mutoolbin = os.path.join(application_path, 'mutool') if self.mudrawbin or self.mutoolbin: self.also_supports = ['xps', 'oxps'] return True elif self.gsbin: return True else: return False
def application(self): """ Adds the openlp to the class dynamically. Windows needs to access the application in a dynamic manner. """ if is_win(): return Registry().get('application') else: if not hasattr(self, '_application') or not self._application: self._application = Registry().get('application') return self._application
def create_property(self, name, value): """ Create an OOo style property object which are passed into some Uno methods. """ log.debug('create property OpenOffice') if is_win(): property_object = self.controller.manager.Bridge_GetStruct('com.sun.star.beans.PropertyValue') else: property_object = PropertyValue() property_object.Name = name property_object.Value = value return property_object
def test_is_linux(self): """ Test the is_linux() function """ # GIVEN: Mocked out objects with patch('openlp.core.common.os') as mocked_os, patch('openlp.core.common.sys') as mocked_sys: # WHEN: The mocked os.name and sys.platform are set to 'posix' and 'linux3' repectivly mocked_os.name = 'posix' mocked_sys.platform = 'linux3' # THEN: The three platform functions should perform properly self.assertTrue(is_linux(), 'is_linux() should return True') self.assertFalse(is_win(), 'is_win() should return False') self.assertFalse(is_macosx(), 'is_macosx() should return False')
def start_process(self): """ Loads a running version of OpenOffice in the background. It is not displayed to the user but is available to the UNO interface when required. """ log.debug('start process Openoffice') if is_win(): self.manager = self.get_com_servicemanager() self.manager._FlagAsMethod('Bridge_GetStruct') self.manager._FlagAsMethod('Bridge_GetValueObject') else: # -headless cmd = get_uno_command() self.process = QtCore.QProcess() self.process.startDetached(cmd)
def start_ooo_process(self): """ Start the OO Process """ try: if is_win(): self.ooo_manager = Dispatch('com.sun.star.ServiceManager') self.ooo_manager._FlagAsMethod('Bridge_GetStruct') self.ooo_manager._FlagAsMethod('Bridge_GetValueObject') else: cmd = get_uno_command() process = QtCore.QProcess() process.startDetached(cmd) self.process_started = True except: log.exception("start_ooo_process failed")
def verify_loading_document(self): """ Test loading a document in PowerPoint """ if is_win() and self.real_controller.check_available(): # GIVEN: A PowerpointDocument and a presentation doc = PowerpointDocument(self.real_controller, self.file_name) # WHEN: loading the filename doc.load_presentation() result = doc.is_loaded() # THEN: result should be true self.assertEqual(result, True, 'The result should be True') else: self.skipTest('Powerpoint not available, skipping test.')
def __init__(self, controller, presentation): """ Constructor, store information about the file and initialise. """ log.debug('Init Presentation Pdf') PresentationDocument.__init__(self, controller, presentation) self.presentation = None self.blanked = False self.hidden = False self.image_files = [] self.num_pages = -1 # Setup startupinfo options for check_output to avoid console popping up on windows if is_win(): self.startupinfo = STARTUPINFO() self.startupinfo.dwFlags |= STARTF_USESHOWWINDOW else: self.startupinfo = None
def get_translator(language): """ Set up a translator to use in this instance of OpenLP :param language: The language to load into the translator """ if LanguageManager.auto_language: language = QtCore.QLocale.system().name() lang_path = AppLocation.get_directory(AppLocation.LanguageDir) app_translator = QtCore.QTranslator() app_translator.load(language, lang_path) # A translator for buttons and other default strings provided by Qt. if not is_win() and not is_macosx(): lang_path = QtCore.QLibraryInfo.location(QtCore.QLibraryInfo.TranslationsPath) default_translator = QtCore.QTranslator() default_translator.load("qt_%s" % language, lang_path) return app_translator, default_translator
def load_presentation_succesfull_test(self): """ Test the PptviewDocument.load_presentation() method when the PPT is successfully opened """ # GIVEN: A reset mocked_os self.mock_os_isdir.reset() # WHEN: The temporary directory exists and OpenPPT returns successfully (not -1) self.mock_os_isdir.return_value = True self.mock_controller.process.OpenPPT.return_value = 0 instance = PptviewDocument(self.mock_controller, self.mock_presentation) instance.file_path = 'test\path.ppt' if is_win(): result = instance.load_presentation() # THEN: PptviewDocument.load_presentation should return True self.assertTrue(result)
def get_vlc(): """ In order to make this module more testable, we have to wrap the VLC import inside a method. We do this so that we can mock out the VLC module entirely. :return: The "vlc" module, or None """ if 'openlp.core.ui.media.vendor.vlc' in sys.modules: # If VLC has already been imported, no need to do all the stuff below again return sys.modules['openlp.core.ui.media.vendor.vlc'] is_vlc_available = False try: if is_macosx(): # Newer versions of VLC on OS X need this. See https://forum.videolan.org/viewtopic.php?t=124521 os.environ['VLC_PLUGIN_PATH'] = '/Applications/VLC.app/Contents/MacOS/plugins' from openlp.core.ui.media.vendor import vlc is_vlc_available = bool(vlc.get_default_instance()) except (ImportError, NameError, NotImplementedError): pass except OSError as e: if is_win(): if not isinstance(e, WindowsError) and e.winerror != 126: raise elif is_macosx(): pass else: raise if is_vlc_available: try: VERSION = vlc.libvlc_get_version().decode('UTF-8') except: VERSION = '0.0.0' # LooseVersion does not work when a string contains letter and digits (e. g. 2.0.5 Twoflower). # http://bugs.python.org/issue14894 if LooseVersion(VERSION.split()[0]) < LooseVersion('1.1.0'): is_vlc_available = False log.debug('VLC could not be loaded, because the vlc version is too old: %s' % VERSION) if is_vlc_available: return vlc else: return None
def check_available_test(self): """ Test check_available / check_installed """ # GIVEN: A mocked dll loader and a controller with patch('ctypes.cdll.LoadLibrary') as mocked_load_library: mocked_process = MagicMock() mocked_process.CheckInstalled.return_value = True mocked_load_library.return_value = mocked_process controller = PptviewController(plugin=self.mock_plugin) # WHEN: check_available is called available = controller.check_available() # THEN: On windows it should return True, on other platforms False if is_win(): self.assertTrue(available, 'check_available should return True on windows.') else: self.assertFalse(available, 'check_available should return False when not on windows.')
def delete_theme(self, theme): """ Delete a theme. :param theme: The theme to delete. """ self.theme_list.remove(theme) thumb = '{name}.png'.format(name=theme) delete_file(os.path.join(self.path, thumb)) delete_file(os.path.join(self.thumb_path, thumb)) try: # Windows is always unicode, so no need to encode filenames if is_win(): shutil.rmtree(os.path.join(self.path, theme)) else: encoding = get_filesystem_encoding() shutil.rmtree(os.path.join(self.path, theme).encode(encoding)) except OSError as os_error: shutil.Error = os_error self.log_exception('Error deleting theme {name}'.format(name=theme))
def load_presentation_un_succesfull_test(self): """ Test the PptviewDocument.load_presentation() method when the temporary directory does not exist and the PPT is not successfully opened """ # GIVEN: A reset mock_os_isdir self.mock_os_isdir.reset() # WHEN: The temporary directory does not exist and OpenPPT returns unsuccessfully (-1) with patch('openlp.plugins.presentations.lib.pptviewcontroller.os.makedirs') as mock_makedirs: self.mock_os_isdir.return_value = False self.mock_controller.process.OpenPPT.return_value = -1 instance = PptviewDocument(self.mock_controller, self.mock_presentation) instance.file_path = 'test\path.ppt' if is_win(): result = instance.load_presentation() # THEN: The temp folder should be created and PptviewDocument.load_presentation should return False mock_makedirs.assert_called_once_with(self.temp_folder) self.assertFalse(result)
def main(): """ The main function which parses command line options and then runs """ args = parse_options() qt_args = ['--disable-web-security'] # qt_args = [] if args and args.loglevel.lower() in ['d', 'debug']: log.setLevel(logging.DEBUG) elif args and args.loglevel.lower() in ['w', 'warning']: log.setLevel(logging.WARNING) else: log.setLevel(logging.INFO) # Throw the rest of the arguments at Qt, just in case. qt_args.extend(args.rargs) # Bug #1018855: Set the WM_CLASS property in X11 if not is_win() and not is_macosx(): qt_args.append('OpenLP') # Initialise the resources qInitResources() # Now create and actually run the application. application = QtWidgets.QApplication(qt_args) application.setOrganizationName('OpenLP') application.setOrganizationDomain('openlp.org') application.setAttribute(QtCore.Qt.AA_UseHighDpiPixmaps, True) application.setAttribute(QtCore.Qt.AA_DontCreateNativeWidgetSiblings, True) if args.portable: application.setApplicationName('OpenLPPortable') Settings.setDefaultFormat(Settings.IniFormat) # Get location OpenLPPortable.ini if args.portablepath: if os.path.isabs(args.portablepath): portable_path = Path(args.portablepath) else: portable_path = AppLocation.get_directory( AppLocation.AppDir) / '..' / args.portablepath else: portable_path = AppLocation.get_directory( AppLocation.AppDir) / '..' / '..' portable_path = portable_path.resolve() data_path = portable_path / 'Data' set_up_logging(portable_path / 'Other') log.info('Running portable') portable_settings_path = data_path / 'OpenLP.ini' # Make this our settings file log.info('INI file: {name}'.format(name=portable_settings_path)) Settings.set_filename(portable_settings_path) portable_settings = Settings() # Set our data path log.info('Data path: {name}'.format(name=data_path)) # Point to our data path portable_settings.setValue('advanced/data path', data_path) portable_settings.setValue('advanced/is portable', True) portable_settings.sync() else: application.setApplicationName('OpenLP') set_up_logging(AppLocation.get_directory(AppLocation.CacheDir)) # Set the libvlc environment variable if we're frozen if getattr(sys, 'frozen', False) and is_win(): # Path to libvlc and the plugins os.environ['PYTHON_VLC_LIB_PATH'] = str( AppLocation.get_directory(AppLocation.AppDir) / 'vlc' / 'libvlc.dll') os.environ['PYTHON_VLC_MODULE_PATH'] = str( AppLocation.get_directory(AppLocation.AppDir) / 'vlc') os.environ['PATH'] += ';' + str( AppLocation.get_directory(AppLocation.AppDir) / 'vlc') log.debug('VLC Path: {}'.format(os.environ['PYTHON_VLC_LIB_PATH'])) app = OpenLP() # Initialise the Registry Registry.create() Registry().register('application-qt', application) Registry().register('application', app) Registry().set_flag('no_web_server', args.no_web_server) # Upgrade settings. settings = Settings() Registry().register('settings', settings) application.setApplicationVersion(get_version()['version']) # Check if an instance of OpenLP is already running. Quit if there is a running instance and the user only wants one server = Server() if server.is_another_instance_running(): app.is_already_running() server.post_to_server(qt_args) sys.exit() else: server.start_server() app.server = server # If the custom data path is missing and the user wants to restore the data path, quit OpenLP. if app.is_data_path_missing(): server.close_server() sys.exit() if settings.can_upgrade(): now = datetime.now() # Only back up if OpenLP has previously run. if settings.value('core/has run wizard'): back_up_path = AppLocation.get_data_path() / ( now.strftime('%Y-%m-%d %H-%M') + '.conf') log.info( 'Settings about to be upgraded. Existing settings are being backed up to {back_up_path}' .format(back_up_path=back_up_path)) QtWidgets.QMessageBox.information( None, translate('OpenLP', 'Settings Upgrade'), translate( 'OpenLP', 'Your settings are about to be upgraded. A backup will be created at ' '{back_up_path}').format(back_up_path=back_up_path)) try: settings.export(back_up_path) except OSError: QtWidgets.QMessageBox.warning( None, translate('OpenLP', 'Settings Upgrade'), translate( 'OpenLP', 'Settings back up failed.\n\nContinuing to upgrade.')) settings.upgrade_settings() # First time checks in settings if not settings.value('core/has run wizard'): if not FirstTimeLanguageForm().exec(): # if cancel then stop processing server.close_server() sys.exit() # i18n Set Language language = LanguageManager.get_language() translators = LanguageManager.get_translators(language) for translator in translators: if not translator.isEmpty(): app.installTranslator(translator) if not translators: log.debug('Could not find translators.') if args and not args.no_error_form: sys.excepthook = app.hook_exception sys.exit(app.run(qt_args))
class PowerpointController(PresentationController): """ Class to control interactions with PowerPoint Presentations. It creates the runtime Environment , Loads the and Closes the Presentation. As well as triggering the correct activities based on the users input. """ log.info('PowerpointController loaded') def __init__(self, plugin): """ Initialise the class """ log.debug('Initialising') super(PowerpointController, self).__init__(plugin, 'Powerpoint', PowerpointDocument) self.supports = ['ppt', 'pps', 'pptx', 'ppsx', 'pptm'] self.process = None def check_available(self): """ PowerPoint is able to run on this machine. """ log.debug('check_available') if is_win(): try: winreg.OpenKey(winreg.HKEY_CLASSES_ROOT, 'PowerPoint.Application').Close() try: # Try to detect if the version is 12 (2007) or above, and if so add 'odp' as a support filetype version_key = winreg.OpenKey(winreg.HKEY_CLASSES_ROOT, 'PowerPoint.Application\\CurVer') tmp1, app_version_string, tmp2 = winreg.EnumValue(version_key, 0) version_key.Close() app_version = int(app_version_string[-2:]) if app_version >= 12: self.also_supports = ['odp'] except (OSError, ValueError): log.warning('Detection of powerpoint version using registry failed.') return True except OSError: pass return False if is_win(): def start_process(self): """ Loads PowerPoint process. """ log.debug('start_process') if not self.process: self.process = Dispatch('PowerPoint.Application') def kill(self): """ Called at system exit to clean up any running presentations. """ log.debug('Kill powerpoint') while self.docs: self.docs[0].close_presentation() if self.process is None: return try: if self.process.Presentations.Count > 0: return self.process.Quit() except (AttributeError, pywintypes.com_error) as e: log.exception('Exception caught while killing powerpoint process') log.exception(e) trace_error_handler(log) self.process = None
def on_load_disc_button_clicked(self, clicked): """ Load the media when the load-button has been clicked :param clicked: Given from signal, not used. """ log.debug('on_load_disc_button_clicked') vlc = get_vlc() self.disable_all() self.application.set_busy_cursor() path = self.media_path_combobox.currentText() # Check if given path is non-empty and exists before starting VLC if not path: log.debug('no given path') critical_error_message_box(message=translate( 'MediaPlugin.MediaClipSelectorForm', 'No path was given')) self.toggle_disable_load_media(False) self.application.set_normal_cursor() return if not os.path.exists(path): log.debug('Given path does not exists') critical_error_message_box( message=translate('MediaPlugin.MediaClipSelectorForm', 'Given path does not exists')) self.toggle_disable_load_media(False) self.application.set_normal_cursor() return # VLC behaves a bit differently on windows and linux when loading, which creates problems when trying to # detect if we're dealing with a DVD or CD, so we use different loading approaches depending on the OS. if is_win(): # If the given path is in the format "D:\" or "D:", prefix it with "/" to make VLC happy pattern = re.compile(r'^\w:\\\\*$') if pattern.match(path): path = '/' + path self.vlc_media = self.vlc_instance.media_new_location('dvd://' + path) else: self.vlc_media = self.vlc_instance.media_new_path(path) if not self.vlc_media: log.debug('vlc media player is none') critical_error_message_box(message=translate( 'MediaPlugin.MediaClipSelectorForm', 'An error happened during initialization of VLC player')) self.toggle_disable_load_media(False) self.application.set_normal_cursor() return # put the media in the media player self.vlc_media_player.set_media(self.vlc_media) self.vlc_media_player.audio_set_mute(True) # start playback to get vlc to parse the media if self.vlc_media_player.play() < 0: log.debug('vlc play returned error') critical_error_message_box( message=translate('MediaPlugin.MediaClipSelectorForm', 'VLC player failed playing the media')) self.toggle_disable_load_media(False) self.application.set_normal_cursor() self.vlc_media_player.audio_set_mute(False) return self.vlc_media_player.audio_set_mute(True) if not self.media_state_wait(vlc.State.Playing): # Tests if this is an audio CD if not self.detect_audio_cd(path): critical_error_message_box( message=translate('MediaPlugin.MediaClipSelectorForm', 'VLC player failed playing the media')) self.toggle_disable_load_media(False) self.application.set_normal_cursor() self.vlc_media_player.audio_set_mute(False) return # pause self.vlc_media_player.set_time(0) self.vlc_media_player.set_pause(1) self.media_state_wait(vlc.State.Paused) self.toggle_disable_load_media(False) self.application.set_normal_cursor() self.vlc_media_player.audio_set_mute(False) if not self.audio_cd: # Temporarily disable signals self.blockSignals(True) # Get titles, insert in combobox titles = self.vlc_media_player.video_get_title_description() self.titles_combo_box.clear() for title in titles: self.titles_combo_box.addItem(title[1].decode(), title[0]) # Re-enable signals self.blockSignals(False) # Main title is usually title #1 if len(titles) > 1: self.titles_combo_box.setCurrentIndex(1) # Enable audio track combobox if anything is in it if len(titles) > 0: self.titles_combo_box.setDisabled(False) log.debug('load_disc_button end - ' 'vlc_media_player state: {state}'.format( state=self.vlc_media_player.get_state()))
def find_optical_devices(self): """ Attempt to autodetect optical devices on the computer, and add them to the media-dropdown :return: """ # Clear list first self.media_path_combobox.clear() if is_win(): # use win api to find optical drives fso = Dispatch('scripting.filesystemobject') for drive in fso.Drives: log.debug('Drive {drive} has type {types:d}'.format( drive=drive.DriveLetter, types=drive.DriveType)) # if type is 4, it is a cd-rom drive if drive.DriveType == 4: self.media_path_combobox.addItem( '{drive}:\\'.format(drive=drive.DriveLetter)) elif is_linux(): # Get disc devices from dbus and find the ones that are optical bus = dbus.SystemBus() try: udev_manager_obj = bus.get_object('org.freedesktop.UDisks', '/org/freedesktop/UDisks') udev_manager = dbus.Interface(udev_manager_obj, 'org.freedesktop.UDisks') for dev in udev_manager.EnumerateDevices(): device_obj = bus.get_object("org.freedesktop.UDisks", dev) device_props = dbus.Interface(device_obj, dbus.PROPERTIES_IFACE) if device_props.Get('org.freedesktop.UDisks.Device', 'DeviceIsDrive'): drive_props = device_props.Get( 'org.freedesktop.UDisks.Device', 'DriveMediaCompatibility') if any('optical' in prop for prop in drive_props): self.media_path_combobox.addItem( device_props.Get( 'org.freedesktop.UDisks.Device', 'DeviceFile')) return except dbus.exceptions.DBusException: log.debug('could not use udisks, will try udisks2') udev_manager_obj = bus.get_object('org.freedesktop.UDisks2', '/org/freedesktop/UDisks2') udev_manager = dbus.Interface( udev_manager_obj, 'org.freedesktop.DBus.ObjectManager') for k, v in udev_manager.GetManagedObjects().items(): drive_info = v.get('org.freedesktop.UDisks2.Drive', {}) drive_props = drive_info.get('MediaCompatibility') if drive_props and any('optical' in prop for prop in drive_props): for device in udev_manager.GetManagedObjects().values(): if dbus.String( 'org.freedesktop.UDisks2.Block') in device: if device[dbus.String( 'org.freedesktop.UDisks2.Block')][ dbus.String('Drive')] == k: block_file = '' for c in device[dbus.String( 'org.freedesktop.UDisks2.Block')][ dbus.String('PreferredDevice')]: if chr(c) != '\x00': block_file += chr(c) self.media_path_combobox.addItem(block_file) elif is_macosx(): # Look for DVD folders in devices to find optical devices volumes = os.listdir('/Volumes') for volume in volumes: if volume.startswith('.'): continue dirs = os.listdir('/Volumes/' + volume) # Detect DVD if 'VIDEO_TS' in dirs: self.media_path_combobox.addItem('/Volumes/' + volume) # Detect audio cd files = [f for f in dirs if os.path.isfile(f)] for file in files: if file.endswith('aiff'): self.media_path_combobox.addItem('/Volumes/' + volume) break
def check_installed(self): """ Check the viewer is installed. :return: True if program to open PDF-files was found, otherwise False. """ log.debug('check_installed Pdf') self.mudrawbin = '' self.mutoolbin = '' self.gsbin = '' self.also_supports = [] # Use the user defined program if given if Settings().value('presentations/enable_pdf_program'): pdf_program = Settings().value('presentations/pdf_program') program_type = self.process_check_binary(pdf_program) if program_type == 'gs': self.gsbin = pdf_program elif program_type == 'mudraw': self.mudrawbin = pdf_program elif program_type == 'mutool': self.mutoolbin = pdf_program else: # Fallback to autodetection application_path = AppLocation.get_directory(AppLocation.AppDir) if is_win(): # for windows we only accept mudraw.exe or mutool.exe in the base folder application_path = AppLocation.get_directory( AppLocation.AppDir) if os.path.isfile(os.path.join(application_path, 'mudraw.exe')): self.mudrawbin = os.path.join(application_path, 'mudraw.exe') elif os.path.isfile( os.path.join(application_path, 'mutool.exe')): self.mutoolbin = os.path.join(application_path, 'mutool.exe') else: DEVNULL = open(os.devnull, 'wb') # First try to find mudraw self.mudrawbin = which('mudraw') # if mudraw isn't installed, try mutool if not self.mudrawbin: self.mutoolbin = which('mutool') # Check we got a working mutool if not self.mutoolbin or self.process_check_binary( self.mutoolbin) != 'mutool': self.gsbin = which('gs') # Last option: check if mudraw or mutool is placed in OpenLP base folder if not self.mudrawbin and not self.mutoolbin and not self.gsbin: application_path = AppLocation.get_directory( AppLocation.AppDir) if os.path.isfile(os.path.join(application_path, 'mudraw')): self.mudrawbin = os.path.join(application_path, 'mudraw') elif os.path.isfile( os.path.join(application_path, 'mutool')): self.mutoolbin = os.path.join(application_path, 'mutool') if self.mudrawbin or self.mutoolbin: self.also_supports = ['xps', 'oxps'] return True elif self.gsbin: return True else: return False
class Settings(QtCore.QSettings): """ Class to wrap QSettings. * Exposes all the methods of QSettings. * Adds functionality for OpenLP Portable. If the ``defaultFormat`` is set to ``IniFormat``, and the path to the Ini file is set using ``set_filename``, then the Settings constructor (without any arguments) will create a Settings object for accessing settings stored in that Ini file. ``__default_settings__`` This dict contains all core settings with their default values. ``__obsolete_settings__`` Each entry is structured in the following way:: ('general/enable slide loop', 'advanced/slide limits', [(SlideLimits.Wrap, True), (SlideLimits.End, False)]) The first entry is the *old key*; it will be removed. The second entry is the *new key*; we will add it to the config. If this is just an empty string, we just remove the old key. The last entry is a list containing two-pair tuples. If the list is empty, no conversion is made. If the first value is callable i.e. a function, the function will be called with the old setting's value. Otherwise each pair describes how to convert the old setting's value:: (SlideLimits.Wrap, True) This means, that if the value of ``general/enable slide loop`` is equal (``==``) ``True`` then we set ``advanced/slide limits`` to ``SlideLimits.Wrap``. **NOTE**, this means that the rules have to cover all cases! So, if the type of the old value is bool, then there must be two rules. """ __default_settings__ = { 'advanced/add page break': False, 'advanced/alternate rows': not is_win(), 'advanced/autoscrolling': { 'dist': 1, 'pos': 0 }, 'advanced/current media plugin': -1, 'advanced/data path': '', # 7 stands for now, 0 to 6 is Monday to Sunday. 'advanced/default service day': 7, 'advanced/default service enabled': True, 'advanced/default service hour': 11, 'advanced/default service minute': 0, 'advanced/default service name': UiStrings().DefaultServiceName, 'advanced/display size': 0, 'advanced/double click live': False, 'advanced/enable exit confirmation': True, 'advanced/expand service item': False, 'advanced/hide mouse': True, 'advanced/is portable': False, 'advanced/max recent files': 20, 'advanced/print file meta data': False, 'advanced/print notes': False, 'advanced/print slide text': False, 'advanced/recent file count': 4, 'advanced/save current plugin': False, 'advanced/slide limits': SlideLimits.End, 'advanced/slide max height': -4, 'advanced/single click preview': False, 'advanced/single click service preview': False, 'advanced/x11 bypass wm': X11_BYPASS_DEFAULT, 'advanced/search as type': True, 'crashreport/last directory': '', 'formattingTags/html_tags': '', 'core/audio repeat list': False, 'core/auto open': False, 'core/auto preview': False, 'core/audio start paused': True, 'core/auto unblank': False, 'core/click live slide to unblank': False, 'core/blank warning': False, 'core/ccli number': '', 'core/has run wizard': False, 'core/language': '[en]', 'core/last version test': '', 'core/loop delay': 5, 'core/recent files': [], 'core/save prompt': False, 'core/screen blank': False, 'core/show splash': True, 'core/logo background color': '#ffffff', 'core/logo file': ':/graphics/openlp-splash-screen.png', 'core/logo hide on startup': False, 'core/songselect password': '', 'core/songselect username': '', 'core/update check': True, 'core/view mode': 'default', # The other display settings (display position and dimensions) are defined in the ScreenList class due to a # circular dependency. 'core/display on monitor': True, 'core/override position': False, 'core/application version': '0.0', 'images/background color': '#000000', 'media/players': 'system,webkit', 'media/override player': QtCore.Qt.Unchecked, 'players/background color': '#000000', 'servicemanager/last directory': '', 'servicemanager/last file': '', 'servicemanager/service theme': '', 'SettingsImport/file_date_created': datetime.datetime.now().strftime("%Y-%m-%d %H:%M"), 'SettingsImport/Make_Changes': 'At_Own_RISK', 'SettingsImport/type': 'OpenLP_settings_export', 'SettingsImport/version': '', 'themes/global theme': '', 'themes/last directory': '', 'themes/last directory export': '', 'themes/last directory import': '', 'themes/theme level': ThemeLevel.Song, 'themes/wrap footer': False, 'user interface/live panel': True, 'user interface/live splitter geometry': QtCore.QByteArray(), 'user interface/lock panel': True, 'user interface/main window geometry': QtCore.QByteArray(), 'user interface/main window position': QtCore.QPoint(0, 0), 'user interface/main window splitter geometry': QtCore.QByteArray(), 'user interface/main window state': QtCore.QByteArray(), 'user interface/preview panel': True, 'user interface/preview splitter geometry': QtCore.QByteArray(), 'user interface/is preset layout': False, 'projector/show after wizard': False, 'projector/db type': 'sqlite', 'projector/db username': '', 'projector/db password': '', 'projector/db hostname': '', 'projector/db database': '', 'projector/enable': True, 'projector/connect on start': False, 'projector/last directory import': '', 'projector/last directory export': '', 'projector/poll time': 20, # PJLink timeout is 30 seconds 'projector/socket timeout': 5, # 5 second socket timeout 'projector/source dialog type': 0 # Source select dialog box type } __file_path__ = '' __obsolete_settings__ = [ # Changed during 2.2.x development. # ('advanced/stylesheet fix', '', []), # ('general/recent files', 'core/recent files', [(recent_files_conv, None)]), ('songs/search as type', 'advanced/search as type', []), ('media/players', 'media/players_temp', [(media_players_conv, None)]), # Convert phonon to system ('media/players_temp', 'media/players', []), # Move temp setting from above to correct setting ('advanced/default color', 'core/logo background color', []), # Default image renamed + moved to general > 2.4. ('advanced/default image', '/core/logo file', [] ) # Default image renamed + moved to general after 2.4. ] @staticmethod def extend_default_settings(default_values): """ Static method to merge the given ``default_values`` with the ``Settings.__default_settings__``. :param default_values: A dict with setting keys and their default values. """ Settings.__default_settings__.update(default_values) @staticmethod def set_filename(ini_file): """ Sets the complete path to an Ini file to be used by Settings objects. Does not affect existing Settings objects. """ Settings.__file_path__ = ini_file @staticmethod def set_up_default_values(): """ This static method is called on start up. It is used to perform any operation on the __default_settings__ dict. """ # Make sure the string is translated (when building the dict the string is not translated because the translate # function was not set up as this stage). Settings.__default_settings__[ 'advanced/default service name'] = UiStrings().DefaultServiceName def __init__(self, *args): """ Constructor which checks if this should be a native settings object, or an INI file. """ if not args and Settings.__file_path__ and Settings.defaultFormat( ) == Settings.IniFormat: QtCore.QSettings.__init__(self, Settings.__file_path__, Settings.IniFormat) else: QtCore.QSettings.__init__(self, *args) # Add shortcuts here so QKeySequence has a QApplication instance to use. Settings.__default_settings__.update({ 'shortcuts/aboutItem': [QtGui.QKeySequence(QtCore.Qt.CTRL + QtCore.Qt.Key_F1)], 'shortcuts/addToService': [], 'shortcuts/audioPauseItem': [], 'shortcuts/displayTagItem': [], 'shortcuts/blankScreen': [QtGui.QKeySequence(QtCore.Qt.Key_Period)], 'shortcuts/collapse': [QtGui.QKeySequence(QtCore.Qt.Key_Minus)], 'shortcuts/desktopScreen': [QtGui.QKeySequence(QtCore.Qt.Key_D)], 'shortcuts/delete': [QtGui.QKeySequence(QtGui.QKeySequence.Delete)], 'shortcuts/down': [QtGui.QKeySequence(QtCore.Qt.Key_Down)], 'shortcuts/editSong': [], 'shortcuts/escapeItem': [QtGui.QKeySequence(QtCore.Qt.Key_Escape)], 'shortcuts/expand': [QtGui.QKeySequence(QtCore.Qt.Key_Plus)], 'shortcuts/exportThemeItem': [], 'shortcuts/fileNewItem': [QtGui.QKeySequence(QtGui.QKeySequence.New)], 'shortcuts/fileSaveAsItem': [QtGui.QKeySequence(QtGui.QKeySequence.SaveAs)], 'shortcuts/fileExitItem': [QtGui.QKeySequence(QtGui.QKeySequence.Quit)], 'shortcuts/fileSaveItem': [QtGui.QKeySequence(QtGui.QKeySequence.Save)], 'shortcuts/fileOpenItem': [QtGui.QKeySequence(QtGui.QKeySequence.Open)], 'shortcuts/goLive': [], 'shortcuts/importThemeItem': [], 'shortcuts/importBibleItem': [], 'shortcuts/listViewBiblesDeleteItem': [QtGui.QKeySequence(QtGui.QKeySequence.Delete)], 'shortcuts/listViewBiblesPreviewItem': [ QtGui.QKeySequence(QtCore.Qt.Key_Return), QtGui.QKeySequence(QtCore.Qt.Key_Enter) ], 'shortcuts/listViewBiblesLiveItem': [ QtGui.QKeySequence(QtCore.Qt.SHIFT + QtCore.Qt.Key_Return), QtGui.QKeySequence(QtCore.Qt.SHIFT + QtCore.Qt.Key_Enter) ], 'shortcuts/listViewBiblesServiceItem': [ QtGui.QKeySequence(QtCore.Qt.Key_Plus), QtGui.QKeySequence(QtCore.Qt.Key_Equal) ], 'shortcuts/listViewCustomDeleteItem': [QtGui.QKeySequence(QtGui.QKeySequence.Delete)], 'shortcuts/listViewCustomPreviewItem': [ QtGui.QKeySequence(QtCore.Qt.Key_Return), QtGui.QKeySequence(QtCore.Qt.Key_Enter) ], 'shortcuts/listViewCustomLiveItem': [ QtGui.QKeySequence(QtCore.Qt.SHIFT + QtCore.Qt.Key_Return), QtGui.QKeySequence(QtCore.Qt.SHIFT + QtCore.Qt.Key_Enter) ], 'shortcuts/listViewCustomServiceItem': [ QtGui.QKeySequence(QtCore.Qt.Key_Plus), QtGui.QKeySequence(QtCore.Qt.Key_Equal) ], 'shortcuts/listViewImagesDeleteItem': [QtGui.QKeySequence(QtGui.QKeySequence.Delete)], 'shortcuts/listViewImagesPreviewItem': [ QtGui.QKeySequence(QtCore.Qt.Key_Return), QtGui.QKeySequence(QtCore.Qt.Key_Enter) ], 'shortcuts/listViewImagesLiveItem': [ QtGui.QKeySequence(QtCore.Qt.SHIFT + QtCore.Qt.Key_Return), QtGui.QKeySequence(QtCore.Qt.SHIFT + QtCore.Qt.Key_Enter) ], 'shortcuts/listViewImagesServiceItem': [ QtGui.QKeySequence(QtCore.Qt.Key_Plus), QtGui.QKeySequence(QtCore.Qt.Key_Equal) ], 'shortcuts/listViewMediaDeleteItem': [QtGui.QKeySequence(QtGui.QKeySequence.Delete)], 'shortcuts/listViewMediaPreviewItem': [ QtGui.QKeySequence(QtCore.Qt.Key_Return), QtGui.QKeySequence(QtCore.Qt.Key_Enter) ], 'shortcuts/listViewMediaLiveItem': [ QtGui.QKeySequence(QtCore.Qt.SHIFT + QtCore.Qt.Key_Return), QtGui.QKeySequence(QtCore.Qt.SHIFT + QtCore.Qt.Key_Enter) ], 'shortcuts/listViewMediaServiceItem': [ QtGui.QKeySequence(QtCore.Qt.Key_Plus), QtGui.QKeySequence(QtCore.Qt.Key_Equal) ], 'shortcuts/listViewPresentationsDeleteItem': [ QtGui.QKeySequence(QtGui.QKeySequence.Delete) ], 'shortcuts/listViewPresentationsPreviewItem': [ QtGui.QKeySequence(QtCore.Qt.Key_Return), QtGui.QKeySequence(QtCore.Qt.Key_Enter) ], 'shortcuts/listViewPresentationsLiveItem': [ QtGui.QKeySequence(QtCore.Qt.SHIFT + QtCore.Qt.Key_Return), QtGui.QKeySequence(QtCore.Qt.SHIFT + QtCore.Qt.Key_Enter) ], 'shortcuts/listViewPresentationsServiceItem': [ QtGui.QKeySequence(QtCore.Qt.Key_Plus), QtGui.QKeySequence(QtCore.Qt.Key_Equal) ], 'shortcuts/listViewSongsDeleteItem': [ QtGui.QKeySequence(QtGui.QKeySequence.Delete) ], 'shortcuts/listViewSongsPreviewItem': [ QtGui.QKeySequence(QtCore.Qt.Key_Return), QtGui.QKeySequence(QtCore.Qt.Key_Enter) ], 'shortcuts/listViewSongsLiveItem': [ QtGui.QKeySequence(QtCore.Qt.SHIFT + QtCore.Qt.Key_Return), QtGui.QKeySequence(QtCore.Qt.SHIFT + QtCore.Qt.Key_Enter) ], 'shortcuts/listViewSongsServiceItem': [ QtGui.QKeySequence(QtCore.Qt.Key_Plus), QtGui.QKeySequence(QtCore.Qt.Key_Equal) ], 'shortcuts/lockPanel': [], 'shortcuts/modeDefaultItem': [], 'shortcuts/modeLiveItem': [], 'shortcuts/make_live': [ QtGui.QKeySequence(QtCore.Qt.Key_Return), QtGui.QKeySequence(QtCore.Qt.Key_Enter) ], 'shortcuts/moveUp': [QtGui.QKeySequence(QtCore.Qt.Key_PageUp)], 'shortcuts/moveTop': [QtGui.QKeySequence(QtCore.Qt.Key_Home)], 'shortcuts/modeSetupItem': [], 'shortcuts/moveBottom': [QtGui.QKeySequence(QtCore.Qt.Key_End)], 'shortcuts/moveDown': [QtGui.QKeySequence(QtCore.Qt.Key_PageDown)], 'shortcuts/nextTrackItem': [], 'shortcuts/nextItem_live': [ QtGui.QKeySequence(QtCore.Qt.Key_Down), QtGui.QKeySequence(QtCore.Qt.Key_PageDown) ], 'shortcuts/nextItem_preview': [ QtGui.QKeySequence(QtCore.Qt.Key_Down), QtGui.QKeySequence(QtCore.Qt.Key_PageDown) ], 'shortcuts/nextService': [QtGui.QKeySequence(QtCore.Qt.Key_Right)], 'shortcuts/newService': [], 'shortcuts/offlineHelpItem': [ QtGui.QKeySequence(QtGui.QKeySequence.HelpContents) ], 'shortcuts/onlineHelpItem': [ QtGui.QKeySequence(QtGui.QKeySequence.HelpContents) ], 'shortcuts/openService': [], 'shortcuts/saveService': [], 'shortcuts/previousItem_live': [ QtGui.QKeySequence(QtCore.Qt.Key_Up), QtGui.QKeySequence(QtCore.Qt.Key_PageUp) ], 'shortcuts/playbackPause': [], 'shortcuts/playbackPlay': [], 'shortcuts/playbackStop': [], 'shortcuts/playSlidesLoop': [], 'shortcuts/playSlidesOnce': [], 'shortcuts/previousService': [ QtGui.QKeySequence(QtCore.Qt.Key_Left) ], 'shortcuts/previousItem_preview': [ QtGui.QKeySequence(QtCore.Qt.Key_Up), QtGui.QKeySequence(QtCore.Qt.Key_PageUp) ], 'shortcuts/printServiceItem': [ QtGui.QKeySequence(QtGui.QKeySequence.Print) ], 'shortcuts/songExportItem': [], 'shortcuts/songUsageStatus': [ QtGui.QKeySequence(QtCore.Qt.Key_F4) ], 'shortcuts/searchShortcut': [ QtGui.QKeySequence(QtGui.QKeySequence.Find) ], 'shortcuts/settingsShortcutsItem': [], 'shortcuts/settingsImportItem': [], 'shortcuts/settingsPluginListItem': [ QtGui.QKeySequence(QtCore.Qt.ALT + QtCore.Qt.Key_F7) ], 'shortcuts/songUsageDelete': [], 'shortcuts/settingsConfigureItem': [ QtGui.QKeySequence(QtGui.QKeySequence.Preferences) ], 'shortcuts/shortcutAction_B': [ QtGui.QKeySequence(QtCore.Qt.Key_B) ], 'shortcuts/shortcutAction_C': [ QtGui.QKeySequence(QtCore.Qt.Key_C) ], 'shortcuts/shortcutAction_E': [ QtGui.QKeySequence(QtCore.Qt.Key_E) ], 'shortcuts/shortcutAction_I': [ QtGui.QKeySequence(QtCore.Qt.Key_I) ], 'shortcuts/shortcutAction_O': [ QtGui.QKeySequence(QtCore.Qt.Key_O) ], 'shortcuts/shortcutAction_P': [ QtGui.QKeySequence(QtCore.Qt.Key_P) ], 'shortcuts/shortcutAction_V': [ QtGui.QKeySequence(QtCore.Qt.Key_V) ], 'shortcuts/shortcutAction_0': [ QtGui.QKeySequence(QtCore.Qt.Key_0) ], 'shortcuts/shortcutAction_1': [ QtGui.QKeySequence(QtCore.Qt.Key_1) ], 'shortcuts/shortcutAction_2': [ QtGui.QKeySequence(QtCore.Qt.Key_2) ], 'shortcuts/shortcutAction_3': [ QtGui.QKeySequence(QtCore.Qt.Key_3) ], 'shortcuts/shortcutAction_4': [ QtGui.QKeySequence(QtCore.Qt.Key_4) ], 'shortcuts/shortcutAction_5': [ QtGui.QKeySequence(QtCore.Qt.Key_5) ], 'shortcuts/shortcutAction_6': [ QtGui.QKeySequence(QtCore.Qt.Key_6) ], 'shortcuts/shortcutAction_7': [ QtGui.QKeySequence(QtCore.Qt.Key_7) ], 'shortcuts/shortcutAction_8': [ QtGui.QKeySequence(QtCore.Qt.Key_8) ], 'shortcuts/shortcutAction_9': [ QtGui.QKeySequence(QtCore.Qt.Key_9) ], 'shortcuts/settingsExportItem': [], 'shortcuts/songUsageReport': [], 'shortcuts/songImportItem': [], 'shortcuts/themeScreen': [QtGui.QKeySequence(QtCore.Qt.Key_T)], 'shortcuts/toolsReindexItem': [], 'shortcuts/toolsFindDuplicates': [], 'shortcuts/toolsAlertItem': [QtGui.QKeySequence(QtCore.Qt.Key_F7)], 'shortcuts/toolsFirstTimeWizard': [], 'shortcuts/toolsOpenDataFolder': [], 'shortcuts/toolsAddToolItem': [], 'shortcuts/updateThemeImages': [], 'shortcuts/up': [QtGui.QKeySequence(QtCore.Qt.Key_Up)], 'shortcuts/viewProjectorManagerItem': [ QtGui.QKeySequence(QtCore.Qt.Key_F6) ], 'shortcuts/viewThemeManagerItem': [ QtGui.QKeySequence(QtCore.Qt.Key_F10) ], 'shortcuts/viewMediaManagerItem': [ QtGui.QKeySequence(QtCore.Qt.Key_F8) ], 'shortcuts/viewPreviewPanel': [ QtGui.QKeySequence(QtCore.Qt.Key_F11) ], 'shortcuts/viewLivePanel': [QtGui.QKeySequence(QtCore.Qt.Key_F12)], 'shortcuts/viewServiceManagerItem': [ QtGui.QKeySequence(QtCore.Qt.Key_F9) ], 'shortcuts/webSiteItem': [] }) def get_default_value(self, key): """ Get the default value of the given key """ if self.group(): key = self.group() + '/' + key return Settings.__default_settings__[key] def remove_obsolete_settings(self): """ This method is only called to clean up the config. It removes old settings and it renames settings. See ``__obsolete_settings__`` for more details. """ for old_key, new_key, rules in Settings.__obsolete_settings__: # Once removed we don't have to do this again. if self.contains(old_key): if new_key: # Get the value of the old_key. old_value = super(Settings, self).value(old_key) # When we want to convert the value, we have to figure out the default value (because we cannot get # the default value from the central settings dict. if rules: default_value = rules[0][1] old_value = self._convert_value( old_value, default_value) # Iterate over our rules and check what the old_value should be "converted" to. for new, old in rules: # If the value matches with the condition (rule), then use the provided value. This is used to # convert values. E. g. an old value 1 results in True, and 0 in False. if callable(new): old_value = new(old_value) elif old == old_value: old_value = new break self.setValue(new_key, old_value) self.remove(old_key) def value(self, key): """ Returns the value for the given ``key``. The returned ``value`` is of the same type as the default value in the *Settings.__default_settings__* dict. :param key: The key to return the value from. """ # if group() is not empty the group has not been specified together with the key. if self.group(): default_value = Settings.__default_settings__[self.group() + '/' + key] else: default_value = Settings.__default_settings__[key] setting = super(Settings, self).value(key, default_value) return self._convert_value(setting, default_value) def _convert_value(self, setting, default_value): """ This converts the given ``setting`` to the type of the given ``default_value``. :param setting: The setting to convert. This could be ``true`` for example.Settings() :param default_value: Indication the type the setting should be converted to. For example ``True`` (type is boolean), meaning that we convert the string ``true`` to a python boolean. **Note**, this method only converts a few types and might need to be extended if a certain type is missing! """ # Handle 'None' type (empty value) properly. if setting is None: # An empty string saved to the settings results in a None type being returned. # Convert it to empty unicode string. if isinstance(default_value, str): return '' # An empty list saved to the settings results in a None type being returned. else: return [] # Convert the setting to the correct type. if isinstance(default_value, bool): if isinstance(setting, bool): return setting # Sometimes setting is string instead of a boolean. return setting == 'true' if isinstance(default_value, int): return int(setting) return setting def get_files_from_config(self, plugin): """ This removes the settings needed for old way we saved files (e. g. the image paths for the image plugin). A list of file paths are returned. **Note**: Only a list of paths is returned; this does not convert anything! :param plugin: The Plugin object.The caller has to convert/save the list himself; o """ files_list = [] # We need QSettings instead of Settings here to bypass our central settings dict. # Do NOT do this anywhere else! settings = QtCore.QSettings(self.fileName(), Settings.IniFormat) settings.beginGroup(plugin.settings_section) if settings.contains('{name} count'.format(name=plugin.name)): # Get the count. list_count = int( settings.value('{name} count'.format(name=plugin.name), 0)) if list_count: for counter in range(list_count): # The keys were named e. g.: "image 0" item = settings.value( '{name} {counter:d}'.format(name=plugin.name, counter=counter), '') if item: files_list.append(item) settings.remove('{name} {counter:d}'.format( name=plugin.name, counter=counter)) settings.remove('{name} count'.format(name=plugin.name)) settings.endGroup() return files_list
# with this program; if not, write to the Free Software Foundation, Inc., 59 # # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### """ This module is for controlling powerpoint. PPT API documentation: `http://msdn.microsoft.com/en-us/library/aa269321(office.10).aspx`_ 2010: https://msdn.microsoft.com/en-us/library/office/ff743835%28v=office.14%29.aspx 2013: https://msdn.microsoft.com/en-us/library/office/ff743835.aspx """ import os import logging import time from openlp.core.common import is_win, Settings if is_win(): from win32com.client import Dispatch import win32con import winreg import win32ui import win32gui import pywintypes from openlp.core.lib import ScreenList from openlp.core.lib.ui import UiStrings, critical_error_message_box, translate from openlp.core.common import trace_error_handler, Registry from .presentationcontroller import PresentationController, PresentationDocument log = logging.getLogger(__name__)
class PowerpointDocument(PresentationDocument): """ Class which holds information and controls a single presentation. """ def __init__(self, controller, presentation): """ Constructor, store information about the file and initialise. :param controller: :param presentation: """ log.debug('Init Presentation Powerpoint') super(PowerpointDocument, self).__init__(controller, presentation) self.presentation = None self.index_map = {} self.slide_count = 0 self.blank_slide = 1 self.blank_click = None self.presentation_hwnd = None def load_presentation(self): """ Called when a presentation is added to the SlideController. Opens the PowerPoint file using the process created earlier. """ log.debug('load_presentation') try: if not self.controller.process: self.controller.start_process() self.controller.process.Presentations.Open(os.path.normpath(self.file_path), False, False, False) self.presentation = self.controller.process.Presentations(self.controller.process.Presentations.Count) self.create_thumbnails() self.create_titles_and_notes() # Make sure powerpoint doesn't steal focus, unless we're on a single screen setup if len(ScreenList().screen_list) > 1: Registry().get('main_window').activateWindow() return True except (AttributeError, pywintypes.com_error) as e: log.exception('Exception caught while loading Powerpoint presentation') log.exception(e) trace_error_handler(log) return False def create_thumbnails(self): """ Create the thumbnail images for the current presentation. Note an alternative and quicker method would be do:: self.presentation.Slides[n].Copy() thumbnail = QApplication.clipboard.image() However, for the moment, we want a physical file since it makes life easier elsewhere. """ log.debug('create_thumbnails') if self.check_thumbnails(): return key = 1 for num in range(self.presentation.Slides.Count): if not self.presentation.Slides(num + 1).SlideShowTransition.Hidden: self.index_map[key] = num + 1 self.presentation.Slides(num + 1).Export( os.path.join(self.get_thumbnail_folder(), 'slide%d.png' % (key)), 'png', 320, 240) key += 1 self.slide_count = key - 1 def close_presentation(self): """ Close presentation and clean up objects. This is triggered by a new object being added to SlideController or OpenLP being shut down. """ log.debug('ClosePresentation') if self.presentation: try: self.presentation.Close() except (AttributeError, pywintypes.com_error) as e: log.exception('Caught exception while closing powerpoint presentation') log.exception(e) trace_error_handler(log) self.presentation = None self.controller.remove_doc(self) # Make sure powerpoint doesn't steal focus, unless we're on a single screen setup if len(ScreenList().screen_list) > 1: Registry().get('main_window').activateWindow() def is_loaded(self): """ Returns ``True`` if a presentation is loaded. """ log.debug('is_loaded') try: if self.controller.process.Presentations.Count == 0: return False except (AttributeError, pywintypes.com_error) as e: log.exception('Caught exception while in is_loaded') log.exception(e) trace_error_handler(log) return False return True def is_active(self): """ Returns ``True`` if a presentation is currently active. """ log.debug('is_active') if not self.is_loaded(): return False try: if self.presentation.SlideShowWindow is None: return False if self.presentation.SlideShowWindow.View is None: return False except (AttributeError, pywintypes.com_error) as e: log.exception('Caught exception while in is_active') log.exception(e) trace_error_handler(log) return False return True def unblank_screen(self): """ Unblanks (restores) the presentation. """ log.debug('unblank_screen') try: self.presentation.SlideShowWindow.Activate() self.presentation.SlideShowWindow.View.State = 1 # Unblanking is broken in PowerPoint 2010 (14.0), need to redisplay if 15.0 > float(self.presentation.Application.Version) >= 14.0: self.presentation.SlideShowWindow.View.GotoSlide(self.index_map[self.blank_slide], False) if self.blank_click: self.presentation.SlideShowWindow.View.GotoClick(self.blank_click) except (AttributeError, pywintypes.com_error) as e: log.exception('Caught exception while in unblank_screen') log.exception(e) trace_error_handler(log) self.show_error_msg() # Stop powerpoint from flashing in the taskbar if self.presentation_hwnd: win32gui.FlashWindowEx(self.presentation_hwnd, win32con.FLASHW_STOP, 0, 0) # Make sure powerpoint doesn't steal focus, unless we're on a single screen setup if len(ScreenList().screen_list) > 1: Registry().get('main_window').activateWindow() def blank_screen(self): """ Blanks the screen. """ log.debug('blank_screen') try: # Unblanking is broken in PowerPoint 2010 (14.0), need to save info for later if 15.0 > float(self.presentation.Application.Version) >= 14.0: self.blank_slide = self.get_slide_number() self.blank_click = self.presentation.SlideShowWindow.View.GetClickIndex() # ppSlideShowBlackScreen = 3 self.presentation.SlideShowWindow.View.State = 3 except (AttributeError, pywintypes.com_error) as e: log.exception('Caught exception while in blank_screen') log.exception(e) trace_error_handler(log) self.show_error_msg() def is_blank(self): """ Returns ``True`` if screen is blank. """ log.debug('is_blank') if self.is_active(): try: # ppSlideShowBlackScreen = 3 return self.presentation.SlideShowWindow.View.State == 3 except (AttributeError, pywintypes.com_error) as e: log.exception('Caught exception while in is_blank') log.exception(e) trace_error_handler(log) self.show_error_msg() else: return False def stop_presentation(self): """ Stops the current presentation and hides the output. Used when blanking to desktop. """ log.debug('stop_presentation') try: self.presentation.SlideShowWindow.View.Exit() except (AttributeError, pywintypes.com_error) as e: log.exception('Caught exception while in stop_presentation') log.exception(e) trace_error_handler(log) self.show_error_msg() if is_win(): def start_presentation(self): """ Starts a presentation from the beginning. """ log.debug('start_presentation') # SlideShowWindow measures its size/position by points, not pixels # https://technet.microsoft.com/en-us/library/dn528846.aspx try: dpi = win32ui.GetActiveWindow().GetDC().GetDeviceCaps(88) except win32ui.error: try: dpi = win32ui.GetForegroundWindow().GetDC().GetDeviceCaps(88) except win32ui.error: dpi = 96 size = ScreenList().current['size'] ppt_window = None try: ppt_window = self.presentation.SlideShowSettings.Run() except (AttributeError, pywintypes.com_error) as e: log.exception('Caught exception while in start_presentation') log.exception(e) trace_error_handler(log) self.show_error_msg() if ppt_window and not Settings().value('presentations/powerpoint control window'): try: ppt_window.Top = size.y() * 72 / dpi ppt_window.Height = size.height() * 72 / dpi ppt_window.Left = size.x() * 72 / dpi ppt_window.Width = size.width() * 72 / dpi except AttributeError as e: log.exception('AttributeError while in start_presentation') log.exception(e) # Find the presentation window and save the handle for later self.presentation_hwnd = None if ppt_window: log.debug('main display size: y=%d, height=%d, x=%d, width=%d' % (size.y(), size.height(), size.x(), size.width())) win32gui.EnumWindows(self._window_enum_callback, size) # Make sure powerpoint doesn't steal focus, unless we're on a single screen setup if len(ScreenList().screen_list) > 1: Registry().get('main_window').activateWindow() def _window_enum_callback(self, hwnd, size): """ Method for callback from win32gui.EnumWindows. Used to find the powerpoint presentation window and stop it flashing in the taskbar. """ # Get the size of the current window and if it matches the size of our main display we assume # it is the powerpoint presentation window. (left, top, right, bottom) = win32gui.GetWindowRect(hwnd) window_title = win32gui.GetWindowText(hwnd) log.debug('window size: left=%d, top=%d, right=%d, width=%d' % (left, top, right, bottom)) log.debug('compare size: %d and %d, %d and %d, %d and %d, %d and %d' % (size.y(), top, size.height(), (bottom - top), size.x(), left, size.width(), (right - left))) log.debug('window title: %s' % window_title) filename_root, filename_ext = os.path.splitext(os.path.basename(self.file_path)) if size.y() == top and size.height() == (bottom - top) and size.x() == left and \ size.width() == (right - left) and filename_root in window_title: log.debug('Found a match and will save the handle') self.presentation_hwnd = hwnd # Stop powerpoint from flashing in the taskbar win32gui.FlashWindowEx(self.presentation_hwnd, win32con.FLASHW_STOP, 0, 0) def get_slide_number(self): """ Returns the current slide number. """ log.debug('get_slide_number') ret = 0 try: # We need 2 approaches to getting the current slide number, because # SlideShowWindow.View.Slide.SlideIndex wont work on the end-slide where Slide isn't available, and # SlideShowWindow.View.CurrentShowPosition returns 0 when called when a transistion is executing (in 2013) # So we use SlideShowWindow.View.Slide.SlideIndex unless the state is done (ppSlideShowDone = 5) if self.presentation.SlideShowWindow.View.State != 5: ret = self.presentation.SlideShowWindow.View.Slide.SlideNumber # Do reverse lookup in the index_map to find the slide number to return ret = next((key for key, slidenum in self.index_map.items() if slidenum == ret), None) else: ret = self.presentation.SlideShowWindow.View.CurrentShowPosition except (AttributeError, pywintypes.com_error) as e: log.exception('Caught exception while in get_slide_number') log.exception(e) trace_error_handler(log) self.show_error_msg() return ret def get_slide_count(self): """ Returns total number of slides. """ log.debug('get_slide_count') return self.slide_count def goto_slide(self, slide_no): """ Moves to a specific slide in the presentation. :param slide_no: The slide the text is required for, starting at 1 """ log.debug('goto_slide') try: if Settings().value('presentations/powerpoint slide click advance') \ and self.get_slide_number() == slide_no: click_index = self.presentation.SlideShowWindow.View.GetClickIndex() click_count = self.presentation.SlideShowWindow.View.GetClickCount() log.debug('We are already on this slide - go to next effect if any left, idx: %d, count: %d' % (click_index, click_count)) if click_index < click_count: self.next_step() else: self.presentation.SlideShowWindow.View.GotoSlide(self.index_map[slide_no]) except (AttributeError, pywintypes.com_error) as e: log.exception('Caught exception while in goto_slide') log.exception(e) trace_error_handler(log) self.show_error_msg() def next_step(self): """ Triggers the next effect of slide on the running presentation. """ log.debug('next_step') try: self.presentation.SlideShowWindow.Activate() self.presentation.SlideShowWindow.View.Next() except (AttributeError, pywintypes.com_error) as e: log.exception('Caught exception while in next_step') log.exception(e) trace_error_handler(log) self.show_error_msg() return if self.get_slide_number() > self.get_slide_count(): log.debug('past end, stepping back to previous') self.previous_step() # Stop powerpoint from flashing in the taskbar if self.presentation_hwnd: win32gui.FlashWindowEx(self.presentation_hwnd, win32con.FLASHW_STOP, 0, 0) # Make sure powerpoint doesn't steal focus, unless we're on a single screen setup if len(ScreenList().screen_list) > 1: Registry().get('main_window').activateWindow() def previous_step(self): """ Triggers the previous slide on the running presentation. """ log.debug('previous_step') try: self.presentation.SlideShowWindow.View.Previous() except (AttributeError, pywintypes.com_error) as e: log.exception('Caught exception while in previous_step') log.exception(e) trace_error_handler(log) self.show_error_msg() def get_slide_text(self, slide_no): """ Returns the text on the slide. :param slide_no: The slide the text is required for, starting at 1 """ return _get_text_from_shapes(self.presentation.Slides(self.index_map[slide_no]).Shapes) def get_slide_notes(self, slide_no): """ Returns the text on the slide. :param slide_no: The slide the text is required for, starting at 1 """ return _get_text_from_shapes(self.presentation.Slides(self.index_map[slide_no]).NotesPage.Shapes) def create_titles_and_notes(self): """ Writes the list of titles (one per slide) to 'titles.txt' and the notes to 'slideNotes[x].txt' in the thumbnails directory """ titles = [] notes = [] for num in range(self.get_slide_count()): slide = self.presentation.Slides(self.index_map[num + 1]) try: text = slide.Shapes.Title.TextFrame.TextRange.Text except Exception as e: log.exception(e) text = '' titles.append(text.replace('\n', ' ').replace('\x0b', ' ') + '\n') note = _get_text_from_shapes(slide.NotesPage.Shapes) if len(note) == 0: note = ' ' notes.append(note) self.save_titles_and_notes(titles, notes) def show_error_msg(self): """ Stop presentation and display an error message. """ try: self.presentation.SlideShowWindow.View.Exit() except (AttributeError, pywintypes.com_error) as e: log.exception('Failed to exit Powerpoint presentation after error') log.exception(e) critical_error_message_box(UiStrings().Error, translate('PresentationPlugin.PowerpointDocument', 'An error occurred in the Powerpoint integration ' 'and the presentation will be stopped. ' 'Restart the presentation if you wish to present it.'))
class Settings(QtCore.QSettings): """ Class to wrap QSettings. * Exposes all the methods of QSettings. * Adds functionality for OpenLP Portable. If the ``defaultFormat`` is set to ``IniFormat``, and the path to the Ini file is set using ``set_filename``, then the Settings constructor (without any arguments) will create a Settings object for accessing settings stored in that Ini file. ``__default_settings__`` This dict contains all core settings with their default values. ``__obsolete_settings__`` Each entry is structured in the following way:: ('general/enable slide loop', 'advanced/slide limits', [(SlideLimits.Wrap, True), (SlideLimits.End, False)]) The first entry is the *old key*; if it is different from the *new key* it will be removed. The second entry is the *new key*; we will add it to the config. If this is just an empty string, we just remove the old key. The last entry is a list containing two-pair tuples. If the list is empty, no conversion is made. If the first value is callable i.e. a function, the function will be called with the old setting's value. Otherwise each pair describes how to convert the old setting's value:: (SlideLimits.Wrap, True) This means, that if the value of ``general/enable slide loop`` is equal (``==``) ``True`` then we set ``advanced/slide limits`` to ``SlideLimits.Wrap``. **NOTE**, this means that the rules have to cover all cases! So, if the type of the old value is bool, then there must be two rules. """ __default_settings__ = { 'settings/version': 0, 'advanced/add page break': False, 'advanced/alternate rows': not is_win(), 'advanced/autoscrolling': {'dist': 1, 'pos': 0}, 'advanced/current media plugin': -1, 'advanced/data path': None, # 7 stands for now, 0 to 6 is Monday to Sunday. 'advanced/default service day': 7, 'advanced/default service enabled': True, 'advanced/default service hour': 11, 'advanced/default service minute': 0, 'advanced/default service name': 'Service %Y-%m-%d %H-%M', 'advanced/display size': 0, 'advanced/double click live': False, 'advanced/enable exit confirmation': True, 'advanced/expand service item': False, 'advanced/hide mouse': True, 'advanced/ignore aspect ratio': False, 'advanced/is portable': False, 'advanced/max recent files': 20, 'advanced/print file meta data': False, 'advanced/print notes': False, 'advanced/print slide text': False, 'advanced/proxy mode': ProxyMode.SYSTEM_PROXY, 'advanced/proxy http': '', 'advanced/proxy https': '', 'advanced/proxy username': '', 'advanced/proxy password': '', 'advanced/recent file count': 4, 'advanced/save current plugin': False, 'advanced/slide limits': SlideLimits.End, 'advanced/slide max height': -4, 'advanced/single click preview': False, 'advanced/single click service preview': False, 'advanced/x11 bypass wm': X11_BYPASS_DEFAULT, 'advanced/search as type': True, 'advanced/use_dark_style': False, 'api/twelve hour': True, 'api/port': 4316, 'api/websocket port': 4317, 'api/user id': 'openlp', 'api/password': '******', 'api/authentication enabled': False, 'api/ip address': '0.0.0.0', 'api/thumbnails': True, 'crashreport/last directory': None, 'formattingTags/html_tags': '', 'core/audio repeat list': False, 'core/auto open': False, 'core/auto preview': False, 'core/audio start paused': True, 'core/auto unblank': False, 'core/click live slide to unblank': False, 'core/blank warning': False, 'core/ccli number': '', 'core/has run wizard': False, 'core/language': '[en]', 'core/last version test': '', 'core/loop delay': 5, 'core/recent files': [], 'core/save prompt': False, 'core/screen blank': False, 'core/show splash': True, 'core/logo background color': '#ffffff', 'core/logo file': Path(':/graphics/openlp-splash-screen.png'), 'core/logo hide on startup': False, 'core/songselect password': '', 'core/songselect username': '', 'core/update check': True, 'core/view mode': 'default', # The other display settings (display position and dimensions) are defined in the ScreenList class due to a # circular dependency. 'core/display on monitor': True, 'core/override position': False, 'core/application version': '0.0', 'images/background color': '#000000', 'media/players': 'system,webkit', 'media/override player': QtCore.Qt.Unchecked, 'remotes/download version': '0.0', 'players/background color': '#000000', 'servicemanager/last directory': None, 'servicemanager/last file': None, 'servicemanager/service theme': None, 'SettingsImport/file_date_created': datetime.datetime.now().strftime("%Y-%m-%d %H:%M"), 'SettingsImport/Make_Changes': 'At_Own_RISK', 'SettingsImport/type': 'OpenLP_settings_export', 'SettingsImport/version': '', 'themes/global theme': '', 'themes/last directory': None, 'themes/last directory export': None, 'themes/last directory import': None, 'themes/theme level': ThemeLevel.Song, 'themes/wrap footer': False, 'user interface/live panel': True, 'user interface/live splitter geometry': QtCore.QByteArray(), 'user interface/lock panel': True, 'user interface/main window geometry': QtCore.QByteArray(), 'user interface/main window position': QtCore.QPoint(0, 0), 'user interface/main window splitter geometry': QtCore.QByteArray(), 'user interface/main window state': QtCore.QByteArray(), 'user interface/preview panel': True, 'user interface/preview splitter geometry': QtCore.QByteArray(), 'user interface/is preset layout': False, 'projector/show after wizard': False, 'projector/db type': 'sqlite', 'projector/db username': '', 'projector/db password': '', 'projector/db hostname': '', 'projector/db database': '', 'projector/enable': True, 'projector/connect on start': False, 'projector/connect when LKUP received': True, # PJLink v2: Projector sends LKUP command after it powers up 'projector/last directory import': None, 'projector/last directory export': None, 'projector/poll time': 20, # PJLink timeout is 30 seconds 'projector/socket timeout': 5, # 5 second socket timeout 'projector/source dialog type': 0 # Source select dialog box type } __file_path__ = '' # Settings upgrades prior to 3.0 __setting_upgrade_1__ = [ ('songs/search as type', 'advanced/search as type', []), ('media/players', 'media/players_temp', [(media_players_conv, None)]), # Convert phonon to system ('media/players_temp', 'media/players', []), # Move temp setting from above to correct setting ] # Settings upgrades for 3.0 (aka 2.6) __setting_upgrade_2__ = [ ('advanced/default color', 'core/logo background color', []), # Default image renamed + moved to general > 2.4. ('advanced/default image', 'core/logo file', []), # Default image renamed + moved to general after 2.4. ('remotes/https enabled', '', []), ('remotes/https port', '', []), ('remotes/twelve hour', 'api/twelve hour', []), ('remotes/port', 'api/port', []), ('remotes/websocket port', 'api/websocket port', []), ('remotes/user id', 'api/user id', []), ('remotes/password', 'api/password', []), ('remotes/authentication enabled', 'api/authentication enabled', []), ('remotes/ip address', 'api/ip address', []), ('remotes/thumbnails', 'api/thumbnails', []), ('shortcuts/escapeItem', 'shortcuts/desktopScreenEnable', []), # Escape item was removed in 2.6. ('shortcuts/offlineHelpItem', 'shortcuts/userManualItem', []), # Online and Offline help were combined in 2.6. ('shortcuts/onlineHelpItem', 'shortcuts/userManualItem', []), # Online and Offline help were combined in 2.6. ('bibles/advanced bible', '', []), # Common bible search widgets combined in 2.6 ('bibles/quick bible', 'bibles/primary bible', []), # Common bible search widgets combined in 2.6 # Last search type was renamed to last used search type in 2.6 since Bible search value type changed in 2.6. ('songs/last search type', 'songs/last used search type', []), ('bibles/last search type', '', []), ('custom/last search type', 'custom/last used search type', []), # The following changes are being made for the conversion to using Path objects made in 2.6 development ('advanced/data path', 'advanced/data path', [(lambda p: Path(p) if p is not None else None, None)]), ('crashreport/last directory', 'crashreport/last directory', [(str_to_path, None)]), ('servicemanager/last directory', 'servicemanager/last directory', [(str_to_path, None)]), ('servicemanager/last file', 'servicemanager/last file', [(str_to_path, None)]), ('themes/last directory', 'themes/last directory', [(str_to_path, None)]), ('themes/last directory export', 'themes/last directory export', [(str_to_path, None)]), ('themes/last directory import', 'themes/last directory import', [(str_to_path, None)]), ('projector/last directory import', 'projector/last directory import', [(str_to_path, None)]), ('projector/last directory export', 'projector/last directory export', [(str_to_path, None)]), ('bibles/last directory import', 'bibles/last directory import', [(str_to_path, None)]), ('presentations/pdf_program', 'presentations/pdf_program', [(str_to_path, None)]), ('songs/last directory import', 'songs/last directory import', [(str_to_path, None)]), ('songs/last directory export', 'songs/last directory export', [(str_to_path, None)]), ('songusage/last directory export', 'songusage/last directory export', [(str_to_path, None)]), ('core/recent files', 'core/recent files', [(files_to_paths, None)]), ('media/media files', 'media/media files', [(files_to_paths, None)]), ('presentations/presentations files', 'presentations/presentations files', [(files_to_paths, None)]), ('core/logo file', 'core/logo file', [(str_to_path, None)]), ('presentations/last directory', 'presentations/last directory', [(str_to_path, None)]), ('images/last directory', 'images/last directory', [(str_to_path, None)]), ('media/last directory', 'media/last directory', [(str_to_path, None)]), ('songuasge/db password', 'songusage/db password', []), ('songuasge/db hostname', 'songusage/db hostname', []), ('songuasge/db database', 'songusage/db database', []), ('presentations / Powerpoint Viewer', '', []) ] @staticmethod def extend_default_settings(default_values): """ Static method to merge the given ``default_values`` with the ``Settings.__default_settings__``. :param default_values: A dict with setting keys and their default values. """ Settings.__default_settings__.update(default_values) @staticmethod def set_filename(ini_path): """ Sets the complete path to an Ini file to be used by Settings objects. Does not affect existing Settings objects. :param openlp.core.common.path.Path ini_path: ini file path :rtype: None """ Settings.__file_path__ = str(ini_path) @staticmethod def set_up_default_values(): """ This static method is called on start up. It is used to perform any operation on the __default_settings__ dict. """ # Make sure the string is translated (when building the dict the string is not translated because the translate # function was not set up as this stage). from openlp.core.common.i18n import UiStrings Settings.__default_settings__['advanced/default service name'] = UiStrings().DefaultServiceName def __init__(self, *args): """ Constructor which checks if this should be a native settings object, or an INI file. """ if not args and Settings.__file_path__ and Settings.defaultFormat() == Settings.IniFormat: QtCore.QSettings.__init__(self, Settings.__file_path__, Settings.IniFormat) else: QtCore.QSettings.__init__(self, *args) # Add shortcuts here so QKeySequence has a QApplication instance to use. Settings.__default_settings__.update({ 'shortcuts/aboutItem': [QtGui.QKeySequence(QtCore.Qt.CTRL + QtCore.Qt.Key_F1)], 'shortcuts/addToService': [], 'shortcuts/audioPauseItem': [], 'shortcuts/displayTagItem': [], 'shortcuts/blankScreen': [QtGui.QKeySequence(QtCore.Qt.Key_Period)], 'shortcuts/collapse': [QtGui.QKeySequence(QtCore.Qt.Key_Minus)], 'shortcuts/desktopScreen': [QtGui.QKeySequence(QtCore.Qt.Key_D)], 'shortcuts/desktopScreenEnable': [QtGui.QKeySequence(QtCore.Qt.Key_Escape)], 'shortcuts/delete': [QtGui.QKeySequence(QtGui.QKeySequence.Delete)], 'shortcuts/down': [QtGui.QKeySequence(QtCore.Qt.Key_Down)], 'shortcuts/editSong': [], 'shortcuts/expand': [QtGui.QKeySequence(QtCore.Qt.Key_Plus)], 'shortcuts/exportThemeItem': [], 'shortcuts/fileNewItem': [QtGui.QKeySequence(QtGui.QKeySequence.New)], 'shortcuts/fileSaveAsItem': [QtGui.QKeySequence(QtGui.QKeySequence.SaveAs)], 'shortcuts/fileExitItem': [QtGui.QKeySequence(QtGui.QKeySequence.Quit)], 'shortcuts/fileSaveItem': [QtGui.QKeySequence(QtGui.QKeySequence.Save)], 'shortcuts/fileOpenItem': [QtGui.QKeySequence(QtGui.QKeySequence.Open)], 'shortcuts/goLive': [], 'shortcuts/userManualItem': [QtGui.QKeySequence(QtGui.QKeySequence.HelpContents)], 'shortcuts/importThemeItem': [], 'shortcuts/importBibleItem': [], 'shortcuts/listViewBiblesDeleteItem': [QtGui.QKeySequence(QtGui.QKeySequence.Delete)], 'shortcuts/listViewBiblesPreviewItem': [QtGui.QKeySequence(QtCore.Qt.Key_Return), QtGui.QKeySequence(QtCore.Qt.Key_Enter)], 'shortcuts/listViewBiblesLiveItem': [QtGui.QKeySequence(QtCore.Qt.SHIFT + QtCore.Qt.Key_Return), QtGui.QKeySequence(QtCore.Qt.SHIFT + QtCore.Qt.Key_Enter)], 'shortcuts/listViewBiblesServiceItem': [QtGui.QKeySequence(QtCore.Qt.Key_Plus), QtGui.QKeySequence(QtCore.Qt.Key_Equal)], 'shortcuts/listViewCustomDeleteItem': [QtGui.QKeySequence(QtGui.QKeySequence.Delete)], 'shortcuts/listViewCustomPreviewItem': [QtGui.QKeySequence(QtCore.Qt.Key_Return), QtGui.QKeySequence(QtCore.Qt.Key_Enter)], 'shortcuts/listViewCustomLiveItem': [QtGui.QKeySequence(QtCore.Qt.SHIFT + QtCore.Qt.Key_Return), QtGui.QKeySequence(QtCore.Qt.SHIFT + QtCore.Qt.Key_Enter)], 'shortcuts/listViewCustomServiceItem': [QtGui.QKeySequence(QtCore.Qt.Key_Plus), QtGui.QKeySequence(QtCore.Qt.Key_Equal)], 'shortcuts/listViewImagesDeleteItem': [QtGui.QKeySequence(QtGui.QKeySequence.Delete)], 'shortcuts/listViewImagesPreviewItem': [QtGui.QKeySequence(QtCore.Qt.Key_Return), QtGui.QKeySequence(QtCore.Qt.Key_Enter)], 'shortcuts/listViewImagesLiveItem': [QtGui.QKeySequence(QtCore.Qt.SHIFT + QtCore.Qt.Key_Return), QtGui.QKeySequence(QtCore.Qt.SHIFT + QtCore.Qt.Key_Enter)], 'shortcuts/listViewImagesServiceItem': [QtGui.QKeySequence(QtCore.Qt.Key_Plus), QtGui.QKeySequence(QtCore.Qt.Key_Equal)], 'shortcuts/listViewMediaDeleteItem': [QtGui.QKeySequence(QtGui.QKeySequence.Delete)], 'shortcuts/listViewMediaPreviewItem': [QtGui.QKeySequence(QtCore.Qt.Key_Return), QtGui.QKeySequence(QtCore.Qt.Key_Enter)], 'shortcuts/listViewMediaLiveItem': [QtGui.QKeySequence(QtCore.Qt.SHIFT + QtCore.Qt.Key_Return), QtGui.QKeySequence(QtCore.Qt.SHIFT + QtCore.Qt.Key_Enter)], 'shortcuts/listViewMediaServiceItem': [QtGui.QKeySequence(QtCore.Qt.Key_Plus), QtGui.QKeySequence(QtCore.Qt.Key_Equal)], 'shortcuts/listViewPresentationsDeleteItem': [QtGui.QKeySequence(QtGui.QKeySequence.Delete)], 'shortcuts/listViewPresentationsPreviewItem': [QtGui.QKeySequence(QtCore.Qt.Key_Return), QtGui.QKeySequence(QtCore.Qt.Key_Enter)], 'shortcuts/listViewPresentationsLiveItem': [QtGui.QKeySequence(QtCore.Qt.SHIFT + QtCore.Qt.Key_Return), QtGui.QKeySequence(QtCore.Qt.SHIFT + QtCore.Qt.Key_Enter)], 'shortcuts/listViewPresentationsServiceItem': [QtGui.QKeySequence(QtCore.Qt.Key_Plus), QtGui.QKeySequence(QtCore.Qt.Key_Equal)], 'shortcuts/listViewSongsDeleteItem': [QtGui.QKeySequence(QtGui.QKeySequence.Delete)], 'shortcuts/listViewSongsPreviewItem': [QtGui.QKeySequence(QtCore.Qt.Key_Return), QtGui.QKeySequence(QtCore.Qt.Key_Enter)], 'shortcuts/listViewSongsLiveItem': [QtGui.QKeySequence(QtCore.Qt.SHIFT + QtCore.Qt.Key_Return), QtGui.QKeySequence(QtCore.Qt.SHIFT + QtCore.Qt.Key_Enter)], 'shortcuts/listViewSongsServiceItem': [QtGui.QKeySequence(QtCore.Qt.Key_Plus), QtGui.QKeySequence(QtCore.Qt.Key_Equal)], 'shortcuts/lockPanel': [], 'shortcuts/modeDefaultItem': [], 'shortcuts/modeLiveItem': [], 'shortcuts/make_live': [QtGui.QKeySequence(QtCore.Qt.Key_Return), QtGui.QKeySequence(QtCore.Qt.Key_Enter)], 'shortcuts/moveUp': [QtGui.QKeySequence(QtCore.Qt.Key_PageUp)], 'shortcuts/moveTop': [QtGui.QKeySequence(QtCore.Qt.Key_Home)], 'shortcuts/modeSetupItem': [], 'shortcuts/moveBottom': [QtGui.QKeySequence(QtCore.Qt.Key_End)], 'shortcuts/moveDown': [QtGui.QKeySequence(QtCore.Qt.Key_PageDown)], 'shortcuts/nextTrackItem': [], 'shortcuts/nextItem_live': [QtGui.QKeySequence(QtCore.Qt.Key_Down), QtGui.QKeySequence(QtCore.Qt.Key_PageDown)], 'shortcuts/nextItem_preview': [QtGui.QKeySequence(QtCore.Qt.Key_Down), QtGui.QKeySequence(QtCore.Qt.Key_PageDown)], 'shortcuts/nextService': [QtGui.QKeySequence(QtCore.Qt.Key_Right)], 'shortcuts/newService': [], 'shortcuts/openService': [], 'shortcuts/saveService': [], 'shortcuts/previousItem_live': [QtGui.QKeySequence(QtCore.Qt.Key_Up), QtGui.QKeySequence(QtCore.Qt.Key_PageUp)], 'shortcuts/playbackPause': [], 'shortcuts/playbackPlay': [], 'shortcuts/playbackStop': [], 'shortcuts/playSlidesLoop': [], 'shortcuts/playSlidesOnce': [], 'shortcuts/previousService': [QtGui.QKeySequence(QtCore.Qt.Key_Left)], 'shortcuts/previousItem_preview': [QtGui.QKeySequence(QtCore.Qt.Key_Up), QtGui.QKeySequence(QtCore.Qt.Key_PageUp)], 'shortcuts/printServiceItem': [QtGui.QKeySequence(QtGui.QKeySequence.Print)], 'shortcuts/songExportItem': [], 'shortcuts/songUsageStatus': [QtGui.QKeySequence(QtCore.Qt.Key_F4)], 'shortcuts/searchShortcut': [QtGui.QKeySequence(QtGui.QKeySequence.Find)], 'shortcuts/settingsShortcutsItem': [], 'shortcuts/settingsImportItem': [], 'shortcuts/settingsPluginListItem': [QtGui.QKeySequence(QtCore.Qt.ALT + QtCore.Qt.Key_F7)], 'shortcuts/songUsageDelete': [], 'shortcuts/settingsConfigureItem': [QtGui.QKeySequence(QtGui.QKeySequence.Preferences)], 'shortcuts/shortcutAction_B': [QtGui.QKeySequence(QtCore.Qt.Key_B)], 'shortcuts/shortcutAction_C': [QtGui.QKeySequence(QtCore.Qt.Key_C)], 'shortcuts/shortcutAction_E': [QtGui.QKeySequence(QtCore.Qt.Key_E)], 'shortcuts/shortcutAction_I': [QtGui.QKeySequence(QtCore.Qt.Key_I)], 'shortcuts/shortcutAction_O': [QtGui.QKeySequence(QtCore.Qt.Key_O)], 'shortcuts/shortcutAction_P': [QtGui.QKeySequence(QtCore.Qt.Key_P)], 'shortcuts/shortcutAction_V': [QtGui.QKeySequence(QtCore.Qt.Key_V)], 'shortcuts/shortcutAction_0': [QtGui.QKeySequence(QtCore.Qt.Key_0)], 'shortcuts/shortcutAction_1': [QtGui.QKeySequence(QtCore.Qt.Key_1)], 'shortcuts/shortcutAction_2': [QtGui.QKeySequence(QtCore.Qt.Key_2)], 'shortcuts/shortcutAction_3': [QtGui.QKeySequence(QtCore.Qt.Key_3)], 'shortcuts/shortcutAction_4': [QtGui.QKeySequence(QtCore.Qt.Key_4)], 'shortcuts/shortcutAction_5': [QtGui.QKeySequence(QtCore.Qt.Key_5)], 'shortcuts/shortcutAction_6': [QtGui.QKeySequence(QtCore.Qt.Key_6)], 'shortcuts/shortcutAction_7': [QtGui.QKeySequence(QtCore.Qt.Key_7)], 'shortcuts/shortcutAction_8': [QtGui.QKeySequence(QtCore.Qt.Key_8)], 'shortcuts/shortcutAction_9': [QtGui.QKeySequence(QtCore.Qt.Key_9)], 'shortcuts/settingsExportItem': [], 'shortcuts/songUsageReport': [], 'shortcuts/songImportItem': [], 'shortcuts/themeScreen': [QtGui.QKeySequence(QtCore.Qt.Key_T)], 'shortcuts/toolsReindexItem': [], 'shortcuts/toolsFindDuplicates': [], 'shortcuts/toolsSongListReport': [], 'shortcuts/toolsAlertItem': [QtGui.QKeySequence(QtCore.Qt.Key_F7)], 'shortcuts/toolsFirstTimeWizard': [], 'shortcuts/toolsOpenDataFolder': [], 'shortcuts/toolsAddToolItem': [], 'shortcuts/updateThemeImages': [], 'shortcuts/up': [QtGui.QKeySequence(QtCore.Qt.Key_Up)], 'shortcuts/viewProjectorManagerItem': [QtGui.QKeySequence(QtCore.Qt.Key_F6)], 'shortcuts/viewThemeManagerItem': [QtGui.QKeySequence(QtCore.Qt.Key_F10)], 'shortcuts/viewMediaManagerItem': [QtGui.QKeySequence(QtCore.Qt.Key_F8)], 'shortcuts/viewPreviewPanel': [QtGui.QKeySequence(QtCore.Qt.Key_F11)], 'shortcuts/viewLivePanel': [QtGui.QKeySequence(QtCore.Qt.Key_F12)], 'shortcuts/viewServiceManagerItem': [QtGui.QKeySequence(QtCore.Qt.Key_F9)], 'shortcuts/webSiteItem': [] }) def get_default_value(self, key): """ Get the default value of the given key """ if self.group(): key = self.group() + '/' + key return Settings.__default_settings__[key] def can_upgrade(self): """ Can / should the settings be upgraded :rtype: bool """ return __version__ != self.value('settings/version') def upgrade_settings(self): """ This method is only called to clean up the config. It removes old settings and it renames settings. See ``__obsolete_settings__`` for more details. """ current_version = self.value('settings/version') for version in range(current_version, __version__): version += 1 upgrade_list = getattr(self, '__setting_upgrade_{version}__'.format(version=version)) for old_keys, new_key, rules in upgrade_list: # Once removed we don't have to do this again. - Can be removed once fully switched to the versioning # system. if not isinstance(old_keys, (tuple, list)): old_keys = [old_keys] if any([not self.contains(old_key) for old_key in old_keys]): log.warning('One of {} does not exist, skipping upgrade'.format(old_keys)) continue if new_key: # Get the value of the old_key. old_values = [super(Settings, self).value(old_key) for old_key in old_keys] # When we want to convert the value, we have to figure out the default value (because we cannot get # the default value from the central settings dict. if rules: default_values = rules[0][1] if not isinstance(default_values, (list, tuple)): default_values = [default_values] old_values = [self._convert_value(old_value, default_value) for old_value, default_value in zip(old_values, default_values)] # Iterate over our rules and check what the old_value should be "converted" to. new_value = None for new_rule, old_rule in rules: # If the value matches with the condition (rule), then use the provided value. This is used to # convert values. E. g. an old value 1 results in True, and 0 in False. if callable(new_rule): new_value = new_rule(*old_values) elif old_rule in old_values: new_value = new_rule break self.setValue(new_key, new_value) [self.remove(old_key) for old_key in old_keys if old_key != new_key] self.setValue('settings/version', version) def value(self, key): """ Returns the value for the given ``key``. The returned ``value`` is of the same type as the default value in the *Settings.__default_settings__* dict. :param str key: The key to return the value from. :return: The value stored by the setting. """ # if group() is not empty the group has not been specified together with the key. if self.group(): default_value = Settings.__default_settings__[self.group() + '/' + key] else: default_value = Settings.__default_settings__[key] setting = super(Settings, self).value(key, default_value) return self._convert_value(setting, default_value) def setValue(self, key, value): """ Reimplement the setValue method to handle Path objects. :param str key: The key of the setting to save :param value: The value to save :rtype: None """ if isinstance(value, Path) or (isinstance(value, list) and value and isinstance(value[0], Path)): value = json.dumps(value, cls=OpenLPJsonEncoder) super().setValue(key, value) def _convert_value(self, setting, default_value): """ This converts the given ``setting`` to the type of the given ``default_value``. :param setting: The setting to convert. This could be ``true`` for example.Settings() :param default_value: Indication the type the setting should be converted to. For example ``True`` (type is boolean), meaning that we convert the string ``true`` to a python boolean. **Note**, this method only converts a few types and might need to be extended if a certain type is missing! """ # Handle 'None' type (empty value) properly. if setting is None: # An empty string saved to the settings results in a None type being returned. # Convert it to empty unicode string. if isinstance(default_value, str): return '' # An empty list saved to the settings results in a None type being returned. elif isinstance(default_value, list): return [] elif isinstance(setting, str): if '__Path__' in setting: return json.loads(setting, cls=OpenLPJsonDecoder) # Convert the setting to the correct type. if isinstance(default_value, bool): if isinstance(setting, bool): return setting # Sometimes setting is string instead of a boolean. return setting == 'true' if isinstance(default_value, int): return int(setting) return setting def export(self, dest_path): """ Export the settings to file. :param openlp.core.common.path.Path dest_path: The file path to create the export file. :return: Success :rtype: bool """ temp_path = Path(gettempdir(), 'openlp', 'exportConf.tmp') # Delete old files if found. if temp_path.exists(): temp_path.unlink() if dest_path.exists(): dest_path.unlink() self.remove('SettingsImport') # Get the settings. keys = self.allKeys() export_settings = QtCore.QSettings(str(temp_path), Settings.IniFormat) # Add a header section. # This is to insure it's our conf file for import. now = datetime.datetime.now() # Write INI format using QSettings. # Write our header. export_settings.beginGroup('SettingsImport') export_settings.setValue('Make_Changes', 'At_Own_RISK') export_settings.setValue('type', 'OpenLP_settings_export') export_settings.setValue('file_date_created', now.strftime("%Y-%m-%d %H:%M")) export_settings.endGroup() # Write all the sections and keys. for section_key in keys: # FIXME: We are conflicting with the standard "General" section. if 'eneral' in section_key: section_key = section_key.lower() key_value = super().value(section_key) if key_value is not None: export_settings.setValue(section_key, key_value) export_settings.sync() # Temp CONF file has been written. Blanks in keys are now '%20'. # Read the temp file and output the user's CONF file with blanks to # make it more readable. try: with dest_path.open('w') as export_conf_file, temp_path.open('r') as temp_conf: for file_record in temp_conf: # Get rid of any invalid entries. if file_record.find('@Invalid()') == -1: file_record = file_record.replace('%20', ' ') export_conf_file.write(file_record) finally: temp_path.unlink()
def main(args=None): """ The main function which parses command line options and then runs :param args: Some args """ args = parse_options(args) qt_args = [] if args and args.loglevel.lower() in ['d', 'debug']: log.setLevel(logging.DEBUG) elif args and args.loglevel.lower() in ['w', 'warning']: log.setLevel(logging.WARNING) else: log.setLevel(logging.INFO) if args and args.style: qt_args.extend(['-style', args.style]) # Throw the rest of the arguments at Qt, just in case. qt_args.extend(args.rargs) # Bug #1018855: Set the WM_CLASS property in X11 if not is_win() and not is_macosx(): qt_args.append('OpenLP') # Initialise the resources qInitResources() # Now create and actually run the application. application = OpenLP(qt_args) application.setOrganizationName('OpenLP') application.setOrganizationDomain('openlp.org') application.setAttribute(QtCore.Qt.AA_UseHighDpiPixmaps, True) if args and args.portable: application.setApplicationName('OpenLPPortable') Settings.setDefaultFormat(Settings.IniFormat) # Get location OpenLPPortable.ini application_path = AppLocation.get_directory(AppLocation.AppDir) set_up_logging( os.path.abspath(os.path.join(application_path, '..', '..', 'Other'))) log.info('Running portable') portable_settings_file = os.path.abspath( os.path.join(application_path, '..', '..', 'Data', 'OpenLP.ini')) # Make this our settings file log.info('INI file: {name}'.format(name=portable_settings_file)) Settings.set_filename(portable_settings_file) portable_settings = Settings() # Set our data path data_path = os.path.abspath( os.path.join( application_path, '..', '..', 'Data', )) log.info('Data path: {name}'.format(name=data_path)) # Point to our data path portable_settings.setValue('advanced/data path', data_path) portable_settings.setValue('advanced/is portable', True) portable_settings.sync() else: application.setApplicationName('OpenLP') set_up_logging(AppLocation.get_directory(AppLocation.CacheDir)) Registry.create() Registry().register('application', application) application.setApplicationVersion(get_application_version()['version']) # Instance check if application.is_already_running(): sys.exit() # Remove/convert obsolete settings. Settings().remove_obsolete_settings() # First time checks in settings if not Settings().value('core/has run wizard'): if not FirstTimeLanguageForm().exec(): # if cancel then stop processing sys.exit() # i18n Set Language language = LanguageManager.get_language() application_translator, default_translator = LanguageManager.get_translator( language) if not application_translator.isEmpty(): application.installTranslator(application_translator) if not default_translator.isEmpty(): application.installTranslator(default_translator) else: log.debug('Could not find default_translator.') if args and not args.no_error_form: sys.excepthook = application.hook_exception sys.exit(application.run(qt_args))
def run(self, args): """ Run the OpenLP application. :param args: Some Args """ self.is_event_loop_active = False # On Windows, the args passed into the constructor are ignored. Not very handy, so set the ones we want to use. # On Linux and FreeBSD, in order to set the WM_CLASS property for X11, we pass "OpenLP" in as a command line # argument. This interferes with files being passed in as command line arguments, so we remove it from the list. if 'OpenLP' in args: args.remove('OpenLP') self.args.extend(args) # Decide how many screens we have and their size screens = ScreenList.create(self.desktop()) # First time checks in settings has_run_wizard = Settings().value('core/has run wizard') if not has_run_wizard: ftw = FirstTimeForm() ftw.initialize(screens) if ftw.exec() == QtWidgets.QDialog.Accepted: Settings().setValue('core/has run wizard', True) elif ftw.was_cancelled: QtCore.QCoreApplication.exit() sys.exit() # Correct stylesheet bugs application_stylesheet = '' if not Settings().value('advanced/alternate rows'): base_color = self.palette().color(QtGui.QPalette.Active, QtGui.QPalette.Base) alternate_rows_repair_stylesheet = \ 'QTableWidget, QListWidget, QTreeWidget {alternate-background-color: ' + base_color.name() + ';}\n' application_stylesheet += alternate_rows_repair_stylesheet if is_win(): application_stylesheet += WIN_REPAIR_STYLESHEET if application_stylesheet: self.setStyleSheet(application_stylesheet) show_splash = Settings().value('core/show splash') if show_splash: self.splash = SplashScreen() self.splash.show() # make sure Qt really display the splash screen self.processEvents() # Check if OpenLP has been upgrade and if a backup of data should be created self.backup_on_upgrade(has_run_wizard) # start the main app window self.main_window = MainWindow() Registry().execute('bootstrap_initialise') Registry().execute('bootstrap_post_set_up') Registry().initialise = False self.main_window.show() if show_splash: # now kill the splashscreen self.splash.finish(self.main_window) log.debug('Splashscreen closed') # make sure Qt really display the splash screen self.processEvents() self.main_window.repaint() self.processEvents() if not has_run_wizard: self.main_window.first_time() # update_check = Settings().value('core/update check') # if update_check: # version = VersionThread(self.main_window) # version.start() self.main_window.is_display_blank() self.main_window.app_startup() return self.exec()