def execute(self, event, *args, **kwargs): """ Execute all the handlers associated with the event and return an array of results. :param event: The function to be processed :param args: Parameters to be passed to the function. :param kwargs: Parameters to be passed to the function. """ results = [] if event in self.functions_list: for function in self.functions_list[event]: try: result = function(*args, **kwargs) if result: results.append(result) except TypeError: # Who has called me can help in debugging trace_error_handler(log) log.exception('Exception for function {function}'.format( function=function)) else: if log.getEffectiveLevel() == logging.DEBUG: trace_error_handler(log) log.exception('Event {event} called but not registered'.format( event=event)) return results
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 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): log.exception('Caught exception while in unblank_screen') 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()) > 1: Registry().get('main_window').activateWindow()
def next_step(self): """ Triggers the next effect of slide on the running presentation. """ log.debug('next_step') # if we are at the presentations end don't go further, just return True if self.presentation.SlideShowWindow.View.GetClickCount() == \ self.presentation.SlideShowWindow.View.GetClickIndex() \ and self.get_slide_number() == self.get_slide_count(): return True past_end = False try: self.presentation.SlideShowWindow.Activate() self.presentation.SlideShowWindow.View.Next() except (AttributeError, pywintypes.com_error): log.exception('Caught exception while in next_step') trace_error_handler(log) self.show_error_msg() return past_end # If for some reason the presentation end was not detected above, this will catch it. if self.get_slide_number() > self.get_slide_count(): log.debug('past end, stepping back to previous') self.previous_step() past_end = True # 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()) > 1: Registry().get('main_window').activateWindow() return past_end
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 Registry().get('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: ' '{index:d}, count: {count:d}'.format(index=click_index, count=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): log.exception('Caught exception while in goto_slide') trace_error_handler(log) self.show_error_msg()
def _pre_wizard(self): """ Prepare the UI for the process. """ self.max_progress = 0 self.finish_button.setVisible(False) self.application.process_events() try: # Loop through the songs list and increase for each selected item for i in range(self.songs_list_widget.count()): self.application.process_events() item = self.songs_list_widget.item(i) if item.checkState() == QtCore.Qt.Checked: filename, sha256 = item.data(QtCore.Qt.UserRole) size = self._get_file_size('%s%s' % (self.songs_url, filename)) self.max_progress += size # Loop through the Bibles list and increase for each selected item iterator = QtWidgets.QTreeWidgetItemIterator(self.bibles_tree_widget) while iterator.value(): self.application.process_events() item = iterator.value() if item.parent() and item.checkState(0) == QtCore.Qt.Checked: filename, sha256 = item.data(0, QtCore.Qt.UserRole) size = self._get_file_size('%s%s' % (self.bibles_url, filename)) self.max_progress += size iterator += 1 # Loop through the themes list and increase for each selected item for i in range(self.themes_list_widget.count()): self.application.process_events() item = self.themes_list_widget.item(i) if item.checkState() == QtCore.Qt.Checked: filename, sha256 = item.data(QtCore.Qt.UserRole) size = self._get_file_size('%s%s' % (self.themes_url, filename)) self.max_progress += size except urllib.error.URLError: trace_error_handler(log) critical_error_message_box(translate('OpenLP.FirstTimeWizard', 'Download Error'), translate('OpenLP.FirstTimeWizard', 'There was a connection problem during ' 'download, so further downloads will be skipped. Try to re-run the ' 'First Time Wizard later.')) self.max_progress = 0 self.web_access = None if self.max_progress: # Add on 2 for plugins status setting plus a "finished" point. self.max_progress += 2 self.progress_bar.setValue(0) self.progress_bar.setMinimum(0) self.progress_bar.setMaximum(self.max_progress) self.progress_page.setTitle(translate('OpenLP.FirstTimeWizard', 'Setting Up And Downloading')) self.progress_page.setSubTitle( translate('OpenLP.FirstTimeWizard', 'Please wait while OpenLP is set up and your data is downloaded.')) else: self.progress_bar.setVisible(False) self.progress_page.setTitle(translate('OpenLP.FirstTimeWizard', 'Setting Up')) self.progress_page.setSubTitle('Setup complete.') self.repaint() self.application.process_events() # Try to give the wizard a chance to repaint itself time.sleep(0.1)
def _pre_wizard(self): """ Prepare the UI for the process. """ self.max_progress = 0 self.finish_button.setVisible(False) self.application.process_events() try: # Loop through the songs list and increase for each selected item for i in range(self.songs_list_widget.count()): self.application.process_events() item = self.songs_list_widget.item(i) if item.checkState() == QtCore.Qt.Checked: filename, sha256 = item.data(QtCore.Qt.UserRole) size = self._get_file_size('%s%s' % (self.songs_url, filename)) self.max_progress += size # Loop through the Bibles list and increase for each selected item iterator = QtGui.QTreeWidgetItemIterator(self.bibles_tree_widget) while iterator.value(): self.application.process_events() item = iterator.value() if item.parent() and item.checkState(0) == QtCore.Qt.Checked: filename, sha256 = item.data(0, QtCore.Qt.UserRole) size = self._get_file_size('%s%s' % (self.bibles_url, filename)) self.max_progress += size iterator += 1 # Loop through the themes list and increase for each selected item for i in range(self.themes_list_widget.count()): self.application.process_events() item = self.themes_list_widget.item(i) if item.checkState() == QtCore.Qt.Checked: filename, sha256 = item.data(QtCore.Qt.UserRole) size = self._get_file_size('%s%s' % (self.themes_url, filename)) self.max_progress += size except urllib.error.URLError: trace_error_handler(log) critical_error_message_box(translate('OpenLP.FirstTimeWizard', 'Download Error'), translate('OpenLP.FirstTimeWizard', 'There was a connection problem during ' 'download, so further downloads will be skipped. Try to re-run the ' 'First Time Wizard later.')) self.max_progress = 0 self.web_access = None if self.max_progress: # Add on 2 for plugins status setting plus a "finished" point. self.max_progress += 2 self.progress_bar.setValue(0) self.progress_bar.setMinimum(0) self.progress_bar.setMaximum(self.max_progress) self.progress_page.setTitle(translate('OpenLP.FirstTimeWizard', 'Setting Up And Downloading')) self.progress_page.setSubTitle( translate('OpenLP.FirstTimeWizard', 'Please wait while OpenLP is set up and your data is downloaded.')) else: self.progress_bar.setVisible(False) self.progress_page.setTitle(translate('OpenLP.FirstTimeWizard', 'Setting Up')) self.progress_page.setSubTitle('Setup complete.') self.repaint() self.application.process_events() # Try to give the wizard a chance to repaint itself time.sleep(0.1)
def download_file(update_object, url, file_path, sha256=None, proxy=None): """" Download a file given a URL. The file is retrieved in chunks, giving the ability to cancel the download at any point. Returns False on download error. :param update_object: the object which needs to be updated :param url: URL to download :param file_path: Destination file :param sha256: The check sum value to be checked against the download value :param dict | ProxyMode | None proxy: ProxyMode enum or a dictionary containing the proxy servers, with their types as the key e.g. {'http': 'http://proxyserver:port', 'https': 'https://proxyserver:port'} """ block_count = 0 block_size = 4096 retries = 0 if not isinstance(proxy, dict): proxy = get_proxy_settings(mode=proxy) log.debug('url_get_file: %s', url) while retries < CONNECTION_RETRIES: try: with file_path.open('wb') as saved_file: response = requests.get(url, proxies=proxy, timeout=float(CONNECTION_TIMEOUT), stream=True) if sha256: hasher = hashlib.sha256() # Download until finished or canceled. for chunk in response.iter_content(chunk_size=block_size): if hasattr(update_object, 'was_cancelled') and update_object.was_cancelled: break saved_file.write(chunk) if sha256: hasher.update(chunk) block_count += 1 if hasattr(update_object, 'update_progress'): update_object.update_progress(block_count, block_size) response.close() if sha256 and hasher.hexdigest() != sha256: log.error('sha256 sums did not match for file %s, got %s, expected %s', file_path, hasher.hexdigest(), sha256) if file_path.exists(): file_path.unlink() return False break except OSError: trace_error_handler(log) if retries > CONNECTION_RETRIES: if file_path.exists(): file_path.unlink() return False else: retries += 1 time.sleep(0.1) continue if hasattr(update_object, 'was_cancelled') and update_object.was_cancelled and file_path.exists(): file_path.unlink() return True
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): log.exception('Caught exception while in stop_presentation') trace_error_handler(log) self.show_error_msg()
def get_flag(self, key): """ Extracts the working_flag value from the list based on the key passed in :param key: The flag to be retrieved. """ if key in self.working_flags: return self.working_flags[key] else: trace_error_handler(log) log.error('Working Flag {key} not found in list'.format(key=key)) raise KeyError('Working Flag {key} not found in list'.format(key=key))
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 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()
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.display_geometry ppt_window = None try: # Disable the presentation console self.presentation.SlideShowSettings.ShowPresenterView = 0 # Start the presentation ppt_window = self.presentation.SlideShowSettings.Run() except (AttributeError, pywintypes.com_error): log.exception('Caught exception while in start_presentation') trace_error_handler(log) self.show_error_msg() if ppt_window and not Registry().get('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: log.exception('AttributeError while in start_presentation') # Find the presentation window and save the handle for later self.presentation_hwnd = None if ppt_window: log.debug('main display size: y={y:d}, height={height:d}, ' 'x={x:d}, width={width:d}'.format( y=size.y(), height=size.height(), x=size.x(), width=size.width())) try: win32gui.EnumWindows(self._window_enum_callback, size) except pywintypes.error: # When _window_enum_callback returns False to stop the enumeration (looping over open windows) # it causes an exception that is ignored here pass # Make sure powerpoint doesn't steal focus, unless we're on a single screen setup if len(ScreenList()) > 1: Registry().get('main_window').activateWindow()
def register(self, key, reference): """ Registers a component against a key. :param key: The service to be created this is usually a major class like "renderer" or "main_window" . :param reference: The service address to be saved. """ if key in self.service_list: trace_error_handler(log) log.error('Duplicate service exception {key}'.format(key=key)) raise KeyError('Duplicate service exception {key}'.format(key=key)) else: self.service_list[key] = reference
def get(self, key): """ Extracts the registry value from the list based on the key passed in :param key: The service to be retrieved. """ if key in self.service_list: return self.service_list[key] else: if not self.initialising: trace_error_handler(log) log.error('Service {key} not found in list'.format(key=key)) raise KeyError('Service {key} not found in list'.format(key=key))
def get(self, key): """ Extracts the registry value from the list based on the key passed in :param key: The service to be retrieved. """ if key in self.service_list: return self.service_list[key] else: if not self.initialising: trace_error_handler(log) log.error('Service %s not found in list' % key) raise KeyError('Service %s not found in list' % key)
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): log.exception('Caught exception while in is_loaded') trace_error_handler(log) return False return True
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 url_get_file(callback, url, f_path, sha256=None): """" Download a file given a URL. The file is retrieved in chunks, giving the ability to cancel the download at any point. Returns False on download error. :param callback: the class which needs to be updated :param url: URL to download :param f_path: Destination file :param sha256: The check sum value to be checked against the download value """ block_count = 0 block_size = 4096 retries = 0 while True: try: filename = open(f_path, "wb") url_file = urllib.request.urlopen(url, timeout=CONNECTION_TIMEOUT) if sha256: hasher = hashlib.sha256() # Download until finished or canceled. while not callback.was_cancelled: data = url_file.read(block_size) if not data: break filename.write(data) if sha256: hasher.update(data) block_count += 1 callback._download_progress(block_count, block_size) filename.close() if sha256 and hasher.hexdigest() != sha256: log.error('sha256 sums did not match for file: {file}'.format(file=f_path)) os.remove(f_path) return False except (urllib.error.URLError, socket.timeout) as err: trace_error_handler(log) filename.close() os.remove(f_path) if retries > CONNECTION_RETRIES: return False else: retries += 1 time.sleep(0.1) continue break # Delete file if cancelled, it may be a partial file. if callback.was_cancelled: os.remove(f_path) return True
def url_get_file(self, url, f_path, sha256=None): """" Download a file given a URL. The file is retrieved in chunks, giving the ability to cancel the download at any point. Returns False on download error. :param url: URL to download :param f_path: Destination file """ block_count = 0 block_size = 4096 retries = 0 while True: try: filename = open(f_path, "wb") url_file = urllib.request.urlopen(url, timeout=CONNECTION_TIMEOUT) if sha256: hasher = hashlib.sha256() # Download until finished or canceled. while not self.was_cancelled: data = url_file.read(block_size) if not data: break filename.write(data) if sha256: hasher.update(data) block_count += 1 self._download_progress(block_count, block_size) filename.close() if sha256 and hasher.hexdigest() != sha256: log.error('sha256 sums did not match for file: {}'.format( f_path)) os.remove(f_path) return False except (urllib.error.URLError, socket.timeout) as err: trace_error_handler(log) filename.close() os.remove(f_path) if retries > CONNECTION_RETRIES: return False else: retries += 1 time.sleep(0.1) continue break # Delete file if cancelled, it may be a partial file. if self.was_cancelled: os.remove(f_path) return True
def test_trace_error_handler(self): """ Test the trace_error_handler() method """ # GIVEN: Mocked out objects with patch('openlp.core.common.traceback') as mocked_traceback: mocked_traceback.extract_stack.return_value = [('openlp.fake', 56, None, 'trace_error_handler_test')] mocked_logger = MagicMock() # WHEN: trace_error_handler() is called trace_error_handler(mocked_logger) # THEN: The mocked_logger.error() method should have been called with the correct parameters mocked_logger.error.assert_called_with( 'OpenLP Error trace\n File openlp.fake at line 56 \n\t called trace_error_handler_test')
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): log.exception('Caught exception while in is_blank') trace_error_handler(log) self.show_error_msg() else: return False
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 previous_step(self): """ Triggers the previous slide on the running presentation. """ log.debug('previous_step') # if we are at the presentations start we can't go further back, just return True if self.presentation.SlideShowWindow.View.GetClickIndex( ) == 0 and self.get_slide_number() == 1: return True try: self.presentation.SlideShowWindow.View.Previous() except (AttributeError, pywintypes.com_error): log.exception('Caught exception while in previous_step') trace_error_handler(log) self.show_error_msg() return False
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): log.exception('Caught exception while in blank_screen') trace_error_handler(log) self.show_error_msg()
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): log.exception('Caught exception while in is_active') trace_error_handler(log) return False return True
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 trace_error_handler_test(self): """ Test the trace_error_handler() method """ # GIVEN: Mocked out objects with patch('openlp.core.common.traceback') as mocked_traceback: mocked_traceback.extract_stack.return_value = [ ('openlp.fake', 56, None, 'trace_error_handler_test') ] mocked_logger = MagicMock() # WHEN: trace_error_handler() is called trace_error_handler(mocked_logger) # THEN: The mocked_logger.error() method should have been called with the correct parameters mocked_logger.error.assert_called_with( 'OpenLP Error trace\n File openlp.fake at line 56 \n\t called trace_error_handler_test' )
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('close_presentation') if self.presentation: try: self.presentation.Close() except (AttributeError, pywintypes.com_error): log.exception( 'Caught exception while closing powerpoint presentation') 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()) > 1: Registry().get('main_window').activateWindow()
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): log.exception( 'Exception caught while killing powerpoint process') trace_error_handler(log) self.process = None
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 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 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 check_binary(program_path): """ Function that checks whether a binary is either ghostscript or mudraw or neither. Is also used from presentationtab.py :param program_path:The full path to the binary to check. :return: Type of the binary, 'gs' if ghostscript, 'mudraw' if mudraw, None if invalid. """ program_type = None runlog = '' log.debug('testing program_path: %s', program_path) try: # Setup startupinfo options for check_output to avoid console popping up on windows if is_win(): startupinfo = STARTUPINFO() startupinfo.dwFlags |= STARTF_USESHOWWINDOW else: startupinfo = None runlog = check_output([program_path, '--help'], stderr=STDOUT, startupinfo=startupinfo) except CalledProcessError as e: runlog = e.output except Exception: trace_error_handler(log) runlog = '' log.debug('check_output returned: %s' % runlog) # Analyse the output to see it the program is mudraw, ghostscript or neither for line in runlog.splitlines(): decoded_line = line.decode() found_mudraw = re.search('usage: mudraw.*', decoded_line, re.IGNORECASE) if found_mudraw: program_type = 'mudraw' break found_mutool = re.search('usage: mutool.*', decoded_line, re.IGNORECASE) if found_mutool: # Test that mutool contains mudraw if re.search('draw\s+--\s+convert document.*', runlog.decode(), re.IGNORECASE | re.MULTILINE): program_type = 'mutool' break found_gs = re.search('GPL Ghostscript.*', decoded_line, re.IGNORECASE) if found_gs: program_type = 'gs' break log.debug('in check_binary, found: %s', program_type) return program_type
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 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 toggle_presentation_screen(self, set_visible): """ Enable or disable the Presentation Screen/Console :param bool set_visible: Should the presentation screen/console be set to be visible. :rtype: None """ # Create Instance of ConfigurationProvider if not self.conf_provider: if is_win(): self.conf_provider = self.manager.createInstance( 'com.sun.star.configuration.ConfigurationProvider') else: self.conf_provider = self.manager.createInstanceWithContext( 'com.sun.star.configuration.ConfigurationProvider', uno.getComponentContext()) # Setup lookup properties to get Impress settings properties = (self.create_property('nodepath', 'org.openoffice.Office.Impress'), ) try: # Get an updateable configuration view impress_conf_props = self.conf_provider.createInstanceWithArguments( 'com.sun.star.configuration.ConfigurationUpdateAccess', properties) # Get the specific setting for presentation screen presenter_screen_enabled = impress_conf_props.getHierarchicalPropertyValue( 'Misc/Start/EnablePresenterScreen') # If the presentation screen is enabled we disable it if presenter_screen_enabled != set_visible: impress_conf_props.setHierarchicalPropertyValue( 'Misc/Start/EnablePresenterScreen', set_visible) impress_conf_props.commitChanges() # if set_visible is False this is an attempt to disable the Presenter Screen # so we make a note that it has been disabled, so it can be enabled again on close. if set_visible is False: self.presenter_screen_disabled_by_openlp = True except Exception as e: log.exception(e) trace_error_handler(log)
def check_binary(program_path): """ Function that checks whether a binary is either ghostscript or mudraw or neither. Is also used from presentationtab.py :param program_path:The full path to the binary to check. :return: Type of the binary, 'gs' if ghostscript, 'mudraw' if mudraw, None if invalid. """ program_type = None runlog = '' log.debug('testing program_path: %s', program_path) try: # Setup startupinfo options for check_output to avoid console popping up on windows if is_win(): startupinfo = STARTUPINFO() startupinfo.dwFlags |= STARTF_USESHOWWINDOW else: startupinfo = None runlog = check_output([program_path, '--help'], stderr=STDOUT, startupinfo=startupinfo) except CalledProcessError as e: runlog = e.output except Exception: trace_error_handler(log) runlog = '' log.debug('check_output returned: %s' % runlog) # Analyse the output to see it the program is mudraw, ghostscript or neither for line in runlog.splitlines(): decoded_line = line.decode() found_mudraw = re.search('usage: mudraw.*', decoded_line, re.IGNORECASE) if found_mudraw: program_type = 'mudraw' break found_gs = re.search('GPL Ghostscript.*', decoded_line, re.IGNORECASE) if found_gs: program_type = 'gs' break log.debug('in check_binary, found: %s', program_type) return program_type
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 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 execute(self, event, *args, **kwargs): """ Execute all the handlers associated with the event and return an array of results. :param event: The function to be processed :param args: Parameters to be passed to the function. :param kwargs: Parameters to be passed to the function. """ results = [] if event in self.functions_list: for function in self.functions_list[event]: try: result = function(*args, **kwargs) if result: results.append(result) except TypeError: # Who has called me can help in debugging trace_error_handler(log) log.exception('Exception for function %s', function) else: trace_error_handler(log) log.error("Event %s called but not registered" % event) return results
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 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 transition 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): log.exception('Caught exception while in get_slide_number') trace_error_handler(log) self.show_error_msg() return ret
def execute(self, event, *args, **kwargs): """ Execute all the handlers associated with the event and return an array of results. :param event: The function to be processed :param args: Parameters to be passed to the function. :param kwargs: Parameters to be passed to the function. """ results = [] if event in self.functions_list: for function in self.functions_list[event]: try: result = function(*args, **kwargs) if result: results.append(result) except TypeError: # Who has called me can help in debugging trace_error_handler(log) log.exception('Exception for function {function}'.format(function=function)) else: trace_error_handler(log) log.error("Event {event} called but not registered".format(event=event)) return results
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 do_import(self, bible_name=None): """ Loads a Bible from file. """ log.debug('Starting OpenSong import from "%s"' % self.filename) if not isinstance(self.filename, str): self.filename = str(self.filename, 'utf8') import_file = None success = True try: # NOTE: We don't need to do any of the normal encoding detection here, because lxml does it's own encoding # detection, and the two mechanisms together interfere with each other. import_file = open(self.filename, 'rb') opensong = objectify.parse(import_file) bible = opensong.getroot() # Check that we're not trying to import a Zefania XML bible, it is sometimes refered to as 'OpenSong' if bible.tag.upper() == 'XMLBIBLE': critical_error_message_box( message=translate('BiblesPlugin.OpenSongImport', 'Incorrect Bible file type supplied. This looks like a Zefania XML bible, ' 'please use the Zefania import option.')) return False # No language info in the opensong format, so ask the user language_id = self.get_language(bible_name) if not language_id: log.error('Importing books from "%s" failed' % self.filename) return False for book in bible.b: if self.stop_import_flag: break book_ref_id = self.get_book_ref_id_by_name(str(book.attrib['n']), len(bible.b), language_id) if not book_ref_id: log.error('Importing books from "%s" failed' % self.filename) return False book_details = BiblesResourcesDB.get_book_by_id(book_ref_id) db_book = self.create_book(book.attrib['n'], book_ref_id, book_details['testament_id']) chapter_number = 0 for chapter in book.c: if self.stop_import_flag: break number = chapter.attrib['n'] if number: chapter_number = int(number.split()[-1]) else: chapter_number += 1 verse_number = 0 for verse in chapter.v: if self.stop_import_flag: break number = verse.attrib['n'] if number: try: number = int(number) except ValueError: verse_parts = number.split('-') if len(verse_parts) > 1: number = int(verse_parts[0]) except TypeError: log.warning('Illegal verse number: %s', str(verse.attrib['n'])) verse_number = number else: verse_number += 1 self.create_verse(db_book.id, chapter_number, verse_number, self.get_text(verse)) self.wizard.increment_progress_bar( translate('BiblesPlugin.Opensong', 'Importing %(bookname)s %(chapter)s...') % {'bookname': db_book.name, 'chapter': chapter_number}) self.session.commit() self.application.process_events() except etree.XMLSyntaxError as inst: trace_error_handler(log) critical_error_message_box( message=translate('BiblesPlugin.OpenSongImport', 'Incorrect Bible file type supplied. OpenSong Bibles may be ' 'compressed. You must decompress them before import.')) log.exception(inst) success = False except (IOError, AttributeError): log.exception('Loading Bible from OpenSong file failed') success = False finally: if import_file: import_file.close() if self.stop_import_flag: return False else: return success
def do_import(self, bible_name=None): """ Loads a Bible from file. """ log.debug('Starting OSIS import from "%s"' % self.filename) if not isinstance(self.filename, str): self.filename = str(self.filename, 'utf8') import_file = None success = True try: # NOTE: We don't need to do any of the normal encoding detection here, because lxml does it's own encoding # detection, and the two mechanisms together interfere with each other. import_file = open(self.filename, 'rb') osis_bible_tree = etree.parse(import_file, parser=etree.XMLParser(recover=True)) namespace = {'ns': 'http://www.bibletechnologies.net/2003/OSIS/namespace'} # Find bible language language_id = None language = osis_bible_tree.xpath("//ns:osisText/@xml:lang", namespaces=namespace) if language: language_id = BiblesResourcesDB.get_language(language[0]) # The language couldn't be detected, ask the user if not language_id: language_id = self.get_language(bible_name) if not language_id: log.error('Importing books from "%s" failed' % self.filename) return False self.save_meta('language_id', language_id) num_books = int(osis_bible_tree.xpath("count(//ns:div[@type='book'])", namespaces=namespace)) self.wizard.increment_progress_bar(translate('BiblesPlugin.OsisImport', 'Removing unused tags (this may take a few minutes)...')) # We strip unused tags from the XML, this should leave us with only chapter, verse and div tags. # Strip tags we don't use - remove content etree.strip_elements(osis_bible_tree, ('{http://www.bibletechnologies.net/2003/OSIS/namespace}note', '{http://www.bibletechnologies.net/2003/OSIS/namespace}milestone', '{http://www.bibletechnologies.net/2003/OSIS/namespace}title', '{http://www.bibletechnologies.net/2003/OSIS/namespace}abbr', '{http://www.bibletechnologies.net/2003/OSIS/namespace}catchWord', '{http://www.bibletechnologies.net/2003/OSIS/namespace}index', '{http://www.bibletechnologies.net/2003/OSIS/namespace}rdg', '{http://www.bibletechnologies.net/2003/OSIS/namespace}rdgGroup', '{http://www.bibletechnologies.net/2003/OSIS/namespace}figure'), with_tail=False) # Strip tags we don't use - keep content etree.strip_tags(osis_bible_tree, ('{http://www.bibletechnologies.net/2003/OSIS/namespace}p', '{http://www.bibletechnologies.net/2003/OSIS/namespace}l', '{http://www.bibletechnologies.net/2003/OSIS/namespace}lg', '{http://www.bibletechnologies.net/2003/OSIS/namespace}q', '{http://www.bibletechnologies.net/2003/OSIS/namespace}a', '{http://www.bibletechnologies.net/2003/OSIS/namespace}w', '{http://www.bibletechnologies.net/2003/OSIS/namespace}divineName', '{http://www.bibletechnologies.net/2003/OSIS/namespace}foreign', '{http://www.bibletechnologies.net/2003/OSIS/namespace}hi', '{http://www.bibletechnologies.net/2003/OSIS/namespace}inscription', '{http://www.bibletechnologies.net/2003/OSIS/namespace}mentioned', '{http://www.bibletechnologies.net/2003/OSIS/namespace}name', '{http://www.bibletechnologies.net/2003/OSIS/namespace}reference', '{http://www.bibletechnologies.net/2003/OSIS/namespace}seg', '{http://www.bibletechnologies.net/2003/OSIS/namespace}transChange', '{http://www.bibletechnologies.net/2003/OSIS/namespace}salute', '{http://www.bibletechnologies.net/2003/OSIS/namespace}signed', '{http://www.bibletechnologies.net/2003/OSIS/namespace}closer', '{http://www.bibletechnologies.net/2003/OSIS/namespace}speech', '{http://www.bibletechnologies.net/2003/OSIS/namespace}speaker', '{http://www.bibletechnologies.net/2003/OSIS/namespace}list', '{http://www.bibletechnologies.net/2003/OSIS/namespace}item', '{http://www.bibletechnologies.net/2003/OSIS/namespace}table', '{http://www.bibletechnologies.net/2003/OSIS/namespace}head', '{http://www.bibletechnologies.net/2003/OSIS/namespace}row', '{http://www.bibletechnologies.net/2003/OSIS/namespace}cell', '{http://www.bibletechnologies.net/2003/OSIS/namespace}caption')) # Precompile a few xpath-querys verse_in_chapter = etree.XPath('count(//ns:chapter[1]/ns:verse)', namespaces=namespace) text_in_verse = etree.XPath('count(//ns:verse[1]/text())', namespaces=namespace) # Find books in the bible bible_books = osis_bible_tree.xpath("//ns:div[@type='book']", namespaces=namespace) for book in bible_books: if self.stop_import_flag: break # Remove div-tags in the book etree.strip_tags(book, ('{http://www.bibletechnologies.net/2003/OSIS/namespace}div')) book_ref_id = self.get_book_ref_id_by_name(book.get('osisID'), num_books, language_id) if not book_ref_id: book_ref_id = self.get_book_ref_id_by_localised_name(book.get('osisID')) if not book_ref_id: log.error('Importing books from "%s" failed' % self.filename) return False book_details = BiblesResourcesDB.get_book_by_id(book_ref_id) db_book = self.create_book(book_details['name'], book_ref_id, book_details['testament_id']) # Find out if chapter-tags contains the verses, or if it is used as milestone/anchor if int(verse_in_chapter(book)) > 0: # The chapter tags contains the verses for chapter in book: chapter_number = chapter.get("osisID").split('.')[1] # Find out if verse-tags contains the text, or if it is used as milestone/anchor if int(text_in_verse(chapter)) == 0: # verse-tags are used as milestone for verse in chapter: # If this tag marks the start of a verse, the verse text is between this tag and # the next tag, which the "tail" attribute gives us. if verse.get('sID'): verse_number = verse.get("osisID").split('.')[2] verse_text = verse.tail if verse_text: self.create_verse(db_book.id, chapter_number, verse_number, verse_text.strip()) else: # Verse-tags contains the text for verse in chapter: verse_number = verse.get("osisID").split('.')[2] if verse.text: self.create_verse(db_book.id, chapter_number, verse_number, verse.text.strip()) self.wizard.increment_progress_bar( translate('BiblesPlugin.OsisImport', 'Importing %(bookname)s %(chapter)s...') % {'bookname': db_book.name, 'chapter': chapter_number}) else: # The chapter tags is used as milestones. For now we assume verses is also milestones chapter_number = 0 for element in book: if element.tag == '{http://www.bibletechnologies.net/2003/OSIS/namespace}chapter' \ and element.get('sID'): chapter_number = element.get("osisID").split('.')[1] self.wizard.increment_progress_bar( translate('BiblesPlugin.OsisImport', 'Importing %(bookname)s %(chapter)s...') % {'bookname': db_book.name, 'chapter': chapter_number}) elif element.tag == '{http://www.bibletechnologies.net/2003/OSIS/namespace}verse' \ and element.get('sID'): # If this tag marks the start of a verse, the verse text is between this tag and # the next tag, which the "tail" attribute gives us. verse_number = element.get("osisID").split('.')[2] verse_text = element.tail if verse_text: self.create_verse(db_book.id, chapter_number, verse_number, verse_text.strip()) self.session.commit() self.application.process_events() except (ValueError, IOError): log.exception('Loading bible from OSIS file failed') trace_error_handler(log) success = False except etree.XMLSyntaxError as e: log.exception('Loading bible from OSIS file failed') trace_error_handler(log) success = False critical_error_message_box(message=translate('BiblesPlugin.OsisImport', 'The file is not a valid OSIS-XML file: \n%s' % e.msg)) finally: if import_file: import_file.close() if self.stop_import_flag: return False else: return success
def log_exception(self, message): """ Common log exception handler which prints the calling path """ trace_error_handler(self.logger) self.logger.exception(message)
def _download_index(self): """ Download the configuration file and kick off the theme screenshot download threads """ # check to see if we have web access self.web_access = False self.config = ConfigParser() user_agent = 'OpenLP/' + Registry().get('application').applicationVersion() self.application.process_events() try: web_config = get_web_page('%s%s' % (self.web, 'download.cfg'), header=('User-Agent', user_agent)) except (urllib.error.URLError, ConnectionError) as err: msg = QtGui.QMessageBox() title = translate('OpenLP.FirstTimeWizard', 'Network Error') msg.setText('{} {}'.format(title, err.code if hasattr(err, 'code') else '')) msg.setInformativeText(translate('OpenLP.FirstTimeWizard', 'There was a network error attempting to ' 'connect to retrieve initial configuration information')) msg.setStandardButtons(msg.Ok) ans = msg.exec_() web_config = False if web_config: files = web_config.read() try: self.config.read_string(files.decode()) self.web = self.config.get('general', 'base url') self.songs_url = self.web + self.config.get('songs', 'directory') + '/' self.bibles_url = self.web + self.config.get('bibles', 'directory') + '/' self.themes_url = self.web + self.config.get('themes', 'directory') + '/' self.web_access = True except (NoSectionError, NoOptionError, MissingSectionHeaderError): log.debug('A problem occured while parsing the downloaded config file') trace_error_handler(log) self.update_screen_list_combo() self.application.process_events() self.downloading = translate('OpenLP.FirstTimeWizard', 'Downloading %s...') if self.has_run_wizard: self.songs_check_box.setChecked(self.plugin_manager.get_plugin_by_name('songs').is_active()) self.bible_check_box.setChecked(self.plugin_manager.get_plugin_by_name('bibles').is_active()) self.presentation_check_box.setChecked(self.plugin_manager.get_plugin_by_name('presentations').is_active()) self.image_check_box.setChecked(self.plugin_manager.get_plugin_by_name('images').is_active()) self.media_check_box.setChecked(self.plugin_manager.get_plugin_by_name('media').is_active()) self.remote_check_box.setChecked(self.plugin_manager.get_plugin_by_name('remotes').is_active()) self.custom_check_box.setChecked(self.plugin_manager.get_plugin_by_name('custom').is_active()) self.song_usage_check_box.setChecked(self.plugin_manager.get_plugin_by_name('songusage').is_active()) self.alert_check_box.setChecked(self.plugin_manager.get_plugin_by_name('alerts').is_active()) self.application.set_normal_cursor() # Sort out internet access for downloads if self.web_access: songs = self.config.get('songs', 'languages') songs = songs.split(',') for song in songs: self.application.process_events() title = self.config.get('songs_%s' % song, 'title') filename = self.config.get('songs_%s' % song, 'filename') sha256 = self.config.get('songs_%s' % song, 'sha256', fallback='') item = QtGui.QListWidgetItem(title, self.songs_list_widget) item.setData(QtCore.Qt.UserRole, (filename, sha256)) item.setCheckState(QtCore.Qt.Unchecked) item.setFlags(item.flags() | QtCore.Qt.ItemIsUserCheckable) bible_languages = self.config.get('bibles', 'languages') bible_languages = bible_languages.split(',') for lang in bible_languages: self.application.process_events() language = self.config.get('bibles_%s' % lang, 'title') lang_item = QtGui.QTreeWidgetItem(self.bibles_tree_widget, [language]) bibles = self.config.get('bibles_%s' % lang, 'translations') bibles = bibles.split(',') for bible in bibles: self.application.process_events() title = self.config.get('bible_%s' % bible, 'title') filename = self.config.get('bible_%s' % bible, 'filename') sha256 = self.config.get('bible_%s' % bible, 'sha256', fallback='') item = QtGui.QTreeWidgetItem(lang_item, [title]) item.setData(0, QtCore.Qt.UserRole, (filename, sha256)) item.setCheckState(0, QtCore.Qt.Unchecked) item.setFlags(item.flags() | QtCore.Qt.ItemIsUserCheckable) self.bibles_tree_widget.expandAll() self.application.process_events() # Download the theme screenshots themes = self.config.get('themes', 'files').split(',') for theme in themes: title = self.config.get('theme_%s' % theme, 'title') filename = self.config.get('theme_%s' % theme, 'filename') sha256 = self.config.get('theme_%s' % theme, 'sha256', fallback='') screenshot = self.config.get('theme_%s' % theme, 'screenshot') worker = ThemeScreenshotWorker(self.themes_url, title, filename, sha256, screenshot) self.theme_screenshot_workers.append(worker) worker.screenshot_downloaded.connect(self.on_screenshot_downloaded) thread = QtCore.QThread(self) self.theme_screenshot_threads.append(thread) thread.started.connect(worker.run) worker.finished.connect(thread.quit) worker.moveToThread(thread) thread.start() self.application.process_events()
def log_critical(self, message): """ Common log critical handler which prints the calling path """ trace_error_handler(self.logger) self.logger.critical(message)