mohij has proposed merging lp:~patrick-zakweb/openlp/image-previews into lp:openlp.
Requested reviews: OpenLP Core (openlp-core) For more details, see: https://code.launchpad.net/~patrick-zakweb/openlp/image-previews/+merge/160207 This branch does not add any new features. It refactors the preview list widget to be in a separate file with a relatively clean interface. This cleans up the slidecontroller.py a little and allows to add alternative implementations of the slide preview widget later on. I tested: -preview -live -live with images -switching songs via cursor keys -switching songs via double clicking in service manager -switching slides via shortcuts -resizing screen (There is a resize bug both, in trunk and in this branch, but I don't want to create a fix before merging this branch, since several changes in slidecontroller.py are necessary and I don't like having to merge branches) -resizing controller area One thing I am not clear about is the import statements. When adding the listpreviewwidget to __init__.py and importing via from openlp.core.ui import ListPreviewWidget I always get: Traceback (most recent call last): File "/home/patrick/Documents/openlp/repo/icon-preview-list/openlp.py", line 40, in <module> from openlp.core import main File "/home/patrick/Documents/openlp/repo/icon-preview-list/openlp/__init__.py", line 33, in <module> import core File "/home/patrick/Documents/openlp/repo/icon-preview-list/openlp/core/__init__.py", line 46, in <module> from openlp.core.lib import Settings, ScreenList, UiStrings, Registry, check_directory_exists File "/home/patrick/Documents/openlp/repo/icon-preview-list/openlp/core/lib/__init__.py", line 392, in <module> from renderer import Renderer File "/home/patrick/Documents/openlp/repo/icon-preview-list/openlp/core/lib/renderer.py", line 37, in <module> from openlp.core.ui import MainDisplay File "/home/patrick/Documents/openlp/repo/icon-preview-list/openlp/core/ui/__init__.py", line 89, in <module> from slidecontroller import SlideController, DisplayController File "/home/patrick/Documents/openlp/repo/icon-preview-list/openlp/core/ui/slidecontroller.py", line 44, in <module> from openlp.core.ui import ListPreviewWidget ImportError: cannot import name ListPreviewWidget Process finished with exit code 1 No idea what is wrong with that. Any hints appreciated. -- https://code.launchpad.net/~patrick-zakweb/openlp/image-previews/+merge/160207 Your team OpenLP Core is requested to review the proposed merge of lp:~patrick-zakweb/openlp/image-previews into lp:openlp.
=== added file 'openlp/core/ui/listpreviewwidget.py' --- openlp/core/ui/listpreviewwidget.py 1970-01-01 00:00:00 +0000 +++ openlp/core/ui/listpreviewwidget.py 2013-04-22 21:03:28 +0000 @@ -0,0 +1,171 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2013 Raoul Snyman # +# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan # +# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, # +# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. # +# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, # +# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, # +# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, # +# Frode Woldsund, Martin Zibricky, Patrick Zimmermann # +# --------------------------------------------------------------------------- # +# This program is free software; you can redistribute it and/or modify it # +# under the terms of the GNU General Public License as published by the Free # +# Software Foundation; version 2 of the License. # +# # +# This program is distributed in the hope that it will be useful, but WITHOUT # +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # +# more details. # +# # +# You should have received a copy of the GNU General Public License along # +# with this program; if not, write to the Free Software Foundation, Inc., 59 # +# Temple Place, Suite 330, Boston, MA 02111-1307 USA # +############################################################################### +""" +The :mod:`slidecontroller` module contains the most important part of OpenLP - the slide controller +""" + +from PyQt4 import QtCore, QtGui + +from openlp.core.lib import ImageSource, Registry, ServiceItem + + +class ListPreviewWidget(QtGui.QTableWidget): + def __init__(self, parent, screen_ratio): + super(QtGui.QTableWidget, self).__init__(parent) + self.service_item = ServiceItem() + self.screen_ratio = screen_ratio + + self.setColumnCount(1) + self.horizontalHeader().setVisible(False) + self.setColumnWidth(0, parent.width()) + self.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows) + self.setSelectionMode(QtGui.QAbstractItemView.SingleSelection) + self.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers) + self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) + self.setAlternatingRowColors(True) + + def resizeEvent(self, QResizeEvent): + """ + Overloaded method from QTableWidget. Will recalculate the layout. + """ + self.__recalculate_layout() + + def __recalculate_layout(self): + """ + Recalculates the layout of the table widget. It will set height and width + of the table cells. QTableWidget does not adapt the cells to the widget size at all. + """ + self.setColumnWidth(0, self.viewport().width()) + if self.service_item: + # Sort out songs, bibles, etc. + if self.service_item.is_text(): + self.resizeRowsToContents() + else: + # Sort out image heights. + for framenumber in range(len(self.service_item.get_frames())): + #self.setRowHeight(framenumber, width / ratio) + height = self.viewport().width() / self.screen_ratio + self.setRowHeight(framenumber, height) + + def screen_size_changed(self, screen_ratio): + """ + To be called whenever the live screen size changes. + Because this makes a layout recalculation necessary. + """ + self.screen_ratio = screen_ratio + self.__recalculate_layout() + + def replace_service_manager_item(self, service_item, width, slide): + """ + Replaces the current preview items with the ones in service_item. + Displays the given slide. + """ + self.service_item = service_item + self.clear() + self.setRowCount(0) + self.setColumnWidth(0, width) + row = 0 + text = [] + for framenumber, frame in enumerate(self.service_item.get_frames()): + self.setRowCount(self.rowCount() + 1) + item = QtGui.QTableWidgetItem() + slideHeight = 0 + if self.service_item.is_text(): + if frame[u'verseTag']: + # These tags are already translated. + verse_def = frame[u'verseTag'] + verse_def = u'%s%s' % (verse_def[0], verse_def[1:]) + two_line_def = u'%s\n%s' % (verse_def[0], verse_def[1:]) + row = two_line_def + else: + row += 1 + item.setText(frame[u'text']) + else: + label = QtGui.QLabel() + label.setMargin(4) + if self.service_item.is_media(): + label.setAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter) + else: + label.setScaledContents(True) + if self.service_item.is_command(): + label.setPixmap(QtGui.QPixmap(frame[u'image'])) + else: + image = self.image_manager.get_image(frame[u'path'], ImageSource.ImagePlugin) + label.setPixmap(QtGui.QPixmap.fromImage(image)) + self.setCellWidget(framenumber, 0, label) + slideHeight = width / self.screen_ratio + row += 1 + text.append(unicode(row)) + self.setItem(framenumber, 0, item) + if slideHeight: + self.setRowHeight(framenumber, slideHeight) + self.setVerticalHeaderLabels(text) + if self.service_item.is_text(): + self.resizeRowsToContents() + self.setColumnWidth(0, self.viewport().width()) + self.setFocus() + self.change_slide(slide) + + def change_slide(self, slide): + """ + Switches to the given row. + """ + if slide >= self.rowCount(): + slide = self.rowCount() - 1 + #Scroll to next item if possible. + if slide + 1 < self.rowCount(): + self.scrollToItem(self.item(slide + 1, 0)) + self.selectRow(slide) + + def currentRow(self): + return super(ListPreviewWidget, self).currentRow() + + def rowCount(self): + return super(ListPreviewWidget, self).rowCount() + + def _get_image_manager(self): + """ + Adds the image manager to the class dynamically + """ + if not hasattr(self, u'_image_manager'): + self._image_manager = Registry().get(u'image_manager') + return self._image_manager + + image_manager = property(_get_image_manager) + + def _get_main_window(self): + """ + Adds the main window to the class dynamically + """ + if not hasattr(self, u'_main_window'): + self._main_window = Registry().get(u'main_window') + return self._main_window + + main_window = property(_get_main_window) + === modified file 'openlp/core/ui/servicemanager.py' --- openlp/core/ui/servicemanager.py 2013-04-05 19:37:56 +0000 +++ openlp/core/ui/servicemanager.py 2013-04-22 21:03:28 +0000 @@ -1380,7 +1380,7 @@ self.preview_controller.addServiceManagerItem(self.service_items[item][u'service_item'], 0) next_item = self.service_manager_list.topLevelItem(item) self.service_manager_list.setCurrentItem(next_item) - self.live_controller.preview_list_widget.setFocus() + self.live_controller.preview_widget.setFocus() else: critical_error_message_box(translate('OpenLP.ServiceManager', 'Missing Display Handler'), translate('OpenLP.ServiceManager', === modified file 'openlp/core/ui/slidecontroller.py' --- openlp/core/ui/slidecontroller.py 2013-04-21 07:26:45 +0000 +++ openlp/core/ui/slidecontroller.py 2013-04-22 21:03:28 +0000 @@ -41,6 +41,7 @@ from openlp.core.ui import HideMode, MainDisplay, Display, DisplayControllerType from openlp.core.lib.ui import create_action from openlp.core.utils.actions import ActionList, CategoryOrder +from openlp.core.ui.listpreviewwidget import ListPreviewWidget log = logging.getLogger(__name__) @@ -157,18 +158,8 @@ self.controller_layout.setSpacing(0) self.controller_layout.setMargin(0) # Controller list view - self.preview_list_widget = QtGui.QTableWidget(self.controller) - self.preview_list_widget.setColumnCount(1) - self.preview_list_widget.horizontalHeader().setVisible(False) - self.preview_list_widget.setColumnWidth(0, self.controller.width()) - self.preview_list_widget.is_live = self.is_live - self.preview_list_widget.setObjectName(u'preview_list_widget') - self.preview_list_widget.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows) - self.preview_list_widget.setSelectionMode(QtGui.QAbstractItemView.SingleSelection) - self.preview_list_widget.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers) - self.preview_list_widget.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) - self.preview_list_widget.setAlternatingRowColors(True) - self.controller_layout.addWidget(self.preview_list_widget) + self.preview_widget = ListPreviewWidget(self, self.ratio) + self.controller_layout.addWidget(self.preview_widget) # Build the full toolbar self.toolbar = OpenLPToolbar(self) size_toolbar_policy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) @@ -350,7 +341,7 @@ {u'key': u'O', u'configurable': True, u'text': translate('OpenLP.SlideController', 'Go to "Other"')} ] shortcuts.extend([{u'key': unicode(number)} for number in range(10)]) - self.preview_list_widget.addActions([create_action(self, + self.controller.addActions([create_action(self, u'shortcutAction_%s' % s[u'key'], text=s.get(u'text'), can_shortcuts=True, context=QtCore.Qt.WidgetWithChildrenShortcut, @@ -358,7 +349,7 @@ triggers=self._slideShortcutActivated) for s in shortcuts]) self.shortcutTimer.timeout.connect(self._slideShortcutActivated) # Signals - self.preview_list_widget.clicked.connect(self.onSlideSelected) + self.preview_widget.clicked.connect(self.onSlideSelected) if self.is_live: # Need to use event as called across threads and UI is updated QtCore.QObject.connect(self, QtCore.SIGNAL(u'slidecontroller_toggle_display'), self.toggle_display) @@ -366,13 +357,13 @@ self.toolbar.set_widget_visible(self.loop_list, False) self.toolbar.set_widget_visible(self.wide_menu, False) else: - self.preview_list_widget.doubleClicked.connect(self.onGoLiveClick) + self.preview_widget.doubleClicked.connect(self.onGoLiveClick) self.toolbar.set_widget_visible([u'editSong'], False) if self.is_live: self.setLiveHotkeys(self) - self.__addActionsToWidget(self.preview_list_widget) + self.__addActionsToWidget(self.controller) else: - self.preview_list_widget.addActions([self.nextItem, self.previous_item]) + self.controller.addActions([self.nextItem, self.previous_item]) Registry().register_function(u'slidecontroller_%s_stop_loop' % self.type_prefix, self.on_stop_loop) Registry().register_function(u'slidecontroller_%s_change' % self.type_prefix, self.on_slide_change) Registry().register_function(u'slidecontroller_%s_blank' % self.type_prefix, self.on_slide_blank) @@ -431,7 +422,7 @@ if len(matches) == 1: self.shortcutTimer.stop() self.current_shortcut = u'' - self.__checkUpdateSelectedSlide(self.slideList[matches[0]]) + self.preview_widget.change_slide(self.slideList[matches[0]]) self.slideSelected() elif sender_name != u'shortcutTimer': # Start the time as we did not have any match. @@ -441,7 +432,7 @@ if self.current_shortcut in keys: # We had more than one match for example "V1" and "V10", but # "V1" was the slide we wanted to go. - self.__checkUpdateSelectedSlide(self.slideList[self.current_shortcut]) + self.preview_widget.change_slide(self.slideList[self.current_shortcut]) self.slideSelected() # Reset the shortcut. self.current_shortcut = u'' @@ -537,6 +528,7 @@ self.ratio = 1 self.media_controller.setup_display(self.display, False) self.previewSizeChanged() + self.preview_widget.screen_size_changed(self.ratio) self.preview_display.setup() service_item = ServiceItem() self.preview_display.web_view.setHtml(build_html(service_item, self.preview_display.screen, None, self.is_live, @@ -575,16 +567,6 @@ self.preview_display.screen = { u'size': self.preview_display.geometry()} # Make sure that the frames have the correct size. - self.preview_list_widget.setColumnWidth(0, self.preview_list_widget.viewport().size().width()) - if self.service_item: - # Sort out songs, bibles, etc. - if self.service_item.is_text(): - self.preview_list_widget.resizeRowsToContents() - else: - # Sort out image heights. - width = self.main_window.controlSplitter.sizes()[self.split] - for framenumber in range(len(self.service_item.get_frames())): - self.preview_list_widget.setRowHeight(framenumber, width / self.ratio) self.onControllerSizeChanged(self.controller.width()) def onControllerSizeChanged(self, width): @@ -710,7 +692,7 @@ Replacement item following a remote edit """ if item == self.service_item: - self._processItem(item, self.preview_list_widget.currentRow()) + self._processItem(item, self.preview_widget.currentRow()) def addServiceManagerItem(self, item, slideno): """ @@ -726,7 +708,7 @@ slidenum = 0 # If service item is the same as the current one, only change slide if slideno >= 0 and item == self.service_item: - self.__checkUpdateSelectedSlide(slidenum) + self.preview_widget.change_slide(slidenum) self.slideSelected() else: self._processItem(item, slidenum) @@ -753,10 +735,6 @@ self._resetBlank() Registry().execute(u'%s_start' % service_item.name.lower(), [service_item, self.is_live, self.hide_mode(), slideno]) self.slideList = {} - width = self.main_window.controlSplitter.sizes()[self.split] - self.preview_list_widget.clear() - self.preview_list_widget.setRowCount(0) - self.preview_list_widget.setColumnWidth(0, width) if self.is_live: self.song_menu.menu().clear() self.display.audio_player.reset() @@ -781,9 +759,8 @@ self.setAudioItemsVisibility(True) row = 0 text = [] + width = self.main_window.controlSplitter.sizes()[self.split] for framenumber, frame in enumerate(self.service_item.get_frames()): - self.preview_list_widget.setRowCount(self.preview_list_widget.rowCount() + 1) - item = QtGui.QTableWidgetItem() slideHeight = 0 if self.service_item.is_text(): if frame[u'verseTag']: @@ -799,37 +776,15 @@ else: row += 1 self.slideList[unicode(row)] = row - 1 - item.setText(frame[u'text']) else: - label = QtGui.QLabel() - label.setMargin(4) - if service_item.is_media(): - label.setAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter) - else: - label.setScaledContents(True) - if self.service_item.is_command(): - label.setPixmap(QtGui.QPixmap(frame[u'image'])) - else: - # If current slide set background to image - if framenumber == slideno: - self.service_item.bg_image_bytes = self.image_manager.get_image_bytes(frame[u'path'], - ImageSource.ImagePlugin) - image = self.image_manager.get_image(frame[u'path'], ImageSource.ImagePlugin) - label.setPixmap(QtGui.QPixmap.fromImage(image)) - self.preview_list_widget.setCellWidget(framenumber, 0, label) slideHeight = width * (1 / self.ratio) row += 1 self.slideList[unicode(row)] = row - 1 - text.append(unicode(row)) - self.preview_list_widget.setItem(framenumber, 0, item) - if slideHeight: - self.preview_list_widget.setRowHeight(framenumber, slideHeight) - self.preview_list_widget.setVerticalHeaderLabels(text) - if self.service_item.is_text(): - self.preview_list_widget.resizeRowsToContents() - self.preview_list_widget.setColumnWidth(0, - self.preview_list_widget.viewport().size().width()) - self.__updatePreviewSelection(slideno) + # If current slide set background to image + if not self.service_item.is_command() and framenumber == slideno: + self.service_item.bg_image_bytes = self.image_manager.get_image_bytes(frame[u'path'], + ImageSource.ImagePlugin) + self.preview_widget.replace_service_manager_item(self.service_item, width, slideno) self.enableToolBar(service_item) # Pass to display for viewing. # Postpone image build, we need to do this later to avoid the theme @@ -839,7 +794,6 @@ if service_item.is_media(): self.onMediaStart(service_item) self.slideSelected(True) - self.preview_list_widget.setFocus() if old_item: # Close the old item after the new one is opened # This avoids the service theme/desktop flashing on screen @@ -851,16 +805,6 @@ self.onMediaClose() Registry().execute(u'slidecontroller_%s_started' % self.type_prefix, [service_item]) - def __updatePreviewSelection(self, slideno): - """ - Utility method to update the selected slide in the list. - """ - if slideno > self.preview_list_widget.rowCount(): - self.preview_list_widget.selectRow( - self.preview_list_widget.rowCount() - 1) - else: - self.__checkUpdateSelectedSlide(slideno) - # Screen event methods def on_slide_selected_index(self, message): """ @@ -873,7 +817,7 @@ Registry().execute(u'%s_slide' % self.service_item.name.lower(), [self.service_item, self.is_live, index]) self.updatePreview() else: - self.__checkUpdateSelectedSlide(index) + self.preview_widget.change_slide(index) self.slideSelected() def mainDisplaySetBackground(self): @@ -1016,9 +960,9 @@ Generate the preview when you click on a slide. if this is the Live Controller also display on the screen """ - row = self.preview_list_widget.currentRow() + row = self.preview_widget.currentRow() self.selected_row = 0 - if -1 < row < self.preview_list_widget.rowCount(): + if -1 < row < self.preview_widget.rowCount(): if self.service_item.is_command(): if self.is_live and not start: Registry().execute(u'%s_slide' % self.service_item.name.lower(), @@ -1036,7 +980,7 @@ self.service_item.bg_image_bytes = None self.updatePreview() self.selected_row = row - self.__checkUpdateSelectedSlide(row) + self.preview_widget.change_slide(row) Registry().execute(u'slidecontroller_%s_changed' % self.type_prefix, row) self.display.setFocus() @@ -1044,7 +988,7 @@ """ The slide has been changed. Update the slidecontroller accordingly """ - self.__checkUpdateSelectedSlide(row) + self.preview_widget.change_slide(row) self.updatePreview() Registry().execute(u'slidecontroller_%s_changed' % self.type_prefix, row) @@ -1089,8 +1033,8 @@ if self.service_item.is_command() and self.is_live: self.updatePreview() else: - row = self.preview_list_widget.currentRow() + 1 - if row == self.preview_list_widget.rowCount(): + row = self.preview_widget.currentRow() + 1 + if row == self.preview_widget.rowCount(): if wrap is None: if self.slide_limits == SlideLimits.Wrap: row = 0 @@ -1098,12 +1042,12 @@ self.serviceNext() return else: - row = self.preview_list_widget.rowCount() - 1 + row = self.preview_widget.rowCount() - 1 elif wrap: row = 0 else: - row = self.preview_list_widget.rowCount() - 1 - self.__checkUpdateSelectedSlide(row) + row = self.preview_widget.rowCount() - 1 + self.preview_widget.change_slide(row) self.slideSelected() def on_slide_selected_previous(self): @@ -1116,27 +1060,19 @@ if self.service_item.is_command() and self.is_live: self.updatePreview() else: - row = self.preview_list_widget.currentRow() - 1 + row = self.preview_widget.currentRow() - 1 if row == -1: if self.slide_limits == SlideLimits.Wrap: - row = self.preview_list_widget.rowCount() - 1 + row = self.preview_widget.rowCount() - 1 elif self.is_live and self.slide_limits == SlideLimits.Next: self.keypress_queue.append(ServiceItemAction.PreviousLastSlide) self._process_queue() return else: row = 0 - self.__checkUpdateSelectedSlide(row) + self.preview_widget.change_slide(row) self.slideSelected() - def __checkUpdateSelectedSlide(self, row): - """ - Check if this slide has been updated - """ - if row + 1 < self.preview_list_widget.rowCount(): - self.preview_list_widget.scrollToItem(self.preview_list_widget.item(row + 1, 0)) - self.preview_list_widget.selectRow(row) - def onToggleLoop(self): """ Toggles the loop state. @@ -1151,7 +1087,7 @@ """ Start the timer loop running and store the timer id """ - if self.preview_list_widget.rowCount() > 1: + if self.preview_widget.rowCount() > 1: self.timer_id = self.startTimer(int(self.delay_spin_box.value()) * 1000) def on_stop_loop(self): @@ -1261,8 +1197,8 @@ """ If preview copy slide item to live controller from Preview Controller """ - row = self.preview_list_widget.currentRow() - if -1 < row < self.preview_list_widget.rowCount(): + row = self.preview_widget.currentRow() + if -1 < row < self.preview_widget.rowCount(): if self.service_item.from_service: self.service_manager.preview_live(self.service_item.unique_identifier, row) else:
_______________________________________________ Mailing list: https://launchpad.net/~openlp-core Post to : openlp-core@lists.launchpad.net Unsubscribe : https://launchpad.net/~openlp-core More help : https://help.launchpad.net/ListHelp