yet more files

This commit is contained in:
Tim Bentley 2016-04-22 19:32:59 +01:00
parent 0d755e3354
commit 123a7f02a7
3 changed files with 605 additions and 0 deletions

View File

@ -0,0 +1,226 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2016 OpenLP Developers #
# --------------------------------------------------------------------------- #
# 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:`listpreviewwidget` is a widget that lists the slides in the slide controller.
It is based on a QTableWidget but represents its contents in list form.
"""
from PyQt5 import QtCore, QtGui, QtWidgets
from openlp.core.common import RegistryProperties, Settings
from openlp.core.lib import ImageSource, ServiceItem
class ListPreviewWidget(QtWidgets.QTableWidget, RegistryProperties):
"""
A special type of QTableWidget which lists the slides in the slide controller
:param parent:
:param screen_ratio:
"""
def __init__(self, parent, screen_ratio):
"""
Initializes the widget to default state.
An empty ``ServiceItem`` is used by default. replace_service_manager_item() needs to be called to make this
widget display something.
"""
super(QtWidgets.QTableWidget, self).__init__(parent)
self._setup(screen_ratio)
def _setup(self, screen_ratio):
"""
Set up the widget
"""
self.setColumnCount(1)
self.horizontalHeader().setVisible(False)
self.setColumnWidth(0, self.parent().width())
self.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
self.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection)
self.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.setAlternatingRowColors(True)
# Initialize variables.
self.service_item = ServiceItem()
self.screen_ratio = screen_ratio
# Connect signals
self.verticalHeader().sectionResized.connect(self.row_resized)
def resizeEvent(self, event):
"""
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 on its own.
"""
self.setColumnWidth(0, self.viewport().width())
if self.service_item:
# Sort out songs, bibles, etc.
if self.service_item.is_text():
self.resizeRowsToContents()
# Sort out image heights.
else:
height = self.viewport().width() // self.screen_ratio
max_img_row_height = Settings().value('advanced/slide max height')
# Adjust for row height cap if in use.
if isinstance(max_img_row_height, int) and max_img_row_height > 0 and height > max_img_row_height:
height = max_img_row_height
# Apply new height to slides
for frame_number in range(len(self.service_item.get_frames())):
self.setRowHeight(frame_number, height)
def row_resized(self, row, old_height, new_height):
"""
Will scale non-image slides.
"""
# Only for non-text slides when row height cap in use
max_img_row_height = Settings().value('advanced/slide max height')
if self.service_item.is_text() or not isinstance(max_img_row_height, int) or max_img_row_height <= 0:
return
# Get and validate label widget containing slide & adjust max width
try:
self.cellWidget(row, 0).children()[1].setMaximumWidth(new_height * self.screen_ratio)
except:
return
def screen_size_changed(self, screen_ratio):
"""
This method is called whenever the live screen size changes, which then makes a layout recalculation necessary
:param screen_ratio: The new screen ratio
"""
self.screen_ratio = screen_ratio
self.__recalculate_layout()
def replace_service_item(self, service_item, width, slide_number):
"""
Replace the current preview items with the ones in service_item and display the given slide
:param service_item: The service item to insert
:param width: The width of the column
:param slide_number: The slide number to pre-select
"""
self.service_item = service_item
self.setRowCount(0)
self.clear()
self.setColumnWidth(0, width)
row = 0
text = []
for frame_number, frame in enumerate(self.service_item.get_frames()):
self.setRowCount(self.slide_count() + 1)
item = QtWidgets.QTableWidgetItem()
slide_height = 0
if self.service_item.is_text():
if frame['verseTag']:
# These tags are already translated.
verse_def = frame['verseTag']
verse_def = '%s%s' % (verse_def[0], verse_def[1:])
two_line_def = '%s\n%s' % (verse_def[0], verse_def[1:])
row = two_line_def
else:
row += 1
item.setText(frame['text'])
else:
label = QtWidgets.QLabel()
label.setContentsMargins(4, 4, 4, 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():
pixmap = QtGui.QPixmap(frame['image'])
pixmap.setDevicePixelRatio(label.devicePixelRatio())
label.setPixmap(pixmap)
else:
image = self.image_manager.get_image(frame['path'], ImageSource.ImagePlugin)
pixmap = QtGui.QPixmap.fromImage(image)
pixmap.setDevicePixelRatio(label.devicePixelRatio())
label.setPixmap(pixmap)
slide_height = width // self.screen_ratio
# Setup and validate row height cap if in use.
max_img_row_height = Settings().value('advanced/slide max height')
if isinstance(max_img_row_height, int) and max_img_row_height > 0:
if slide_height > max_img_row_height:
slide_height = max_img_row_height
label.setMaximumWidth(max_img_row_height * self.screen_ratio)
label.resize(max_img_row_height * self.screen_ratio, max_img_row_height)
# Build widget with stretch padding
container = QtWidgets.QWidget()
hbox = QtWidgets.QHBoxLayout()
hbox.setContentsMargins(0, 0, 0, 0)
hbox.addWidget(label, stretch=1)
hbox.addStretch(0)
container.setLayout(hbox)
# Add to table
self.setCellWidget(frame_number, 0, container)
else:
# Add to table
self.setCellWidget(frame_number, 0, label)
row += 1
text.append(str(row))
self.setItem(frame_number, 0, item)
if slide_height:
self.setRowHeight(frame_number, slide_height)
self.setVerticalHeaderLabels(text)
if self.service_item.is_text():
self.resizeRowsToContents()
self.setColumnWidth(0, self.viewport().width())
self.change_slide(slide_number)
def change_slide(self, slide):
"""
Switches to the given row.
"""
# Retrieve setting
autoscrolling = Settings().value('advanced/autoscrolling')
# Check if auto-scroll disabled (None) and validate value as dict containing 'dist' and 'pos'
# 'dist' represents the slide to scroll to relative to the new slide (-1 = previous, 0 = current, 1 = next)
# 'pos' represents the vert position of of the slide (0 = in view, 1 = top, 2 = middle, 3 = bottom)
if not (isinstance(autoscrolling, dict) and 'dist' in autoscrolling and 'pos' in autoscrolling and
isinstance(autoscrolling['dist'], int) and isinstance(autoscrolling['pos'], int)):
return
# prevent scrolling past list bounds
scroll_to_slide = slide + autoscrolling['dist']
if scroll_to_slide < 0:
scroll_to_slide = 0
if scroll_to_slide >= self.slide_count():
scroll_to_slide = self.slide_count() - 1
# Scroll to item if possible.
self.scrollToItem(self.item(scroll_to_slide, 0), autoscrolling['pos'])
self.selectRow(slide)
def current_slide_number(self):
"""
Returns the position of the currently active item. Will return -1 if the widget is empty.
"""
return super(ListPreviewWidget, self).currentRow()
def slide_count(self):
"""
Returns the number of slides this widget holds.
"""
return super(ListPreviewWidget, self).rowCount()

View File

@ -0,0 +1,71 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2016 OpenLP Developers #
# --------------------------------------------------------------------------- #
# 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 media manager dock.
"""
import logging
from openlp.core.lib import StringContent
log = logging.getLogger(__name__)
class MediaDockManager(object):
"""
Provide a repository for MediaManagerItems
"""
def __init__(self, media_dock):
"""
Initialise the media dock
"""
self.media_dock = media_dock
def add_item_to_dock(self, media_item):
"""
Add a MediaManagerItem to the dock
If the item has been added before, it's silently skipped
:param media_item: The item to add to the dock
"""
visible_title = media_item.plugin.get_string(StringContent.VisibleName)
log.debug('Inserting %s dock' % visible_title['title'])
match = False
for dock_index in range(self.media_dock.count()):
if self.media_dock.widget(dock_index).settings_section == media_item.plugin.name:
match = True
break
if not match:
self.media_dock.addItem(media_item, visible_title['title'])
def remove_dock(self, media_item):
"""
Removes a MediaManagerItem from the dock
:param media_item: The item to add to the dock
"""
visible_title = media_item.plugin.get_string(StringContent.VisibleName)
log.debug('remove %s dock' % visible_title['title'])
for dock_index in range(self.media_dock.count()):
if self.media_dock.widget(dock_index):
if self.media_dock.widget(dock_index).settings_section == media_item.plugin.name:
self.media_dock.widget(dock_index).setVisible(False)
self.media_dock.removeItem(dock_index)

