openlp-core team mailing list archive
-
openlp-core team
-
Mailing list archive
-
Message #20582
[Merge] lp:~patrick-zakweb/openlp/image-previews into lp:openlp
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:
Follow ups