View File

@ -0,0 +1,308 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2016 OpenLP Developers #
# --------------------------------------------------------------------------- #
# 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:``wizard`` module provides generic wizard tools for OpenLP.
"""
import logging
import os
from PyQt5 import QtGui, QtWidgets
from openlp.core.common import Registry, RegistryProperties, Settings, UiStrings, translate, is_macosx
from openlp.core.lib import build_icon
from openlp.core.lib.ui import add_welcome_page
log = logging.getLogger(__name__)
class WizardStrings(object):
"""
Provide standard strings for wizards to use.
"""
# Applications/Formats we import from or export to. These get used in
# multiple places but do not need translating unless you find evidence of
# the writers translating their own product name.
CSV = 'CSV'
OS = 'OpenSong'
OSIS = 'OSIS'
ZEF = 'Zefania'
# These strings should need a good reason to be retranslated elsewhere.
FinishedImport = translate('OpenLP.Ui', 'Finished import.')
FormatLabel = translate('OpenLP.Ui', 'Format:')
HeaderStyle = '<span style="font-size:14pt; font-weight:600;">%s</span>'
Importing = translate('OpenLP.Ui', 'Importing')
ImportingType = translate('OpenLP.Ui', 'Importing "%s"...')
ImportSelect = translate('OpenLP.Ui', 'Select Import Source')
ImportSelectLong = translate('OpenLP.Ui', 'Select the import format and the location to import from.')
OpenTypeFile = translate('OpenLP.Ui', 'Open %s File')
OpenTypeFolder = translate('OpenLP.Ui', 'Open %s Folder')
PercentSymbolFormat = translate('OpenLP.Ui', '%p%')
Ready = translate('OpenLP.Ui', 'Ready.')
StartingImport = translate('OpenLP.Ui', 'Starting import...')
YouSpecifyFile = translate('OpenLP.Ui', 'You need to specify one %s file to import from.',
'A file type e.g. OpenSong')
YouSpecifyFiles = translate('OpenLP.Ui', 'You need to specify at least one %s file to import from.',
'A file type e.g. OpenSong')
YouSpecifyFolder = translate('OpenLP.Ui', 'You need to specify one %s folder to import from.',
'A song format e.g. PowerSong')
class OpenLPWizard(QtWidgets.QWizard, RegistryProperties):
"""
Generic OpenLP wizard to provide generic functionality and a unified look
and feel.
``parent``
The QWidget-derived parent of the wizard.
``plugin``
Plugin this wizard is part of. The plugin will be saved in the "plugin" variable.
The plugin will also be used as basis for the file dialog methods this class provides.
``name``
The object name this wizard should have.
``image``
The image to display on the "welcome" page of the wizard. Should be 163x350.
``add_progress_page``
Whether to add a progress page with a progressbar at the end of the wizard.
"""
def __init__(self, parent, plugin, name, image, add_progress_page=True):
"""
Constructor
"""
super(OpenLPWizard, self).__init__(parent)
self.plugin = plugin
self.with_progress_page = add_progress_page
self.setObjectName(name)
self.open_icon = build_icon(':/general/general_open.png')
self.delete_icon = build_icon(':/general/general_delete.png')
self.finish_button = self.button(QtWidgets.QWizard.FinishButton)
self.cancel_button = self.button(QtWidgets.QWizard.CancelButton)
self.setupUi(image)
self.register_fields()
self.custom_init()
self.custom_signals()
self.currentIdChanged.connect(self.on_current_id_changed)
if self.with_progress_page:
self.error_copy_to_button.clicked.connect(self.on_error_copy_to_button_clicked)
self.error_save_to_button.clicked.connect(self.on_error_save_to_button_clicked)
def setupUi(self, image):
"""
Set up the wizard UI.
:param image: path to start up image
"""
self.setWindowIcon(build_icon(u':/icon/openlp-logo.svg'))
self.setModal(True)
self.setOptions(QtWidgets.QWizard.IndependentPages |
QtWidgets.QWizard.NoBackButtonOnStartPage | QtWidgets.QWizard.NoBackButtonOnLastPage)
if is_macosx():
self.setPixmap(QtWidgets.QWizard.BackgroundPixmap, QtGui.QPixmap(':/wizards/openlp-osx-wizard.png'))
else:
self.setWizardStyle(QtWidgets.QWizard.ModernStyle)
add_welcome_page(self, image)
self.add_custom_pages()
if self.with_progress_page:
self.add_progress_page()
self.retranslateUi()
def register_fields(self):
"""
Hook method for wizards to register any fields they need.
"""
pass
def custom_init(self):
"""
Hook method for custom initialisation
"""
pass
def custom_signals(self):
"""
Hook method for adding custom signals
"""
pass
def add_custom_pages(self):
"""
Hook method for wizards to add extra pages
"""
pass
def add_progress_page(self):
"""
Add the progress page for the wizard. This page informs the user how
the wizard is progressing with its task.
"""
self.progress_page = QtWidgets.QWizardPage()
self.progress_page.setObjectName('progress_page')
self.progress_layout = QtWidgets.QVBoxLayout(self.progress_page)
self.progress_layout.setContentsMargins(48, 48, 48, 48)
self.progress_layout.setObjectName('progress_layout')
self.progress_label = QtWidgets.QLabel(self.progress_page)
self.progress_label.setObjectName('progress_label')
self.progress_label.setWordWrap(True)
self.progress_layout.addWidget(self.progress_label)
self.progress_bar = QtWidgets.QProgressBar(self.progress_page)
self.progress_bar.setObjectName('progress_bar')
self.progress_layout.addWidget(self.progress_bar)
# Add a QTextEdit and a copy to file and copy to clipboard button to be
# able to provide feedback to the user. Hidden by default.
self.error_report_text_edit = QtWidgets.QTextEdit(self.progress_page)
self.error_report_text_edit.setObjectName('error_report_text_edit')
self.error_report_text_edit.setHidden(True)
self.error_report_text_edit.setReadOnly(True)
self.progress_layout.addWidget(self.error_report_text_edit)
self.error_button_layout = QtWidgets.QHBoxLayout()
self.error_button_layout.setObjectName('error_button_layout')
spacer = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.error_button_layout.addItem(spacer)
self.error_copy_to_button = QtWidgets.QPushButton(self.progress_page)
self.error_copy_to_button.setObjectName('error_copy_to_button')
self.error_copy_to_button.setHidden(True)
self.error_copy_to_button.setIcon(build_icon(':/system/system_edit_copy.png'))
self.error_button_layout.addWidget(self.error_copy_to_button)
self.error_save_to_button = QtWidgets.QPushButton(self.progress_page)
self.error_save_to_button.setObjectName('error_save_to_button')
self.error_save_to_button.setHidden(True)
self.error_save_to_button.setIcon(build_icon(':/general/general_save.png'))
self.error_button_layout.addWidget(self.error_save_to_button)
self.progress_layout.addLayout(self.error_button_layout)
self.addPage(self.progress_page)
def exec(self):
"""
Run the wizard.
"""
self.set_defaults()
return QtWidgets.QWizard.exec(self)
def reject(self):
"""
Stop the wizard on cancel button, close button or ESC key.
"""
log.debug('Wizard cancelled by user.')
if self.with_progress_page and self.currentPage() == self.progress_page:
Registry().execute('openlp_stop_wizard')
self.done(QtWidgets.QDialog.Rejected)
def on_current_id_changed(self, page_id):
"""
Perform necessary functions depending on which wizard page is active.
:param page_id: current page number
"""
if self.with_progress_page and self.page(page_id) == self.progress_page:
self.pre_wizard()
self.perform_wizard()
self.post_wizard()
else:
self.custom_page_changed(page_id)
def custom_page_changed(self, page_id):
"""
Called when changing to a page other than the progress page
:param page_id: current page number
"""
pass
def on_error_copy_to_button_clicked(self):
"""
Called when the ``error_copy_to_button`` has been clicked.
"""
pass
def on_error_save_to_button_clicked(self):
"""
Called when the ``error_save_to_button`` has been clicked.
"""
pass
def increment_progress_bar(self, status_text, increment=1):
"""
Update the wizard progress page.
:param status_text: Current status information to display.
:param increment: The value to increment the progress bar by.
"""
log.debug('IncrementBar %s', status_text)
self.progress_label.setText(status_text)
if increment > 0:
self.progress_bar.setValue(self.progress_bar.value() + increment)
self.application.process_events()
def pre_wizard(self):
"""
Prepare the UI for the import.
"""
self.finish_button.setVisible(False)
self.progress_bar.setMinimum(0)
self.progress_bar.setMaximum(1188)
self.progress_bar.setValue(0)
def post_wizard(self):
"""
Clean up the UI after the import has finished.
"""
self.progress_bar.setValue(self.progress_bar.maximum())
self.finish_button.setVisible(True)
self.cancel_button.setVisible(False)
self.application.process_events()
def get_file_name(self, title, editbox, setting_name, filters=''):
"""
Opens a QFileDialog and saves the filename to the given editbox.
:param title: The title of the dialog (unicode).
:param editbox: An editbox (QLineEdit).
:param setting_name: The place where to save the last opened directory.
:param filters: The file extension filters. It should contain the file description
as well as the file extension. For example::
'OpenLP 2 Databases (*.sqlite)'
"""
if filters:
filters += ';;'
filters += '%s (*)' % UiStrings().AllFiles
filename, filter_used = QtWidgets.QFileDialog.getOpenFileName(
self, title, os.path.dirname(Settings().value(self.plugin.settings_section + '/' + setting_name)),
filters)
if filename:
editbox.setText(filename)
Settings().setValue(self.plugin.settings_section + '/' + setting_name, filename)
def get_folder(self, title, editbox, setting_name):
"""
Opens a QFileDialog and saves the selected folder to the given editbox.
:param title: The title of the dialog (unicode).
:param editbox: An editbox (QLineEdit).
:param setting_name: The place where to save the last opened directory.
"""
folder = QtWidgets.QFileDialog.getExistingDirectory(
self, title, Settings().value(self.plugin.settings_section + '/' + setting_name),
QtWidgets.QFileDialog.ShowDirsOnly)
if folder:
editbox.setText(folder)
Settings().setValue(self.plugin.settings_section + '/' + setting_name, folder)