This commit is contained in:
Raoul Snyman 2010-07-14 17:59:13 +02:00
commit 38afbbdd5b
21 changed files with 782 additions and 260 deletions

View File

@ -174,6 +174,8 @@ def resize_image(image, width, height):
""" """
preview = QtGui.QImage(image) preview = QtGui.QImage(image)
if not preview.isNull(): if not preview.isNull():
if preview.width() == width and preview.height == height:
return preview
preview = preview.scaled(width, height, QtCore.Qt.KeepAspectRatio, preview = preview.scaled(width, height, QtCore.Qt.KeepAspectRatio,
QtCore.Qt.SmoothTransformation) QtCore.Qt.SmoothTransformation)
realw = preview.width() realw = preview.width()

View File

@ -323,9 +323,15 @@ class MediaManagerItem(QtGui.QWidget):
translate('MediaManagerItem', translate('MediaManagerItem',
'&Add to selected Service Item'), '&Add to selected Service Item'),
self.onAddEditClick)) self.onAddEditClick))
QtCore.QObject.connect( if QtCore.QSettings().value(u'advanced/double click live',
self.listView, QtCore.SIGNAL(u'doubleClicked(QModelIndex)'), QtCore.QVariant(False)).toBool():
self.onPreviewClick) QtCore.QObject.connect(self.listView,
QtCore.SIGNAL(u'doubleClicked(QModelIndex)'),
self.onLiveClick)
else:
QtCore.QObject.connect(self.listView,
QtCore.SIGNAL(u'doubleClicked(QModelIndex)'),
self.onPreviewClick)
def initialise(self): def initialise(self):
""" """
@ -379,14 +385,17 @@ class MediaManagerItem(QtGui.QWidget):
""" """
Validates to see if the file still exists or thumbnail is up to date Validates to see if the file still exists or thumbnail is up to date
""" """
if os.path.exists(file): if not os.path.exists(file):
return False
if os.path.exists(thumb):
filedate = os.stat(file).st_mtime filedate = os.stat(file).st_mtime
thumbdate = os.stat(thumb).st_mtime thumbdate = os.stat(thumb).st_mtime
#if file updated rebuild icon #if file updated rebuild icon
if filedate > thumbdate: if filedate > thumbdate:
self.iconFromFile(file, thumb) self.iconFromFile(file, thumb)
return True else:
return False self.iconFromFile(file, thumb)
return True
def iconFromFile(self, file, thumb): def iconFromFile(self, file, thumb):
""" """

View File

@ -269,8 +269,22 @@ class Plugin(QtCore.QObject):
if self.settings_tab: if self.settings_tab:
self.settingsForm.insertTab(self.settings_tab, self.weight) self.settingsForm.insertTab(self.settings_tab, self.weight)
def canDeleteTheme(self, theme): def usesTheme(self, theme):
""" """
Called to ask the plugin if a theme can be deleted Called to find out if a plugin is currently using a theme.
Returns True if the theme is being used, otherwise returns False.
""" """
return True return False
def renameTheme(self, oldTheme, newTheme):
"""
Renames a theme a plugin is using making the plugin use the new name.
``oldTheme``
The name of the theme the plugin should stop using.
``newTheme``
The new name the plugin should now use.
"""
pass

View File

@ -53,22 +53,34 @@ class AdvancedTab(SettingsTab):
self.leftLayout = QtGui.QVBoxLayout(self.leftWidget) self.leftLayout = QtGui.QVBoxLayout(self.leftWidget)
self.leftLayout.setSpacing(8) self.leftLayout.setSpacing(8)
self.leftLayout.setMargin(0) self.leftLayout.setMargin(0)
self.recentGroupBox = QtGui.QGroupBox(self.leftWidget) self.uiGroupBox = QtGui.QGroupBox(self.leftWidget)
self.recentGroupBox.setObjectName(u'recentGroupBox') self.uiGroupBox.setObjectName(u'uiGroupBox')
self.recentGroupBox.setGeometry(QtCore.QRect(0, 0, 220, 57)) self.uiLayout = QtGui.QVBoxLayout(self.uiGroupBox)
self.recentGroupBox.setMaximumSize(QtCore.QSize(220, 57)) self.uiLayout.setSpacing(8)
self.recentLayout = QtGui.QHBoxLayout(self.recentGroupBox) self.uiLayout.setMargin(6)
self.uiLayout.setObjectName(u'uiLayout')
self.recentLayout = QtGui.QHBoxLayout()
self.recentLayout.setSpacing(8) self.recentLayout.setSpacing(8)
self.recentLayout.setMargin(0) self.recentLayout.setMargin(0)
self.recentLayout.setObjectName(u'recentLayout') self.recentLayout.setObjectName(u'recentLayout')
self.recentLabel = QtGui.QLabel(self.recentGroupBox) self.recentLabel = QtGui.QLabel(self.uiGroupBox)
self.recentLabel.setObjectName(u'recentLabel') self.recentLabel.setObjectName(u'recentLabel')
self.recentLayout.addWidget(self.recentLabel) self.recentLayout.addWidget(self.recentLabel)
self.recentSpinBox = QtGui.QSpinBox(self.recentGroupBox) self.recentSpinBox = QtGui.QSpinBox(self.uiGroupBox)
self.recentSpinBox.setMinimum(0)
self.recentSpinBox.setObjectName(u'recentSpinBox') self.recentSpinBox.setObjectName(u'recentSpinBox')
self.recentSpinBox.setMinimum(0)
self.recentLayout.addWidget(self.recentSpinBox) self.recentLayout.addWidget(self.recentSpinBox)
self.leftLayout.addWidget(self.recentGroupBox) self.recentSpacer = QtGui.QSpacerItem(50, 20,
QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.recentLayout.addItem(self.recentSpacer)
self.uiLayout.addLayout(self.recentLayout)
self.mediaPluginCheckBox = QtGui.QCheckBox(self.uiGroupBox)
self.mediaPluginCheckBox.setObjectName(u'mediaPluginCheckBox')
self.uiLayout.addWidget(self.mediaPluginCheckBox)
self.doubleClickLiveCheckBox = QtGui.QCheckBox(self.uiGroupBox)
self.doubleClickLiveCheckBox.setObjectName(u'doubleClickLiveCheckBox')
self.uiLayout.addWidget(self.doubleClickLiveCheckBox)
self.leftLayout.addWidget(self.uiGroupBox)
# self.sharedDirGroupBox = QtGui.QGroupBox(self.leftWidget) # self.sharedDirGroupBox = QtGui.QGroupBox(self.leftWidget)
# self.sharedDirGroupBox.setObjectName(u'sharedDirGroupBox') # self.sharedDirGroupBox.setObjectName(u'sharedDirGroupBox')
# self.sharedDirGroupBox.setGeometry(QtCore.QRect(0, 65, 500, 85)) # self.sharedDirGroupBox.setGeometry(QtCore.QRect(0, 65, 500, 85))
@ -97,10 +109,10 @@ class AdvancedTab(SettingsTab):
QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
self.leftLayout.addItem(self.leftSpacer) self.leftLayout.addItem(self.leftSpacer)
self.advancedTabLayout.addWidget(self.leftWidget) self.advancedTabLayout.addWidget(self.leftWidget)
# self.rightWidget = QtGui.QWidget(self) self.rightWidget = QtGui.QWidget(self)
# self.rightLayout = QtGui.QVBoxLayout(self.rightWidget) self.rightLayout = QtGui.QVBoxLayout(self.rightWidget)
# self.rightLayout.setSpacing(8) self.rightLayout.setSpacing(8)
# self.rightLayout.setMargin(0) self.rightLayout.setMargin(0)
# self.databaseGroupBox = QtGui.QGroupBox(self.rightWidget) # self.databaseGroupBox = QtGui.QGroupBox(self.rightWidget)
# self.databaseGroupBox.setObjectName(u'databaseGroupBox') # self.databaseGroupBox.setObjectName(u'databaseGroupBox')
# self.databaseGroupBox.setEnabled(False) # self.databaseGroupBox.setEnabled(False)
@ -108,7 +120,10 @@ class AdvancedTab(SettingsTab):
# self.databaseLayout.setSpacing(8) # self.databaseLayout.setSpacing(8)
# self.databaseLayout.setMargin(8) # self.databaseLayout.setMargin(8)
# self.rightLayout.addWidget(self.databaseGroupBox) # self.rightLayout.addWidget(self.databaseGroupBox)
# self.advancedTabLayout.addWidget(self.rightWidget) self.rightSpacer = QtGui.QSpacerItem(20, 40,
QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
self.rightLayout.addItem(self.rightSpacer)
self.advancedTabLayout.addWidget(self.rightWidget)
# QtCore.QObject.connect(self.sharedCheckBox, # QtCore.QObject.connect(self.sharedCheckBox,
# QtCore.SIGNAL(u'stateChanged(int)'), self.onSharedCheckBoxChanged) # QtCore.SIGNAL(u'stateChanged(int)'), self.onSharedCheckBoxChanged)
@ -116,9 +131,13 @@ class AdvancedTab(SettingsTab):
""" """
Setup the interface translation strings. Setup the interface translation strings.
""" """
self.recentGroupBox.setTitle(translate('AdvancedTab', 'Recent Files')) self.uiGroupBox.setTitle(translate('AdvancedTab', 'UI Settings'))
self.recentLabel.setText( self.recentLabel.setText(
translate('AdvancedTab', 'Number of recent files to list:')) translate('AdvancedTab', 'Number of recent files to display:'))
self.mediaPluginCheckBox.setText(translate('AdvancedTab',
'Save currently selected media manager plugin'))
self.doubleClickLiveCheckBox.setText(translate('AdvancedTab',
'Double-click to send items straight to live (requires restart)'))
# self.sharedDirGroupBox.setTitle( # self.sharedDirGroupBox.setTitle(
# translate('AdvancedTab', 'Central Data Store')) # translate('AdvancedTab', 'Central Data Store'))
# self.sharedCheckBox.setText( # self.sharedCheckBox.setText(
@ -140,6 +159,12 @@ class AdvancedTab(SettingsTab):
u'max recent files', QtCore.QVariant(20)).toInt()[0]) u'max recent files', QtCore.QVariant(20)).toInt()[0])
self.recentSpinBox.setValue(settings.value(u'recent file count', self.recentSpinBox.setValue(settings.value(u'recent file count',
QtCore.QVariant(4)).toInt()[0]) QtCore.QVariant(4)).toInt()[0])
self.mediaPluginCheckBox.setChecked(
settings.value(u'save current plugin',
QtCore.QVariant(False)).toBool())
self.doubleClickLiveCheckBox.setChecked(
settings.value(u'double click live',
QtCore.QVariant(False)).toBool())
settings.endGroup() settings.endGroup()
def save(self): def save(self):
@ -150,6 +175,10 @@ class AdvancedTab(SettingsTab):
settings.beginGroup(self.settingsSection) settings.beginGroup(self.settingsSection)
settings.setValue(u'recent file count', settings.setValue(u'recent file count',
QtCore.QVariant(self.recentSpinBox.value())) QtCore.QVariant(self.recentSpinBox.value()))
settings.setValue(u'save current plugin',
QtCore.QVariant(self.mediaPluginCheckBox.isChecked()))
settings.setValue(u'double click live',
QtCore.QVariant(self.doubleClickLiveCheckBox.isChecked()))
settings.endGroup() settings.endGroup()
def onSharedCheckBoxChanged(self, checked): def onSharedCheckBoxChanged(self, checked):
@ -159,4 +188,3 @@ class AdvancedTab(SettingsTab):
self.sharedLabel.setEnabled(checked) self.sharedLabel.setEnabled(checked)
self.sharedTextEdit.setEnabled(checked) self.sharedTextEdit.setEnabled(checked)
self.sharedPushButton.setEnabled(checked) self.sharedPushButton.setEnabled(checked)

View File

@ -130,9 +130,9 @@ class Ui_AmendThemeDialog(object):
self.ImageLineEdit.setObjectName(u'ImageLineEdit') self.ImageLineEdit.setObjectName(u'ImageLineEdit')
self.horizontalLayout_2.addWidget(self.ImageLineEdit) self.horizontalLayout_2.addWidget(self.ImageLineEdit)
self.ImageToolButton = QtGui.QToolButton(self.ImageFilenameWidget) self.ImageToolButton = QtGui.QToolButton(self.ImageFilenameWidget)
icon1 = build_icon(u':/general/general_open.png') self.ImageToolButton.setIcon(build_icon(u':/general/general_open.png'))
self.ImageToolButton.setIcon(icon1)
self.ImageToolButton.setObjectName(u'ImageToolButton') self.ImageToolButton.setObjectName(u'ImageToolButton')
self.ImageToolButton.setAutoRaise(True)
self.horizontalLayout_2.addWidget(self.ImageToolButton) self.horizontalLayout_2.addWidget(self.ImageToolButton)
self.BackgroundLayout.setWidget(4, QtGui.QFormLayout.FieldRole, self.BackgroundLayout.setWidget(4, QtGui.QFormLayout.FieldRole,
self.ImageFilenameWidget) self.ImageFilenameWidget)

View File

@ -36,15 +36,21 @@ from amendthemedialog import Ui_AmendThemeDialog
log = logging.getLogger(u'AmendThemeForm') log = logging.getLogger(u'AmendThemeForm')
class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog): class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog):
"""
The :class:`AmendThemeForm` class provides the user interface to set up
new and edit existing themes.
"""
def __init__(self, parent): def __init__(self, parent):
"""
Initialise the theme editor user interface
"""
QtGui.QDialog.__init__(self, parent) QtGui.QDialog.__init__(self, parent)
self.thememanager = parent self.thememanager = parent
self.path = None self.path = None
self.theme = ThemeXML() self.theme = ThemeXML()
self.setupUi(self) self.setupUi(self)
#define signals # define signals
#Buttons # Buttons
QtCore.QObject.connect(self.Color1PushButton, QtCore.QObject.connect(self.Color1PushButton,
QtCore.SIGNAL(u'pressed()'), self.onColor1PushButtonClicked) QtCore.SIGNAL(u'pressed()'), self.onColor1PushButtonClicked)
QtCore.QObject.connect(self.Color2PushButton, QtCore.QObject.connect(self.Color2PushButton,
@ -59,8 +65,8 @@ class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog):
QtCore.QObject.connect(self.ShadowColorPushButton, QtCore.QObject.connect(self.ShadowColorPushButton,
QtCore.SIGNAL(u'pressed()'), self.onShadowColorPushButtonClicked) QtCore.SIGNAL(u'pressed()'), self.onShadowColorPushButtonClicked)
QtCore.QObject.connect(self.ImageToolButton, QtCore.QObject.connect(self.ImageToolButton,
QtCore.SIGNAL(u'pressed()'), self.onImageToolButtonClicked) QtCore.SIGNAL(u'clicked()'), self.onImageToolButtonClicked)
#Combo boxes # Combo boxes
QtCore.QObject.connect(self.BackgroundComboBox, QtCore.QObject.connect(self.BackgroundComboBox,
QtCore.SIGNAL(u'activated(int)'), self.onBackgroundComboBoxSelected) QtCore.SIGNAL(u'activated(int)'), self.onBackgroundComboBoxSelected)
QtCore.QObject.connect(self.BackgroundTypeComboBox, QtCore.QObject.connect(self.BackgroundTypeComboBox,
@ -82,16 +88,13 @@ class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog):
QtCore.SIGNAL(u'activated(int)'), self.onHorizontalComboBoxSelected) QtCore.SIGNAL(u'activated(int)'), self.onHorizontalComboBoxSelected)
QtCore.QObject.connect(self.VerticalComboBox, QtCore.QObject.connect(self.VerticalComboBox,
QtCore.SIGNAL(u'activated(int)'), self.onVerticalComboBoxSelected) QtCore.SIGNAL(u'activated(int)'), self.onVerticalComboBoxSelected)
#Spin boxes # Spin boxes
QtCore.QObject.connect(self.FontMainSizeSpinBox, QtCore.QObject.connect(self.FontMainSizeSpinBox,
QtCore.SIGNAL(u'editingFinished()'), QtCore.SIGNAL(u'editingFinished()'),
self.onFontMainSizeSpinBoxChanged) self.onFontMainSizeSpinBoxChanged)
QtCore.QObject.connect(self.FontFooterSizeSpinBox, QtCore.QObject.connect(self.FontFooterSizeSpinBox,
QtCore.SIGNAL(u'editingFinished()'), QtCore.SIGNAL(u'editingFinished()'),
self.onFontFooterSizeSpinBoxChanged) self.onFontFooterSizeSpinBoxChanged)
QtCore.QObject.connect(self.FontMainDefaultCheckBox,
QtCore.SIGNAL(u'stateChanged(int)'),
self.onFontMainDefaultCheckBoxChanged)
QtCore.QObject.connect(self.FontMainXSpinBox, QtCore.QObject.connect(self.FontMainXSpinBox,
QtCore.SIGNAL(u'editingFinished()'), self.onFontMainXSpinBoxChanged) QtCore.SIGNAL(u'editingFinished()'), self.onFontMainXSpinBoxChanged)
QtCore.QObject.connect(self.FontMainYSpinBox, QtCore.QObject.connect(self.FontMainYSpinBox,
@ -108,9 +111,6 @@ class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog):
QtCore.QObject.connect(self.FontMainLineSpacingSpinBox, QtCore.QObject.connect(self.FontMainLineSpacingSpinBox,
QtCore.SIGNAL(u'editingFinished()'), QtCore.SIGNAL(u'editingFinished()'),
self.onFontMainLineSpacingSpinBoxChanged) self.onFontMainLineSpacingSpinBoxChanged)
QtCore.QObject.connect(self.FontFooterDefaultCheckBox,
QtCore.SIGNAL(u'stateChanged(int)'),
self.onFontFooterDefaultCheckBoxChanged)
QtCore.QObject.connect(self.FontFooterXSpinBox, QtCore.QObject.connect(self.FontFooterXSpinBox,
QtCore.SIGNAL(u'editingFinished()'), QtCore.SIGNAL(u'editingFinished()'),
self.onFontFooterXSpinBoxChanged) self.onFontFooterXSpinBoxChanged)
@ -123,16 +123,23 @@ class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog):
QtCore.QObject.connect(self.FontFooterHeightSpinBox, QtCore.QObject.connect(self.FontFooterHeightSpinBox,
QtCore.SIGNAL(u'editingFinished()'), QtCore.SIGNAL(u'editingFinished()'),
self.onFontFooterHeightSpinBoxChanged) self.onFontFooterHeightSpinBoxChanged)
QtCore.QObject.connect(self.OutlineCheckBox,
QtCore.SIGNAL(u'stateChanged(int)'), self.onOutlineCheckBoxChanged)
QtCore.QObject.connect(self.ShadowSpinBox, QtCore.QObject.connect(self.ShadowSpinBox,
QtCore.SIGNAL(u'editingFinished()'), QtCore.SIGNAL(u'editingFinished()'),
self.onShadowSpinBoxChanged) self.onShadowSpinBoxChanged)
QtCore.QObject.connect(self.ShadowCheckBox,
QtCore.SIGNAL(u'stateChanged(int)'), self.onShadowCheckBoxChanged)
QtCore.QObject.connect(self.OutlineSpinBox, QtCore.QObject.connect(self.OutlineSpinBox,
QtCore.SIGNAL(u'editingFinished()'), QtCore.SIGNAL(u'editingFinished()'),
self.onOutlineSpinBoxChanged) self.onOutlineSpinBoxChanged)
# CheckBoxes
QtCore.QObject.connect(self.FontMainDefaultCheckBox,
QtCore.SIGNAL(u'stateChanged(int)'),
self.onFontMainDefaultCheckBoxChanged)
QtCore.QObject.connect(self.FontFooterDefaultCheckBox,
QtCore.SIGNAL(u'stateChanged(int)'),
self.onFontFooterDefaultCheckBoxChanged)
QtCore.QObject.connect(self.OutlineCheckBox,
QtCore.SIGNAL(u'stateChanged(int)'), self.onOutlineCheckBoxChanged)
QtCore.QObject.connect(self.ShadowCheckBox,
QtCore.SIGNAL(u'stateChanged(int)'), self.onShadowCheckBoxChanged)
QtCore.QObject.connect(self.SlideTransitionCheckBox, QtCore.QObject.connect(self.SlideTransitionCheckBox,
QtCore.SIGNAL(u'stateChanged(int)'), QtCore.SIGNAL(u'stateChanged(int)'),
self.onSlideTransitionCheckBoxChanged) self.onSlideTransitionCheckBoxChanged)

View File

@ -658,6 +658,12 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
log.info(u'Load Themes') log.info(u'Load Themes')
self.ThemeManagerContents.loadThemes() self.ThemeManagerContents.loadThemes()
log.info(u'Load data from Settings') log.info(u'Load data from Settings')
if QtCore.QSettings().value(u'advanced/save current plugin',
QtCore.QVariant(False)).toBool():
savedPlugin = QtCore.QSettings().value(
u'advanced/current media plugin', QtCore.QVariant()).toInt()[0]
if savedPlugin != -1:
self.MediaToolBox.setCurrentIndex(savedPlugin)
self.settingsForm.postSetUp() self.settingsForm.postSetUp()
def setAutoLanguage(self, value): def setAutoLanguage(self, value):
@ -820,6 +826,10 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
""" """
# Clean temporary files used by services # Clean temporary files used by services
self.ServiceManagerContents.cleanUp() self.ServiceManagerContents.cleanUp()
if QtCore.QSettings().value(u'advanced/save current plugin',
QtCore.QVariant(False)).toBool():
QtCore.QSettings().setValue(u'advanced/current media plugin',
QtCore.QVariant(self.MediaToolBox.currentIndex()))
# Call the cleanup method to shutdown plugins. # Call the cleanup method to shutdown plugins.
log.info(u'cleanup plugins') log.info(u'cleanup plugins')
self.plugin_manager.finalise_plugins() self.plugin_manager.finalise_plugins()

View File

@ -202,15 +202,21 @@ class SlideController(QtGui.QWidget):
self.Toolbar.addToolbarWidget(u'Hide Menu', self.HideMenu) self.Toolbar.addToolbarWidget(u'Hide Menu', self.HideMenu)
self.HideMenu.setMenu(QtGui.QMenu( self.HideMenu.setMenu(QtGui.QMenu(
translate('SlideController', 'Hide'), self.Toolbar)) translate('SlideController', 'Hide'), self.Toolbar))
self.BlankScreen = QtGui.QAction(QtGui.QIcon( u':/slides/slide_blank.png'), u'Blank Screen', self.HideMenu) self.BlankScreen = QtGui.QAction(QtGui.QIcon(
u':/slides/slide_blank.png'), u'Blank Screen', self.HideMenu)
self.BlankScreen.setCheckable(True) self.BlankScreen.setCheckable(True)
QtCore.QObject.connect(self.BlankScreen, QtCore.SIGNAL("triggered(bool)"), self.onBlankDisplay) QtCore.QObject.connect(self.BlankScreen,
self.ThemeScreen = QtGui.QAction(QtGui.QIcon(u':/slides/slide_theme.png'), u'Blank to Theme', self.HideMenu) QtCore.SIGNAL("triggered(bool)"), self.onBlankDisplay)
self.ThemeScreen = QtGui.QAction(QtGui.QIcon(
u':/slides/slide_theme.png'), u'Blank to Theme', self.HideMenu)
self.ThemeScreen.setCheckable(True) self.ThemeScreen.setCheckable(True)
QtCore.QObject.connect(self.ThemeScreen, QtCore.SIGNAL("triggered(bool)"), self.onThemeDisplay) QtCore.QObject.connect(self.ThemeScreen,
self.DesktopScreen = QtGui.QAction(QtGui.QIcon(u':/slides/slide_desktop.png'), u'Show Desktop', self.HideMenu) QtCore.SIGNAL("triggered(bool)"), self.onThemeDisplay)
self.DesktopScreen = QtGui.QAction(QtGui.QIcon(
u':/slides/slide_desktop.png'), u'Show Desktop', self.HideMenu)
self.DesktopScreen.setCheckable(True) self.DesktopScreen.setCheckable(True)
QtCore.QObject.connect(self.DesktopScreen, QtCore.SIGNAL("triggered(bool)"), self.onHideDisplay) QtCore.QObject.connect(self.DesktopScreen,
QtCore.SIGNAL("triggered(bool)"), self.onHideDisplay)
self.HideMenu.setDefaultAction(self.BlankScreen) self.HideMenu.setDefaultAction(self.BlankScreen)
self.HideMenu.menu().addAction(self.BlankScreen) self.HideMenu.menu().addAction(self.BlankScreen)
self.HideMenu.menu().addAction(self.ThemeScreen) self.HideMenu.menu().addAction(self.ThemeScreen)
@ -241,9 +247,8 @@ class SlideController(QtGui.QWidget):
self.Toolbar.addToolbarWidget( self.Toolbar.addToolbarWidget(
u'Image SpinBox', self.DelaySpinBox) u'Image SpinBox', self.DelaySpinBox)
self.DelaySpinBox.setSuffix(translate('SlideController', 's')) self.DelaySpinBox.setSuffix(translate('SlideController', 's'))
self.DelaySpinBox.setToolTip( self.DelaySpinBox.setToolTip(translate('SlideController',
translate('SlideController', 'Delay between slides in seconds'))
'Delay between slides in seconds'))
self.ControllerLayout.addWidget(self.Toolbar) self.ControllerLayout.addWidget(self.Toolbar)
#Build a Media ToolBar #Build a Media ToolBar
self.Mediabar = OpenLPToolbar(self) self.Mediabar = OpenLPToolbar(self)
@ -268,8 +273,7 @@ class SlideController(QtGui.QWidget):
self.volumeSlider = Phonon.VolumeSlider() self.volumeSlider = Phonon.VolumeSlider()
self.volumeSlider.setGeometry(QtCore.QRect(90, 260, 221, 24)) self.volumeSlider.setGeometry(QtCore.QRect(90, 260, 221, 24))
self.volumeSlider.setObjectName(u'volumeSlider') self.volumeSlider.setObjectName(u'volumeSlider')
self.Mediabar.addToolbarWidget( self.Mediabar.addToolbarWidget(u'Audio Volume', self.volumeSlider)
u'Audio Volume', self.volumeSlider)
self.ControllerLayout.addWidget(self.Mediabar) self.ControllerLayout.addWidget(self.Mediabar)
# Build the Song Toolbar # Build the Song Toolbar
if isLive: if isLive:
@ -328,6 +332,11 @@ class SlideController(QtGui.QWidget):
# Signals # Signals
QtCore.QObject.connect(self.PreviewListWidget, QtCore.QObject.connect(self.PreviewListWidget,
QtCore.SIGNAL(u'clicked(QModelIndex)'), self.onSlideSelected) QtCore.SIGNAL(u'clicked(QModelIndex)'), self.onSlideSelected)
if not self.isLive:
if QtCore.QSettings().value(u'advanced/double click live',
QtCore.QVariant(False)).toBool():
QtCore.QObject.connect(self.PreviewListWidget,
QtCore.SIGNAL(u'doubleClicked(QModelIndex)'), self.onGoLive)
if isLive: if isLive:
QtCore.QObject.connect(Receiver.get_receiver(), QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'slidecontroller_live_spin_delay'), QtCore.SIGNAL(u'slidecontroller_live_spin_delay'),

View File

@ -48,6 +48,7 @@ class ThemeManager(QtGui.QWidget):
QtGui.QWidget.__init__(self, parent) QtGui.QWidget.__init__(self, parent)
self.parent = parent self.parent = parent
self.settingsSection = u'themes' self.settingsSection = u'themes'
self.serviceComboBox = self.parent.ServiceManagerContents.ThemeComboBox
self.Layout = QtGui.QVBoxLayout(self) self.Layout = QtGui.QVBoxLayout(self)
self.Layout.setSpacing(0) self.Layout.setSpacing(0)
self.Layout.setMargin(0) self.Layout.setMargin(0)
@ -116,6 +117,7 @@ class ThemeManager(QtGui.QWidget):
self.thumbPath = os.path.join(self.path, u'thumbnails') self.thumbPath = os.path.join(self.path, u'thumbnails')
self.checkThemesExists(self.thumbPath) self.checkThemesExists(self.thumbPath)
self.amendThemeForm.path = self.path self.amendThemeForm.path = self.path
self.oldBackgroundImage = None
# Last little bits of setting up # Last little bits of setting up
self.global_theme = unicode(QtCore.QSettings().value( self.global_theme = unicode(QtCore.QSettings().value(
self.settingsSection + u'/global theme', self.settingsSection + u'/global theme',
@ -182,11 +184,17 @@ class ThemeManager(QtGui.QWidget):
Loads the settings for the theme that is to be edited and launches the Loads the settings for the theme that is to be edited and launches the
theme editing form so the user can make their changes. theme editing form so the user can make their changes.
""" """
self.editingDefault = False
if check_item_selected(self.ThemeListWidget, translate('ThemeManager', if check_item_selected(self.ThemeListWidget, translate('ThemeManager',
'You must select a theme to edit.')): 'You must select a theme to edit.')):
item = self.ThemeListWidget.currentItem() item = self.ThemeListWidget.currentItem()
themeName = unicode(item.text())
if themeName != unicode(item.data(QtCore.Qt.UserRole).toString()):
self.editingDefault = True
theme = self.getThemeData( theme = self.getThemeData(
unicode(item.data(QtCore.Qt.UserRole).toString())) unicode(item.data(QtCore.Qt.UserRole).toString()))
if theme.background_type == u'image':
self.oldBackgroundImage = theme.background_filename
self.amendThemeForm.loadTheme(theme) self.amendThemeForm.loadTheme(theme)
self.saveThemeName = unicode( self.saveThemeName = unicode(
item.data(QtCore.Qt.UserRole).toString()) item.data(QtCore.Qt.UserRole).toString())
@ -212,37 +220,44 @@ class ThemeManager(QtGui.QWidget):
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok)) QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok))
else: else:
for plugin in self.parent.plugin_manager.plugins: for plugin in self.parent.plugin_manager.plugins:
if not plugin.canDeleteTheme(theme): if plugin.usesTheme(theme):
QtGui.QMessageBox.critical(self, QtGui.QMessageBox.critical(self,
translate('ThemeManager', 'Error'), translate('ThemeManager', 'Error'),
unicode(translate('ThemeManager', unicode(translate('ThemeManager',
'Theme %s is use in %s plugin.')) % \ 'Theme %s is use in %s plugin.')) % \
(theme, plugin.name)) (theme, plugin.name))
return return
if unicode(self.parent.ServiceManagerContents.ThemeComboBox \ if unicode(self.serviceComboBox.currentText()) == theme:
.currentText()) == theme:
QtGui.QMessageBox.critical(self, QtGui.QMessageBox.critical(self,
translate('ThemeManager', 'Error'), translate('ThemeManager', 'Error'),
unicode(translate('ThemeManager', unicode(translate('ThemeManager',
'Theme %s is use by the service manager.')) % theme) 'Theme %s is use by the service manager.')) % theme)
return return
self.themelist.remove(theme)
th = theme + u'.png'
row = self.ThemeListWidget.row(item) row = self.ThemeListWidget.row(item)
self.ThemeListWidget.takeItem(row) self.ThemeListWidget.takeItem(row)
try: self.deleteTheme(theme)
os.remove(os.path.join(self.path, th))
os.remove(os.path.join(self.thumbPath, th)) def deleteTheme(self, theme):
encoding = get_filesystem_encoding() """
shutil.rmtree( Delete a theme.
os.path.join(self.path, theme).encode(encoding))
except OSError: ``theme``
#if not present do not worry The theme to delete.
pass """
# As we do not reload the themes push out the change self.themelist.remove(theme)
# Reaload the list as the internal lists and events need th = theme + u'.png'
# to be triggered try:
self.pushThemes() os.remove(os.path.join(self.path, th))
os.remove(os.path.join(self.thumbPath, th))
encoding = get_filesystem_encoding()
shutil.rmtree(os.path.join(self.path, theme).encode(encoding))
except OSError:
#if not present do not worry
pass
# As we do not reload the themes push out the change
# Reaload the list as the internal lists and events need
# to be triggered
self.pushThemes()
def onExportTheme(self): def onExportTheme(self):
""" """
@ -532,18 +547,31 @@ class ThemeManager(QtGui.QWidget):
os.mkdir(os.path.join(self.path, name)) os.mkdir(os.path.join(self.path, name))
theme_file = os.path.join(theme_dir, name + u'.xml') theme_file = os.path.join(theme_dir, name + u'.xml')
log.debug(theme_file) log.debug(theme_file)
editedServiceTheme = False
result = QtGui.QMessageBox.Yes result = QtGui.QMessageBox.Yes
if self.saveThemeName != name: if self.saveThemeName != name:
if os.path.exists(theme_file): if os.path.exists(theme_file):
result = QtGui.QMessageBox.question(self, result = QtGui.QMessageBox.question(self,
translate('ThemeManager', 'Theme Exists'), translate('ThemeManager', 'Theme Exists'),
translate('ThemeManager', translate('ThemeManager', 'A theme with this name already '
'A theme with this name already exists. ' 'exists. Would you like to overwrite it?'),
'Would you like to overwrite it?'),
(QtGui.QMessageBox.Yes | QtGui.QMessageBox.No), (QtGui.QMessageBox.Yes | QtGui.QMessageBox.No),
QtGui.QMessageBox.No) QtGui.QMessageBox.No)
if self.saveThemeName != u'':
for plugin in self.parent.plugin_manager.plugins:
if plugin.usesTheme(self.saveThemeName):
plugin.renameTheme(self.saveThemeName, name)
if unicode(self.serviceComboBox.currentText()) == name:
editedServiceTheme = True
self.deleteTheme(self.saveThemeName)
if result == QtGui.QMessageBox.Yes: if result == QtGui.QMessageBox.Yes:
# Save the theme, overwriting the existing theme if necessary. # Save the theme, overwriting the existing theme if necessary.
if image_to and self.oldBackgroundImage and \
image_to != self.oldBackgroundImage:
try:
os.remove(self.oldBackgroundImage)
except OSError:
log.exception(u'Unable to remove old theme background')
outfile = None outfile = None
try: try:
outfile = open(theme_file, u'w') outfile = open(theme_file, u'w')
@ -563,6 +591,26 @@ class ThemeManager(QtGui.QWidget):
log.exception(u'Failed to save theme image') log.exception(u'Failed to save theme image')
self.generateAndSaveImage(self.path, name, theme_xml) self.generateAndSaveImage(self.path, name, theme_xml)
self.loadThemes() self.loadThemes()
# Check if we need to set a new service theme
if editedServiceTheme:
newThemeIndex = self.serviceComboBox.findText(name)
if newThemeIndex != -1:
self.serviceComboBox.setCurrentIndex(newThemeIndex)
if self.editingDefault:
newThemeItem = self.ThemeListWidget.findItems(name,
QtCore.Qt.MatchExactly)[0]
newThemeIndex = self.ThemeListWidget.indexFromItem(
newThemeItem).row()
self.global_theme = unicode(
self.ThemeListWidget.item(newThemeIndex).text())
newName = unicode(translate('ThemeManager', '%s (default)')) % \
self.global_theme
self.ThemeListWidget.item(newThemeIndex).setText(newName)
QtCore.QSettings().setValue(
self.settingsSection + u'/global theme',
QtCore.QVariant(self.global_theme))
Receiver.send_message(u'theme_update_global', self.global_theme)
self.pushThemes()
else: else:
# Don't close the dialog - allow the user to change the name of # Don't close the dialog - allow the user to change the name of
# the theme or to cancel the theme dialog completely. # the theme or to cancel the theme dialog completely.

View File

@ -95,7 +95,26 @@ class BiblePlugin(Plugin):
'displayed on the screen during the service.') 'displayed on the screen during the service.')
return about_text return about_text
def canDeleteTheme(self, theme): def usesTheme(self, theme):
"""
Called to find out if the bible plugin is currently using a theme.
Returns True if the theme is being used, otherwise returns False.
"""
if self.settings_tab.bible_theme == theme: if self.settings_tab.bible_theme == theme:
return False return True
return True return False
def renameTheme(self, oldTheme, newTheme):
"""
Rename the theme the bible plugin is using making the plugin use the
new name.
``oldTheme``
The name of the theme the plugin should stop using. Unused for
this particular plugin.
``newTheme``
The new name the plugin should now use.
"""
self.settings_tab.bible_theme = newTheme

View File

@ -69,8 +69,30 @@ class CustomPlugin(Plugin):
'songs plugin.<br>') 'songs plugin.<br>')
return about_text return about_text
def canDeleteTheme(self, theme): def usesTheme(self, theme):
if not self.custommanager.get_all_objects_filtered(CustomSlide, """
Called to find out if the custom plugin is currently using a theme.
Returns True if the theme is being used, otherwise returns False.
"""
if self.custommanager.get_all_objects_filtered(CustomSlide,
CustomSlide.theme_name == theme): CustomSlide.theme_name == theme):
return True return True
return False return False
def renameTheme(self, oldTheme, newTheme):
"""
Renames a theme the custom plugin is using making the plugin use the
new name.
``oldTheme``
The name of the theme the plugin should stop using.
``newTheme``
The new name the plugin should now use.
"""
customsUsingTheme = self.custommanager.get_all_objects_filtered(
CustomSlide, CustomSlide.theme_name == oldTheme)
for custom in customsUsingTheme:
custom.theme_name = newTheme
self.custommanager.save_object(custom)

View File

@ -37,8 +37,6 @@ import logging
import os import os
import time import time
from openlp.core.lib import resize_image
if os.name == u'nt': if os.name == u'nt':
from win32com.client import Dispatch from win32com.client import Dispatch
import pywintypes import pywintypes
@ -74,6 +72,7 @@ class ImpressController(PresentationController):
self.alsosupports = [u'.ppt', u'.pps', u'.pptx', u'.ppsx'] self.alsosupports = [u'.ppt', u'.pps', u'.pptx', u'.ppsx']
self.process = None self.process = None
self.desktop = None self.desktop = None
self.manager = None
def check_available(self): def check_available(self):
""" """
@ -104,6 +103,10 @@ class ImpressController(PresentationController):
self.process.waitForStarted() self.process.waitForStarted()
def get_uno_desktop(self): def get_uno_desktop(self):
"""
On non-Windows platforms, use Uno. Get the OpenOffice desktop
which will be used to manage impress
"""
log.debug(u'get UNO Desktop Openoffice') log.debug(u'get UNO Desktop Openoffice')
ctx = None ctx = None
loop = 0 loop = 0
@ -134,10 +137,19 @@ class ImpressController(PresentationController):
return None return None
def get_com_desktop(self): def get_com_desktop(self):
"""
On Windows platforms, use COM. Return the desktop object which
will be used to manage Impress
"""
log.debug(u'get COM Desktop OpenOffice') log.debug(u'get COM Desktop OpenOffice')
if not self.manager:
return None
return self.manager.createInstance(u'com.sun.star.frame.Desktop') return self.manager.createInstance(u'com.sun.star.frame.Desktop')
def get_com_servicemanager(self): def get_com_servicemanager(self):
"""
Return the OOo service manager for windows
"""
log.debug(u'get_com_servicemanager openoffice') log.debug(u'get_com_servicemanager openoffice')
try: try:
return Dispatch(u'com.sun.star.ServiceManager') return Dispatch(u'com.sun.star.ServiceManager')
@ -171,13 +183,23 @@ class ImpressController(PresentationController):
log.exception(u'Failed to terminate OpenOffice') log.exception(u'Failed to terminate OpenOffice')
def add_doc(self, name): def add_doc(self, name):
"""
Called when a new Impress document is opened
"""
log.debug(u'Add Doc OpenOffice') log.debug(u'Add Doc OpenOffice')
doc = ImpressDocument(self, name) doc = ImpressDocument(self, name)
self.docs.append(doc) self.docs.append(doc)
return doc return doc
class ImpressDocument(PresentationDocument): class ImpressDocument(PresentationDocument):
"""
Class which holds information and controls a single presentation
"""
def __init__(self, controller, presentation): def __init__(self, controller, presentation):
"""
Constructor, store information about the file and initialise
"""
log.debug(u'Init Presentation OpenOffice') log.debug(u'Init Presentation OpenOffice')
PresentationDocument.__init__(self, controller, presentation) PresentationDocument.__init__(self, controller, presentation)
self.document = None self.document = None
@ -208,9 +230,8 @@ class ImpressDocument(PresentationDocument):
desktop = self.controller.get_uno_desktop() desktop = self.controller.get_uno_desktop()
url = uno.systemPathToFileUrl(self.filepath) url = uno.systemPathToFileUrl(self.filepath)
if desktop is None: if desktop is None:
return return False
self.desktop = desktop self.desktop = desktop
#print "s.dsk2 ", self.desktop
properties = [] properties = []
properties.append(self.create_property(u'Minimized', True)) properties.append(self.create_property(u'Minimized', True))
properties = tuple(properties) properties = tuple(properties)
@ -219,12 +240,13 @@ class ImpressDocument(PresentationDocument):
0, properties) 0, properties)
except: except:
log.exception(u'Failed to load presentation') log.exception(u'Failed to load presentation')
return return False
self.presentation = self.document.getPresentation() self.presentation = self.document.getPresentation()
self.presentation.Display = \ self.presentation.Display = \
self.controller.plugin.renderManager.screens.current_display + 1 self.controller.plugin.renderManager.screens.current_display + 1
self.control = None self.control = None
self.create_thumbnails() self.create_thumbnails()
return True
def create_thumbnails(self): def create_thumbnails(self):
""" """
@ -234,30 +256,36 @@ class ImpressDocument(PresentationDocument):
if self.check_thumbnails(): if self.check_thumbnails():
return return
if os.name == u'nt': if os.name == u'nt':
thumbdir = u'file:///' + self.thumbnailpath.replace( thumbdirurl = u'file:///' + self.get_temp_folder().replace(
u'\\', u'/').replace(u':', u'|').replace(u' ', u'%20') u'\\', u'/').replace(u':', u'|').replace(u' ', u'%20')
else: else:
thumbdir = uno.systemPathToFileUrl(self.thumbnailpath) thumbdirurl = uno.systemPathToFileUrl(self.get_temp_folder())
props = [] props = []
props.append(self.create_property(u'FilterName', u'impress_png_Export')) props.append(self.create_property(u'FilterName', u'impress_png_Export'))
props = tuple(props) props = tuple(props)
doc = self.document doc = self.document
pages = doc.getDrawPages() pages = doc.getDrawPages()
if not os.path.isdir(self.get_temp_folder()):
os.makedirs(self.get_temp_folder())
for idx in range(pages.getCount()): for idx in range(pages.getCount()):
page = pages.getByIndex(idx) page = pages.getByIndex(idx)
doc.getCurrentController().setCurrentPage(page) doc.getCurrentController().setCurrentPage(page)
path = u'%s/%s%s.png' % (thumbdir, self.controller.thumbnailprefix, urlpath = u'%s/%s.png' % (thumbdirurl, unicode(idx + 1))
unicode(idx + 1)) path = os.path.join(self.get_temp_folder(),
unicode(idx + 1) + u'.png')
try: try:
doc.storeToURL(path , props) doc.storeToURL(urlpath, props)
preview = resize_image(path, 640, 480) self.convert_thumbnail(path, idx + 1)
if os.path.exists(path): if os.path.exists(path):
os.remove(path) os.remove(path)
preview.save(path, u'png')
except: except:
log.exception(u'%s - Unable to store openoffice preview' % path) log.exception(u'%s - Unable to store openoffice preview' % path)
def create_property(self, name, value): def create_property(self, name, value):
"""
Create an OOo style property object which are passed into some
Uno methods
"""
log.debug(u'create property OpenOffice') log.debug(u'create property OpenOffice')
if os.name == u'nt': if os.name == u'nt':
prop = self.controller.manager.\ prop = self.controller.manager.\
@ -288,6 +316,9 @@ class ImpressDocument(PresentationDocument):
self.controller.remove_doc(self) self.controller.remove_doc(self)
def is_loaded(self): def is_loaded(self):
"""
Returns true if a presentation is loaded
"""
log.debug(u'is loaded OpenOffice') log.debug(u'is loaded OpenOffice')
#print "is_loaded " #print "is_loaded "
if self.presentation is None or self.document is None: if self.presentation is None or self.document is None:
@ -302,6 +333,9 @@ class ImpressDocument(PresentationDocument):
return True return True
def is_active(self): def is_active(self):
"""
Returns true if a presentation is active and running
"""
log.debug(u'is active OpenOffice') log.debug(u'is active OpenOffice')
#print "is_active " #print "is_active "
if not self.is_loaded(): if not self.is_loaded():
@ -313,10 +347,16 @@ class ImpressDocument(PresentationDocument):
return True return True
def unblank_screen(self): def unblank_screen(self):
"""
Unblanks the screen
"""
log.debug(u'unblank screen OpenOffice') log.debug(u'unblank screen OpenOffice')
return self.control.resume() return self.control.resume()
def blank_screen(self): def blank_screen(self):
"""
Blanks the screen
"""
log.debug(u'blank screen OpenOffice') log.debug(u'blank screen OpenOffice')
self.control.blankScreen(0) self.control.blankScreen(0)
@ -331,6 +371,9 @@ class ImpressDocument(PresentationDocument):
return False return False
def stop_presentation(self): def stop_presentation(self):
"""
Stop the presentation, remove from screen
"""
log.debug(u'stop presentation OpenOffice') log.debug(u'stop presentation OpenOffice')
# deactivate should hide the screen according to docs, but doesn't # deactivate should hide the screen according to docs, but doesn't
#self.control.deactivate() #self.control.deactivate()
@ -338,6 +381,9 @@ class ImpressDocument(PresentationDocument):
self.control = None self.control = None
def start_presentation(self): def start_presentation(self):
"""
Start the presentation from the beginning
"""
log.debug(u'start presentation OpenOffice') log.debug(u'start presentation OpenOffice')
if self.control is None or not self.control.isRunning(): if self.control is None or not self.control.isRunning():
self.presentation.start() self.presentation.start()
@ -354,12 +400,21 @@ class ImpressDocument(PresentationDocument):
self.goto_slide(1) self.goto_slide(1)
def get_slide_number(self): def get_slide_number(self):
"""
Return the current slide number on the screen, from 1
"""
return self.control.getCurrentSlideIndex() + 1 return self.control.getCurrentSlideIndex() + 1
def get_slide_count(self): def get_slide_count(self):
"""
Return the total number of slides
"""
return self.document.getDrawPages().getCount() return self.document.getDrawPages().getCount()
def goto_slide(self, slideno): def goto_slide(self, slideno):
"""
Go to a specific slide (from 1)
"""
self.control.gotoSlideIndex(slideno-1) self.control.gotoSlideIndex(slideno-1)
def next_step(self): def next_step(self):

View File

@ -30,14 +30,17 @@ from PyQt4 import QtCore, QtGui
from openlp.core.lib import MediaManagerItem, BaseListWithDnD, build_icon, \ from openlp.core.lib import MediaManagerItem, BaseListWithDnD, build_icon, \
SettingsManager, translate, check_item_selected SettingsManager, translate, check_item_selected
from openlp.core.utils import AppLocation
from openlp.plugins.presentations.lib import MessageListener from openlp.plugins.presentations.lib import MessageListener
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
# We have to explicitly create separate classes for each plugin
# in order for DnD to the Service manager to work correctly.
class PresentationListView(BaseListWithDnD): class PresentationListView(BaseListWithDnD):
"""
Class for the list of Presentations
We have to explicitly create separate classes for each plugin
in order for DnD to the Service manager to work correctly.
"""
def __init__(self, parent=None): def __init__(self, parent=None):
self.PluginName = u'Presentations' self.PluginName = u'Presentations'
BaseListWithDnD.__init__(self, parent) BaseListWithDnD.__init__(self, parent)
@ -45,11 +48,14 @@ class PresentationListView(BaseListWithDnD):
class PresentationMediaItem(MediaManagerItem): class PresentationMediaItem(MediaManagerItem):
""" """
This is the Presentation media manager item for Presentation Items. This is the Presentation media manager item for Presentation Items.
It can present files using Openoffice It can present files using Openoffice and Powerpoint
""" """
log.info(u'Presentations Media Item loaded') log.info(u'Presentations Media Item loaded')
def __init__(self, parent, icon, title, controllers): def __init__(self, parent, icon, title, controllers):
"""
Constructor. Setup defaults
"""
self.controllers = controllers self.controllers = controllers
self.PluginNameShort = u'Presentation' self.PluginNameShort = u'Presentation'
self.pluginNameVisible = translate('PresentationPlugin.MediaItem', self.pluginNameVisible = translate('PresentationPlugin.MediaItem',
@ -63,6 +69,9 @@ class PresentationMediaItem(MediaManagerItem):
self.message_listener = MessageListener(self) self.message_listener = MessageListener(self)
def retranslateUi(self): def retranslateUi(self):
"""
The name of the plugin media displayed in UI
"""
self.OnNewPrompt = translate('PresentationPlugin.MediaItem', self.OnNewPrompt = translate('PresentationPlugin.MediaItem',
'Select Presentation(s)') 'Select Presentation(s)')
self.Automatic = translate('PresentationPlugin.MediaItem', self.Automatic = translate('PresentationPlugin.MediaItem',
@ -80,12 +89,18 @@ class PresentationMediaItem(MediaManagerItem):
'Presentations (%s)' % fileType) 'Presentations (%s)' % fileType)
def requiredIcons(self): def requiredIcons(self):
"""
Set which icons the media manager tab should show
"""
MediaManagerItem.requiredIcons(self) MediaManagerItem.requiredIcons(self)
self.hasFileIcon = True self.hasFileIcon = True
self.hasNewIcon = False self.hasNewIcon = False
self.hasEditIcon = False self.hasEditIcon = False
def addEndHeaderBar(self): def addEndHeaderBar(self):
"""
Display custom media manager items for presentations
"""
self.PresentationWidget = QtGui.QWidget(self) self.PresentationWidget = QtGui.QWidget(self)
sizePolicy = QtGui.QSizePolicy( sizePolicy = QtGui.QSizePolicy(
QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
@ -109,15 +124,13 @@ class PresentationMediaItem(MediaManagerItem):
self.pageLayout.addWidget(self.PresentationWidget) self.pageLayout.addWidget(self.PresentationWidget)
def initialise(self): def initialise(self):
self.servicePath = os.path.join( """
AppLocation.get_section_data_path(self.settingsSection), Populate the media manager tab
u'thumbnails') """
self.listView.setIconSize(QtCore.QSize(88, 50)) self.listView.setIconSize(QtCore.QSize(88, 50))
if not os.path.exists(self.servicePath):
os.mkdir(self.servicePath)
list = SettingsManager.load_list( list = SettingsManager.load_list(
self.settingsSection, u'presentations') self.settingsSection, u'presentations')
self.loadList(list) self.loadList(list, True)
for item in self.controllers: for item in self.controllers:
#load the drop down selection #load the drop down selection
if self.controllers[item].enabled: if self.controllers[item].enabled:
@ -126,7 +139,12 @@ class PresentationMediaItem(MediaManagerItem):
self.DisplayTypeComboBox.insertItem(0, self.Automatic) self.DisplayTypeComboBox.insertItem(0, self.Automatic)
self.DisplayTypeComboBox.setCurrentIndex(0) self.DisplayTypeComboBox.setCurrentIndex(0)
def loadList(self, list): def loadList(self, list, initialLoad=False):
"""
Add presentations into the media manager
This is called both on initial load of the plugin to populate with
existing files, and when the user adds new files via the media manager
"""
currlist = self.getFileList() currlist = self.getFileList()
titles = [] titles = []
for file in currlist: for file in currlist:
@ -136,40 +154,43 @@ class PresentationMediaItem(MediaManagerItem):
continue continue
filename = os.path.split(unicode(file))[1] filename = os.path.split(unicode(file))[1]
if titles.count(filename) > 0: if titles.count(filename) > 0:
QtGui.QMessageBox.critical( if not initialLoad:
self, translate('PresentationPlugin.MediaItem', QtGui.QMessageBox.critical(
'File exists'), self, translate('PresentationPlugin.MediaItem',
'File exists'),
translate('PresentationPlugin.MediaItem', translate('PresentationPlugin.MediaItem',
'A presentation with that filename already exists.'), 'A presentation with that filename already exists.'),
QtGui.QMessageBox.Ok) QtGui.QMessageBox.Ok)
else: continue
icon = None controller_name = self.findControllerByType(filename)
for controller in self.controllers: if controller_name:
thumbPath = os.path.join( controller = self.controllers[controller_name]
AppLocation.get_section_data_path( doc = controller.add_doc(unicode(file))
self.settingsSection), thumb = os.path.join(doc.get_thumbnail_folder(), u'icon.png')
u'thumbnails', controller, filename) preview = doc.get_thumbnail_path(1, True)
thumb = os.path.join(thumbPath, u'slide1.png') if not preview and not initialLoad:
preview = os.path.join( doc.load_presentation()
AppLocation.get_section_data_path( preview = doc.get_thumbnail_path(1, True)
self.settingsSection), doc.close_presentation()
controller, u'thumbnails', filename, u'slide1.png') if preview and self.validate(preview, thumb):
if os.path.exists(preview): icon = build_icon(thumb)
if os.path.exists(thumb): else:
if self.validate(preview, thumb):
icon = build_icon(thumb)
else:
icon = build_icon(
u':/general/general_delete.png')
else:
os.makedirs(thumbPath)
icon = self.iconFromFile(preview, thumb)
if not icon:
icon = build_icon(u':/general/general_delete.png') icon = build_icon(u':/general/general_delete.png')
item_name = QtGui.QListWidgetItem(filename) else:
item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(file)) if initialLoad:
item_name.setIcon(icon) icon = build_icon(u':/general/general_delete.png')
self.listView.addItem(item_name) else:
QtGui.QMessageBox.critical(
self, translate('PresentationPlugin.MediaItem',
'Unsupported file'),
translate('PresentationPlugin.MediaItem',
'This type of presentation is not supported'),
QtGui.QMessageBox.Ok)
continue
item_name = QtGui.QListWidgetItem(filename)
item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(file))
item_name.setIcon(icon)
self.listView.addItem(item_name)
def onDeleteClick(self): def onDeleteClick(self):
""" """
@ -184,8 +205,6 @@ class PresentationMediaItem(MediaManagerItem):
for item in items: for item in items:
filepath = unicode(item.data( filepath = unicode(item.data(
QtCore.Qt.UserRole).toString()) QtCore.Qt.UserRole).toString())
#not sure of this has errors
#John please can you look at .
for cidx in self.controllers: for cidx in self.controllers:
doc = self.controllers[cidx].add_doc(filepath) doc = self.controllers[cidx].add_doc(filepath)
doc.presentation_deleted() doc.presentation_deleted()
@ -196,6 +215,11 @@ class PresentationMediaItem(MediaManagerItem):
self.settingsSection, self.getFileList()) self.settingsSection, self.getFileList())
def generateSlideData(self, service_item, item=None): def generateSlideData(self, service_item, item=None):
"""
Load the relevant information for displaying the presentation
in the slidecontroller. In the case of powerpoints, an image
for each slide
"""
items = self.listView.selectedIndexes() items = self.listView.selectedIndexes()
if len(items) > 1: if len(items) > 1:
return False return False
@ -213,20 +237,27 @@ class PresentationMediaItem(MediaManagerItem):
controller = self.controllers[service_item.shortname] controller = self.controllers[service_item.shortname]
(path, name) = os.path.split(filename) (path, name) = os.path.split(filename)
doc = controller.add_doc(filename) doc = controller.add_doc(filename)
if doc.get_slide_preview_file(1) is None: if doc.get_thumbnail_path(1, True) is None:
doc.load_presentation() doc.load_presentation()
i = 1 i = 1
img = doc.get_slide_preview_file(i) img = doc.get_thumbnail_path(i, True)
while img: while img:
service_item.add_from_command(path, name, img) service_item.add_from_command(path, name, img)
i = i + 1 i = i + 1
img = doc.get_slide_preview_file(i) img = doc.get_thumbnail_path(i, True)
doc.close_presentation() doc.close_presentation()
return True return True
else: else:
return False return False
def findControllerByType(self, filename): def findControllerByType(self, filename):
"""
Determine the default application controller to use for the selected
file type. This is used if "Automatic" is set as the preferred
controller. Find the first (alphabetic) enabled controller which
"supports" the extension. If none found, then look for a controller
which "alsosupports" it instead.
"""
filetype = os.path.splitext(filename)[1] filetype = os.path.splitext(filename)[1]
if not filetype: if not filetype:
return None return None

View File

@ -41,17 +41,28 @@ class Controller(object):
log.info(u'Controller loaded') log.info(u'Controller loaded')
def __init__(self, live): def __init__(self, live):
"""
Constructor
"""
self.is_live = live self.is_live = live
self.doc = None self.doc = None
log.info(u'%s controller loaded' % live) log.info(u'%s controller loaded' % live)
def add_handler(self, controller, file, is_blank): def add_handler(self, controller, file, is_blank):
"""
Add a handler, which is an instance of a presentation and
slidecontroller combination. If the slidecontroller has a display
then load the presentation.
"""
log.debug(u'Live = %s, add_handler %s' % (self.is_live, file)) log.debug(u'Live = %s, add_handler %s' % (self.is_live, file))
self.controller = controller self.controller = controller
if self.doc is not None: if self.doc is not None:
self.shutdown() self.shutdown()
self.doc = self.controller.add_doc(file) self.doc = self.controller.add_doc(file)
self.doc.load_presentation() if not self.doc.load_presentation():
# Display error message to user
# Inform slidecontroller that the action failed?
return
if self.is_live: if self.is_live:
self.doc.start_presentation() self.doc.start_presentation()
if is_blank: if is_blank:
@ -60,6 +71,10 @@ class Controller(object):
self.doc.slidenumber = 0 self.doc.slidenumber = 0
def activate(self): def activate(self):
"""
Active the presentation, and show it on the screen.
Use the last slide number.
"""
log.debug(u'Live = %s, activate' % self.is_live) log.debug(u'Live = %s, activate' % self.is_live)
if self.doc.is_active(): if self.doc.is_active():
return return
@ -71,6 +86,9 @@ class Controller(object):
self.doc.goto_slide(self.doc.slidenumber) self.doc.goto_slide(self.doc.slidenumber)
def slide(self, slide): def slide(self, slide):
"""
Go to a specific slide
"""
log.debug(u'Live = %s, slide' % self.is_live) log.debug(u'Live = %s, slide' % self.is_live)
if not self.is_live: if not self.is_live:
return return
@ -152,6 +170,9 @@ class Controller(object):
#self.timer.stop() #self.timer.stop()
def blank(self): def blank(self):
"""
Instruct the controller to blank the presentation
"""
log.debug(u'Live = %s, blank' % self.is_live) log.debug(u'Live = %s, blank' % self.is_live)
if not self.is_live: if not self.is_live:
return return
@ -162,6 +183,9 @@ class Controller(object):
self.doc.blank_screen() self.doc.blank_screen()
def stop(self): def stop(self):
"""
Instruct the controller to stop and hide the presentation
"""
log.debug(u'Live = %s, stop' % self.is_live) log.debug(u'Live = %s, stop' % self.is_live)
if not self.is_live: if not self.is_live:
return return
@ -172,6 +196,9 @@ class Controller(object):
self.doc.stop_presentation() self.doc.stop_presentation()
def unblank(self): def unblank(self):
"""
Instruct the controller to unblank the presentation
"""
log.debug(u'Live = %s, unblank' % self.is_live) log.debug(u'Live = %s, unblank' % self.is_live)
if not self.is_live: if not self.is_live:
return return
@ -246,6 +273,9 @@ class MessageListener(object):
controller.add_handler(self.controllers[self.handler], file, is_blank) controller.add_handler(self.controllers[self.handler], file, is_blank)
def slide(self, message): def slide(self, message):
"""
React to the message to move to a specific slide
"""
is_live = message[1] is_live = message[1]
slide = message[2] slide = message[2]
if is_live: if is_live:
@ -254,6 +284,9 @@ class MessageListener(object):
self.preview_handler.slide(slide) self.preview_handler.slide(slide)
def first(self, message): def first(self, message):
"""
React to the message to move to the first slide
"""
is_live = message[1] is_live = message[1]
if is_live: if is_live:
self.live_handler.first() self.live_handler.first()
@ -261,6 +294,9 @@ class MessageListener(object):
self.preview_handler.first() self.preview_handler.first()
def last(self, message): def last(self, message):
"""
React to the message to move to the last slide
"""
is_live = message[1] is_live = message[1]
if is_live: if is_live:
self.live_handler.last() self.live_handler.last()
@ -268,6 +304,9 @@ class MessageListener(object):
self.preview_handler.last() self.preview_handler.last()
def next(self, message): def next(self, message):
"""
React to the message to move to the next animation/slide
"""
is_live = message[1] is_live = message[1]
if is_live: if is_live:
self.live_handler.next() self.live_handler.next()
@ -275,6 +314,9 @@ class MessageListener(object):
self.preview_handler.next() self.preview_handler.next()
def previous(self, message): def previous(self, message):
"""
React to the message to move to the previous animation/slide
"""
is_live = message[1] is_live = message[1]
if is_live: if is_live:
self.live_handler.previous() self.live_handler.previous()
@ -282,6 +324,10 @@ class MessageListener(object):
self.preview_handler.previous() self.preview_handler.previous()
def shutdown(self, message): def shutdown(self, message):
"""
React to message to shutdown the presentation. I.e. end the show
and close the file
"""
is_live = message[1] is_live = message[1]
if is_live: if is_live:
Receiver.send_message(u'maindisplay_show') Receiver.send_message(u'maindisplay_show')
@ -290,19 +336,34 @@ class MessageListener(object):
self.preview_handler.shutdown() self.preview_handler.shutdown()
def hide(self, message): def hide(self, message):
"""
React to the message to show the desktop
"""
is_live = message[1] is_live = message[1]
if is_live: if is_live:
self.live_handler.stop() self.live_handler.stop()
def blank(self, message): def blank(self, message):
"""
React to the message to blank the display
"""
is_live = message[1] is_live = message[1]
if is_live: if is_live:
self.live_handler.blank() self.live_handler.blank()
def unblank(self, message): def unblank(self, message):
"""
React to the message to unblank the display
"""
is_live = message[1] is_live = message[1]
if is_live: if is_live:
self.live_handler.unblank() self.live_handler.unblank()
def timeout(self): def timeout(self):
"""
The presentation may be timed or might be controlled by the
application directly, rather than through OpenLP. Poll occassionally
to check which slide is currently displayed so the slidecontroller
view can be updated
"""
self.live_handler.poll() self.live_handler.poll()

View File

@ -97,13 +97,23 @@ class PowerpointController(PresentationController):
self.process = None self.process = None
def add_doc(self, name): def add_doc(self, name):
"""
Called when a new powerpoint document is opened
"""
log.debug(u'Add Doc PowerPoint') log.debug(u'Add Doc PowerPoint')
doc = PowerpointDocument(self, name) doc = PowerpointDocument(self, name)
self.docs.append(doc) self.docs.append(doc)
return doc return doc
class PowerpointDocument(PresentationDocument): class PowerpointDocument(PresentationDocument):
"""
Class which holds information and controls a single presentation
"""
def __init__(self, controller, presentation): def __init__(self, controller, presentation):
"""
Constructor, store information about the file and initialise
"""
log.debug(u'Init Presentation Powerpoint') log.debug(u'Init Presentation Powerpoint')
PresentationDocument.__init__(self, controller, presentation) PresentationDocument.__init__(self, controller, presentation)
self.presentation = None self.presentation = None
@ -111,22 +121,23 @@ class PowerpointDocument(PresentationDocument):
def load_presentation(self): def load_presentation(self):
""" """
Called when a presentation is added to the SlideController. Called when a presentation is added to the SlideController.
It builds the environment, starts communcations with the background Opens the PowerPoint file using the process created earlier
OpenOffice task started earlier. If OpenOffice is not present is is
started. Once the environment is available the presentation is loaded
and started.
``presentation`` ``presentation``
The file name of the presentations to run. The file name of the presentations to run.
""" """
log.debug(u'LoadPresentation') log.debug(u'LoadPresentation')
if not self.controller.process.Visible: if not self.controller.process or not self.controller.process.Visible:
self.controller.start_process() self.controller.start_process()
self.controller.process.Presentations.Open(self.filepath, False, False, try:
True) self.controller.process.Presentations.Open(self.filepath, False,
False, True)
except pywintypes.com_error:
return False
self.presentation = self.controller.process.Presentations( self.presentation = self.controller.process.Presentations(
self.controller.process.Presentations.Count) self.controller.process.Presentations.Count)
self.create_thumbnails() self.create_thumbnails()
return True
def create_thumbnails(self): def create_thumbnails(self):
""" """
@ -139,8 +150,8 @@ class PowerpointDocument(PresentationDocument):
""" """
if self.check_thumbnails(): if self.check_thumbnails():
return return
self.presentation.Export(os.path.join(self.thumbnailpath, ''), 'png', self.presentation.Export(os.path.join(self.get_thumbnail_folder(), ''),
320, 240) 'png', 320, 240)
def close_presentation(self): def close_presentation(self):
""" """
@ -298,4 +309,4 @@ class PowerpointDocument(PresentationDocument):
shape = shapes(idx + 1) shape = shapes(idx + 1)
if shape.HasTextFrame: if shape.HasTextFrame:
text += shape.TextFrame.TextRange.Text + '\n' text += shape.TextFrame.TextRange.Text + '\n'
return text return text

View File

@ -93,13 +93,22 @@ class PptviewController(PresentationController):
self.docs[0].close_presentation() self.docs[0].close_presentation()
def add_doc(self, name): def add_doc(self, name):
"""
Called when a new powerpoint document is opened
"""
log.debug(u'Add Doc PPTView') log.debug(u'Add Doc PPTView')
doc = PptviewDocument(self, name) doc = PptviewDocument(self, name)
self.docs.append(doc) self.docs.append(doc)
return doc return doc
class PptviewDocument(PresentationDocument): class PptviewDocument(PresentationDocument):
"""
Class which holds information and controls a single presentation
"""
def __init__(self, controller, presentation): def __init__(self, controller, presentation):
"""
Constructor, store information about the file and initialise
"""
log.debug(u'Init Presentation PowerPoint') log.debug(u'Init Presentation PowerPoint')
PresentationDocument.__init__(self, controller, presentation) PresentationDocument.__init__(self, controller, presentation)
self.presentation = None self.presentation = None
@ -117,17 +126,31 @@ class PptviewDocument(PresentationDocument):
The file name of the presentations to run. The file name of the presentations to run.
""" """
log.debug(u'LoadPresentation') log.debug(u'LoadPresentation')
#if self.pptid >= 0:
# self.close_presentation()
rendermanager = self.controller.plugin.renderManager rendermanager = self.controller.plugin.renderManager
rect = rendermanager.screens.current[u'size'] rect = rendermanager.screens.current[u'size']
rect = RECT(rect.x(), rect.y(), rect.right(), rect.bottom()) rect = RECT(rect.x(), rect.y(), rect.right(), rect.bottom())
filepath = str(self.filepath.replace(u'/', u'\\')) filepath = str(self.filepath.replace(u'/', u'\\'))
if not os.path.isdir(self.get_temp_folder()):
os.makedirs(self.get_temp_folder())
self.pptid = self.controller.process.OpenPPT(filepath, None, rect, self.pptid = self.controller.process.OpenPPT(filepath, None, rect,
str(os.path.join(self.thumbnailpath, str(self.get_temp_folder()) + '\\slide')
self.controller.thumbnailprefix))) if self.pptid >= 0:
if self.pptid: self.create_thumbnails()
self.stop_presentation() self.stop_presentation()
return True
else:
return False
def create_thumbnails(self):
"""
PPTviewLib creates large BMP's, but we want small PNG's for consistency.
Convert them here.
"""
if self.check_thumbnails():
return
for idx in range(self.get_slide_count()):
path = u'%s\\slide%s.bmp' % (self.get_temp_folder(), unicode(idx + 1))
self.convert_thumbnail(path, idx + 1)
def close_presentation(self): def close_presentation(self):
""" """
@ -224,17 +247,3 @@ class PptviewDocument(PresentationDocument):
""" """
self.controller.process.PrevStep(self.pptid) self.controller.process.PrevStep(self.pptid)
def get_slide_preview_file(self, slide_no):
"""
Returns an image path containing a preview for the requested slide
``slide_no``
The slide an image is required for, starting at 1
"""
path = os.path.join(self.thumbnailpath,
self.controller.thumbnailprefix + unicode(slide_no) + u'.bmp')
if os.path.isfile(path):
return path
else:
return None

View File

@ -29,7 +29,7 @@ import shutil
from PyQt4 import QtCore from PyQt4 import QtCore
from openlp.core.lib import Receiver from openlp.core.lib import Receiver, resize_image
from openlp.core.utils import AppLocation from openlp.core.utils import AppLocation
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -63,6 +63,13 @@ class PresentationController(object):
``plugin`` ``plugin``
The presentationplugin object The presentationplugin object
``supports``
The primary native file types this application supports
``alsosupports``
Other file types the application can import, although not necessarily
the first choice due to potential incompatibilities
**Hook Functions** **Hook Functions**
``kill()`` ``kill()``
@ -109,12 +116,16 @@ class PresentationController(object):
QtCore.Qt.Checked QtCore.Qt.Checked
else: else:
self.enabled = False self.enabled = False
self.thumbnailroot = os.path.join( self.temp_folder = os.path.join(
AppLocation.get_section_data_path(self.settings_section), name)
self.thumbnail_folder = os.path.join(
AppLocation.get_section_data_path(self.settings_section), AppLocation.get_section_data_path(self.settings_section),
name, u'thumbnails') u'thumbnails')
self.thumbnailprefix = u'slide' self.thumbnail_prefix = u'slide'
if not os.path.isdir(self.thumbnailroot): if not os.path.isdir(self.thumbnail_folder):
os.makedirs(self.thumbnailroot) os.makedirs(self.thumbnail_folder)
if not os.path.isdir(self.temp_folder):
os.makedirs(self.temp_folder)
def check_available(self): def check_available(self):
""" """
@ -208,14 +219,19 @@ class PresentationDocument(object):
``previous_step()`` ``previous_step()``
Triggers the previous slide on the running presentation Triggers the previous slide on the running presentation
``get_slide_preview_file(slide_no)`` ``get_thumbnail_path(slide_no, check_exists)``
Returns a path to an image containing a preview for the requested slide Returns a path to an image containing a preview for the requested slide
""" """
def __init__(self, controller, name): def __init__(self, controller, name):
"""
Constructor for the PresentationController class
"""
self.slidenumber = 0 self.slidenumber = 0
self.controller = controller self.controller = controller
self.store_filename(name) self.filepath = name
if not os.path.isdir(self.get_thumbnail_folder()):
os.mkdir(self.get_thumbnail_folder())
def load_presentation(self): def load_presentation(self):
""" """
@ -224,9 +240,10 @@ class PresentationDocument(object):
``presentation`` ``presentation``
The file name of the presentations to the run. The file name of the presentations to the run.
Returns False if the file could not be opened
""" """
pass return False
def presentation_deleted(self): def presentation_deleted(self):
""" """
@ -234,33 +251,37 @@ class PresentationDocument(object):
a file, e.g. thumbnails a file, e.g. thumbnails
""" """
try: try:
shutil.rmtree(self.thumbnailpath) shutil.rmtree(self.get_thumbnail_folder())
shutil.rmtree(self.get_temp_folder())
except OSError: except OSError:
log.exception(u'Failed to delete presentation controller files') log.exception(u'Failed to delete presentation controller files')
def store_filename(self, presentation): def get_file_name(self):
""" """
Set properties for the filename and thumbnail paths Return just the filename of the presention, without the directory
""" """
self.filepath = presentation return os.path.split(self.filepath)[1]
self.filename = self.get_file_name(presentation)
self.thumbnailpath = self.get_thumbnail_path(presentation)
if not os.path.isdir(self.thumbnailpath):
os.mkdir(self.thumbnailpath)
def get_file_name(self, presentation): def get_thumbnail_folder(self):
return os.path.split(presentation)[1] """
The location where thumbnail images will be stored
def get_thumbnail_path(self, presentation): """
return os.path.join( return os.path.join(
self.controller.thumbnailroot, self.get_file_name(presentation)) self.controller.thumbnail_folder, self.get_file_name())
def get_temp_folder(self):
"""
The location where thumbnail images will be stored
"""
return os.path.join(
self.controller.temp_folder, self.get_file_name())
def check_thumbnails(self): def check_thumbnails(self):
""" """
Returns true if the thumbnail images look to exist and are more Returns true if the thumbnail images look to exist and are more
recent than the powerpoint recent than the powerpoint
""" """
lastimage = self.get_slide_preview_file(self.get_slide_count()) lastimage = self.get_thumbnail_path(self.get_slide_count(), True)
if not (lastimage and os.path.isfile(lastimage)): if not (lastimage and os.path.isfile(lastimage)):
return False return False
imgdate = os.stat(lastimage).st_mtime imgdate = os.stat(lastimage).st_mtime
@ -350,16 +371,27 @@ class PresentationDocument(object):
""" """
pass pass
def get_slide_preview_file(self, slide_no): def convert_thumbnail(self, file, idx):
"""
Convert the slide image the application made to a standard 320x240
.png image.
"""
if self.check_thumbnails():
return
if os.path.isfile(file):
img = resize_image(file, 320, 240)
img.save(self.get_thumbnail_path(idx, False))
def get_thumbnail_path(self, slide_no, check_exists):
""" """
Returns an image path containing a preview for the requested slide Returns an image path containing a preview for the requested slide
``slide_no`` ``slide_no``
The slide an image is required for, starting at 1 The slide an image is required for, starting at 1
""" """
path = os.path.join(self.thumbnailpath, path = os.path.join(self.get_thumbnail_folder(),
self.controller.thumbnailprefix + unicode(slide_no) + u'.png') self.controller.thumbnail_prefix + unicode(slide_no) + u'.png')
if os.path.isfile(path): if os.path.isfile(path) or not check_exists:
return path return path
else: else:
return None return None

View File

@ -32,10 +32,16 @@ class PresentationTab(SettingsTab):
PresentationsTab is the Presentations settings tab in the settings dialog. PresentationsTab is the Presentations settings tab in the settings dialog.
""" """
def __init__(self, title, controllers): def __init__(self, title, controllers):
"""
Constructor
"""
self.controllers = controllers self.controllers = controllers
SettingsTab.__init__(self, title) SettingsTab.__init__(self, title)
def setupUi(self): def setupUi(self):
"""
Create the controls for the settings tab
"""
self.setObjectName(u'PresentationTab') self.setObjectName(u'PresentationTab')
self.tabTitleVisible = translate('PresentationPlugin.PresentationTab', self.tabTitleVisible = translate('PresentationPlugin.PresentationTab',
'Presentations') 'Presentations')
@ -89,6 +95,9 @@ class PresentationTab(SettingsTab):
self.PresentationLayout.addWidget(self.PresentationRightWidget) self.PresentationLayout.addWidget(self.PresentationRightWidget)
def retranslateUi(self): def retranslateUi(self):
"""
Make any translation changes
"""
self.VerseDisplayGroupBox.setTitle( self.VerseDisplayGroupBox.setTitle(
translate('PresentationPlugin.PresentationTab', translate('PresentationPlugin.PresentationTab',
'Available Controllers')) 'Available Controllers'))
@ -100,6 +109,9 @@ class PresentationTab(SettingsTab):
translate('PresentationPlugin.PresentationTab', 'available'))) translate('PresentationPlugin.PresentationTab', 'available')))
def load(self): def load(self):
"""
Load the settings.
"""
for key in self.controllers: for key in self.controllers:
controller = self.controllers[key] controller = self.controllers[key]
if controller.available: if controller.available:
@ -109,6 +121,9 @@ class PresentationTab(SettingsTab):
QtCore.QVariant(0)).toInt()[0]) QtCore.QVariant(0)).toInt()[0])
def save(self): def save(self):
"""
Save the settings.
"""
for key in self.controllers: for key in self.controllers:
controller = self.controllers[key] controller = self.controllers[key]
checkbox = self.PresenterCheckboxes[controller.name] checkbox = self.PresenterCheckboxes[controller.name]

View File

@ -33,9 +33,17 @@ from openlp.plugins.presentations.lib import *
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
class PresentationPlugin(Plugin): class PresentationPlugin(Plugin):
"""
This plugin allowed a Presentation to be opened, controlled and displayed
on the output display. The plugin controls third party applications such
as OpenOffice.org Impress, Microsoft PowerPoint and the PowerPoint viewer
"""
log = logging.getLogger(u'PresentationPlugin') log = logging.getLogger(u'PresentationPlugin')
def __init__(self, plugin_helpers): def __init__(self, plugin_helpers):
"""
PluginPresentation constructor.
"""
log.debug(u'Initialised') log.debug(u'Initialised')
self.controllers = {} self.controllers = {}
Plugin.__init__(self, u'Presentations', u'1.9.2', plugin_helpers) Plugin.__init__(self, u'Presentations', u'1.9.2', plugin_helpers)
@ -51,6 +59,10 @@ class PresentationPlugin(Plugin):
return PresentationTab(self.name, self.controllers) return PresentationTab(self.name, self.controllers)
def initialise(self): def initialise(self):
"""
Initialise the plugin. Determine which controllers are enabled
are start their processes.
"""
log.info(u'Presentations Initialising') log.info(u'Presentations Initialising')
Plugin.initialise(self) Plugin.initialise(self)
self.insertToolboxItem() self.insertToolboxItem()
@ -59,6 +71,10 @@ class PresentationPlugin(Plugin):
self.controllers[controller].start_process() self.controllers[controller].start_process()
def finalise(self): def finalise(self):
"""
Finalise the plugin. Ask all the enabled presentation applications
to close down their applications and release resources.
"""
log.info(u'Plugin Finalise') log.info(u'Plugin Finalise')
#Ask each controller to tidy up #Ask each controller to tidy up
for key in self.controllers: for key in self.controllers:
@ -75,6 +91,10 @@ class PresentationPlugin(Plugin):
self, self.icon, self.name, self.controllers) self, self.icon, self.name, self.controllers)
def registerControllers(self, controller): def registerControllers(self, controller):
"""
Register each presentation controller (Impress, PPT etc) and
store for later use
"""
self.controllers[controller.name] = controller self.controllers[controller.name] = controller
def checkPreConditions(self): def checkPreConditions(self):
@ -109,9 +129,13 @@ class PresentationPlugin(Plugin):
return False return False
def about(self): def about(self):
"""
Return information about this plugin
"""
about_text = translate('PresentationPlugin', about_text = translate('PresentationPlugin',
'<b>Presentation Plugin</b> <br> Delivers ' '<b>Presentation Plugin</b> <br> Delivers '
'the ability to show presentations using a number of different ' 'the ability to show presentations using a number of different '
'programs. The choice of available presentation programs is ' 'programs. The choice of available presentation programs is '
'available to the user in a drop down box.') 'available to the user in a drop down box.')
return about_text return about_text

View File

@ -24,6 +24,7 @@
############################################################################### ###############################################################################
from PyQt4 import QtGui, QtCore from PyQt4 import QtGui, QtCore
from sqlalchemy.sql import and_
from openlp.core.lib import translate from openlp.core.lib import translate
from openlp.plugins.songs.forms import AuthorsForm, TopicsForm, SongBookForm from openlp.plugins.songs.forms import AuthorsForm, TopicsForm, SongBookForm
@ -97,6 +98,9 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
QtGui.QMessageBox.critical(self, dlg_title, sel_text) QtGui.QMessageBox.critical(self, dlg_title, sel_text)
def resetAuthors(self): def resetAuthors(self):
"""
Reloads the Authors list.
"""
self.AuthorsListWidget.clear() self.AuthorsListWidget.clear()
authors = self.songmanager.get_all_objects(Author, Author.display_name) authors = self.songmanager.get_all_objects(Author, Author.display_name)
for author in authors: for author in authors:
@ -109,6 +113,9 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
self.AuthorsListWidget.addItem(author_name) self.AuthorsListWidget.addItem(author_name)
def resetTopics(self): def resetTopics(self):
"""
Reloads the Topics list.
"""
self.TopicsListWidget.clear() self.TopicsListWidget.clear()
topics = self.songmanager.get_all_objects(Topic, Topic.name) topics = self.songmanager.get_all_objects(Topic, Topic.name)
for topic in topics: for topic in topics:
@ -117,13 +124,88 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
self.TopicsListWidget.addItem(topic_name) self.TopicsListWidget.addItem(topic_name)
def resetBooks(self): def resetBooks(self):
"""
Reloads the Books list.
"""
self.BooksListWidget.clear() self.BooksListWidget.clear()
books = self.songmanager.get_all_objects(Book, Book.name) books = self.songmanager.get_all_objects(Book, Book.name)
for book in books: for book in books:
book_name = QtGui.QListWidgetItem(book.name) book_name = QtGui.QListWidgetItem(u'%s (%s)' % (book.name,
book.publisher))
book_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(book.id)) book_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(book.id))
self.BooksListWidget.addItem(book_name) self.BooksListWidget.addItem(book_name)
def checkAuthor(self, new_author, edit=False):
"""
Returns False when the given Author is already in the list elsewise
True.
"""
authors = self.songmanager.get_all_objects_filtered(Author,
and_(
Author.first_name == new_author.first_name,
Author.last_name == new_author.last_name,
Author.display_name == new_author.display_name
)
)
if len(authors) > 0:
# If we edit an existing Author, we need to make sure that we do
# not return False when nothing has changed (because this would
# cause an error message later on).
if edit:
if authors[0].id == new_author.id:
return True
else:
return False
else:
return False
else:
return True
def checkTopic(self, new_topic, edit=False):
"""
Returns False when the given Topic is already in the list elsewise True.
"""
topics = self.songmanager.get_all_objects_filtered(Topic,
Topic.name == new_topic.name
)
if len(topics) > 0:
# If we edit an existing Topic, we need to make sure that we do
# not return False when nothing has changed (because this would
# cause an error message later on).
if edit:
if topics[0].id == new_topic.id:
return True
else:
return False
else:
return False
else:
return True
def checkBook(self, new_book, edit=False):
"""
Returns False when the given Book is already in the list elsewise True.
"""
books = self.songmanager.get_all_objects_filtered(Book,
and_(
Book.name == new_book.name,
Book.publisher == new_book.publisher
)
)
if len(books) > 0:
# If we edit an existing Book, we need to make sure that we do
# not return False when nothing has changed (because this would
# cause an error message later on).
if edit:
if books[0].id == new_book.id:
return True
else:
return False
else:
return False
else:
return True
def onAuthorAddButtonClick(self): def onAuthorAddButtonClick(self):
self.authorform.setAutoDisplayName(True) self.authorform.setAutoDisplayName(True)
if self.authorform.exec_(): if self.authorform.exec_():
@ -131,86 +213,93 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
first_name=unicode(self.authorform.FirstNameEdit.text()), first_name=unicode(self.authorform.FirstNameEdit.text()),
last_name=unicode(self.authorform.LastNameEdit.text()), last_name=unicode(self.authorform.LastNameEdit.text()),
display_name=unicode(self.authorform.DisplayEdit.text())) display_name=unicode(self.authorform.DisplayEdit.text()))
if self.songmanager.save_object(author): if self.checkAuthor(author):
self.resetAuthors() if self.songmanager.save_object(author):
self.resetAuthors()
else: else:
QtGui.QMessageBox.critical( QtGui.QMessageBox.critical(self,
self, translate('SongsPlugin.SongMaintenanceForm', translate('SongsPlugin.SongMaintenanceForm', 'Error'),
'Error'),
translate('SongsPlugin.SongMaintenanceForm', translate('SongsPlugin.SongMaintenanceForm',
'Couldn\'t add your author.')) 'Could not add your author.'))
def onTopicAddButtonClick(self): def onTopicAddButtonClick(self):
if self.topicform.exec_(): if self.topicform.exec_():
topic = Topic.populate(name=unicode(self.topicform.NameEdit.text())) topic = Topic.populate(name=unicode(self.topicform.NameEdit.text()))
if self.songmanager.save_object(topic): if self.checkTopic(topic):
self.resetTopics() if self.songmanager.save_object(topic):
self.resetTopics()
else: else:
QtGui.QMessageBox.critical( QtGui.QMessageBox.critical(self,
self, translate('SongsPlugin.SongMaintenanceForm', translate('SongsPlugin.SongMaintenanceForm', 'Error'),
'Error'),
translate('SongsPlugin.SongMaintenanceForm', translate('SongsPlugin.SongMaintenanceForm',
'Couldn\'t add your topic.')) 'Could not add your topic.'))
def onBookAddButtonClick(self): def onBookAddButtonClick(self):
if self.bookform.exec_(): if self.bookform.exec_():
book = Book.populate( book = Book.populate(
name=unicode(self.bookform.NameEdit.text()), name=unicode(self.bookform.NameEdit.text()),
publisher=unicode(self.bookform.PublisherEdit.text())) publisher=unicode(self.bookform.PublisherEdit.text()))
if self.songmanager.save_object(book): if self.checkBook(book):
self.resetBooks() if self.songmanager.save_object(book):
self.resetBooks()
else: else:
QtGui.QMessageBox.critical( QtGui.QMessageBox.critical(self,
self, translate('SongsPlugin.SongMaintenanceForm', translate('SongsPlugin.SongMaintenanceForm', 'Error'),
'Error'),
translate('SongsPlugin.SongMaintenanceForm', translate('SongsPlugin.SongMaintenanceForm',
'Couldn\'t add your book.')) 'Could not add your book.'))
def onAuthorEditButtonClick(self): def onAuthorEditButtonClick(self):
author_id = self._getCurrentItemId(self.AuthorsListWidget) author_id = self._getCurrentItemId(self.AuthorsListWidget)
if author_id != -1: if author_id != -1:
author = self.songmanager.get_object(Author, author_id) author = self.songmanager.get_object(Author, author_id)
# Just make sure none of the fields is None
if author.first_name is None:
author.first_name = u''
if author.last_name is None:
author.last_name = u''
if author.display_name is None:
author.display_name = u''
self.authorform.setAutoDisplayName(False) self.authorform.setAutoDisplayName(False)
self.authorform.FirstNameEdit.setText(author.first_name) self.authorform.FirstNameEdit.setText(author.first_name)
self.authorform.LastNameEdit.setText(author.last_name) self.authorform.LastNameEdit.setText(author.last_name)
self.authorform.DisplayEdit.setText(author.display_name) self.authorform.DisplayEdit.setText(author.display_name)
# Save the author's first and last name as well as the display name
# for the case that they have to be restored.
temp_first_name = author.first_name
temp_last_name = author.last_name
temp_display_name = author.display_name
if self.authorform.exec_(False): if self.authorform.exec_(False):
author.first_name = unicode( author.first_name = unicode(
self.authorform.FirstNameEdit.text()) self.authorform.FirstNameEdit.text())
author.last_name = unicode(self.authorform.LastNameEdit.text()) author.last_name = unicode(self.authorform.LastNameEdit.text())
author.display_name = unicode( author.display_name = unicode(
self.authorform.DisplayEdit.text()) self.authorform.DisplayEdit.text())
if self.songmanager.save_object(author): if self.checkAuthor(author, True):
self.resetAuthors() if self.songmanager.save_object(author):
self.resetAuthors()
else: else:
QtGui.QMessageBox.critical( # We restore the author's old first and last name as well as
self, translate('SongsPlugin.SongMaintenanceForm', # his display name.
'Error'), author.first_name = temp_first_name
author.last_name = temp_last_name
author.display_name = temp_display_name
QtGui.QMessageBox.critical(self,
translate('SongsPlugin.SongMaintenanceForm', 'Error'),
translate('SongsPlugin.SongMaintenanceForm', translate('SongsPlugin.SongMaintenanceForm',
'Couldn\'t save your author.')) 'Could not save your author.'))
def onTopicEditButtonClick(self): def onTopicEditButtonClick(self):
topic_id = self._getCurrentItemId(self.TopicsListWidget) topic_id = self._getCurrentItemId(self.TopicsListWidget)
if topic_id != -1: if topic_id != -1:
topic = self.songmanager.get_object(Topic, topic_id) topic = self.songmanager.get_object(Topic, topic_id)
self.topicform.NameEdit.setText(topic.name) self.topicform.NameEdit.setText(topic.name)
# Save the topic's name for the case that he has to be restored.
temp_name = topic.name
if self.topicform.exec_(False): if self.topicform.exec_(False):
topic.name = unicode(self.topicform.NameEdit.text()) topic.name = unicode(self.topicform.NameEdit.text())
if self.songmanager.save_object(topic): if self.checkTopic(topic, True):
self.resetTopics() if self.songmanager.save_object(topic):
self.resetTopics()
else: else:
QtGui.QMessageBox.critical( # We restore the topics's old name.
self, translate('SongsPlugin.SongMaintenanceForm', topic.name = temp_name
'Error'), QtGui.QMessageBox.critical(self,
translate('SongsPlugin.SongMaintenanceForm', 'Error'),
translate('SongsPlugin.SongMaintenanceForm', translate('SongsPlugin.SongMaintenanceForm',
'Couldn\'t save your topic.')) 'Could not save your topic.'))
def onBookEditButtonClick(self): def onBookEditButtonClick(self):
book_id = self._getCurrentItemId(self.BooksListWidget) book_id = self._getCurrentItemId(self.BooksListWidget)
@ -218,17 +307,24 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
book = self.songmanager.get_object(Book, book_id) book = self.songmanager.get_object(Book, book_id)
self.bookform.NameEdit.setText(book.name) self.bookform.NameEdit.setText(book.name)
self.bookform.PublisherEdit.setText(book.publisher) self.bookform.PublisherEdit.setText(book.publisher)
# Save the book's name and publisher for the case that they have to
# be restored.
temp_name = book.name
temp_publisher = book.publisher
if self.bookform.exec_(False): if self.bookform.exec_(False):
book.name = unicode(self.bookform.NameEdit.text()) book.name = unicode(self.bookform.NameEdit.text())
book.publisher = unicode(self.bookform.PublisherEdit.text()) book.publisher = unicode(self.bookform.PublisherEdit.text())
if self.songmanager.save_object(book): if self.checkBook(book, True):
self.resetBooks() if self.songmanager.save_object(book):
self.resetBooks()
else: else:
QtGui.QMessageBox.critical( # We restore the book's old name and publisher.
self, translate('SongsPlugin.SongMaintenanceForm', book.name = temp_name
'Error'), book.publisher = temp_publisher
QtGui.QMessageBox.critical(self,
translate('SongsPlugin.SongMaintenanceForm', 'Error'),
translate('SongsPlugin.SongMaintenanceForm', translate('SongsPlugin.SongMaintenanceForm',
'Couldn\'t save your book.')) 'Could not save your book.'))
def onAuthorDeleteButtonClick(self): def onAuthorDeleteButtonClick(self):
""" """
@ -236,13 +332,12 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
""" """
self._deleteItem(Author, self.AuthorsListWidget, self.resetAuthors, self._deleteItem(Author, self.AuthorsListWidget, self.resetAuthors,
translate('SongsPlugin.SongMaintenanceForm', 'Delete Author'), translate('SongsPlugin.SongMaintenanceForm', 'Delete Author'),
translate('SongsPlugin.SongMaintenanceForm', translate('SongsPlugin.SongMaintenanceForm',
'Are you sure you want to delete the selected author?'), 'Are you sure you want to delete the selected author?'),
translate('SongsPlugin.SongMaintenanceForm', translate('SongsPlugin.SongMaintenanceForm',
'This author can\'t be deleted, they are currently ' 'This author cannot be deleted, they are currently '
'assigned to at least one song.'), 'assigned to at least one song.'),
translate('SongsPlugin.SongMaintenanceForm', translate('SongsPlugin.SongMaintenanceForm', 'No author selected!'))
'No author selected!'))
def onTopicDeleteButtonClick(self): def onTopicDeleteButtonClick(self):
""" """
@ -250,13 +345,12 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
""" """
self._deleteItem(Topic, self.TopicsListWidget, self.resetTopics, self._deleteItem(Topic, self.TopicsListWidget, self.resetTopics,
translate('SongsPlugin.SongMaintenanceForm', 'Delete Topic'), translate('SongsPlugin.SongMaintenanceForm', 'Delete Topic'),
translate('SongsPlugin.SongMaintenanceForm',
'Are you sure you want to delete the selected topic?'),
translate('SongsPlugin.SongMaintenanceForm',
'This topic can\'t be deleted, it is currently '
'assigned to at least one song.'),
translate('SongsPlugin.SongMaintenanceForm', translate('SongsPlugin.SongMaintenanceForm',
'No topic selected!')) 'Are you sure you want to delete the selected topic?'),
translate('SongsPlugin.SongMaintenanceForm',
'This topic cannot be deleted, it is currently '
'assigned to at least one song.'),
translate('SongsPlugin.SongMaintenanceForm', 'No topic selected!'))
def onBookDeleteButtonClick(self): def onBookDeleteButtonClick(self):
""" """
@ -266,7 +360,7 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
translate('SongsPlugin.SongMaintenanceForm', 'Delete Book'), translate('SongsPlugin.SongMaintenanceForm', 'Delete Book'),
translate('SongsPlugin.SongMaintenanceForm', translate('SongsPlugin.SongMaintenanceForm',
'Are you sure you want to delete the selected book?'), 'Are you sure you want to delete the selected book?'),
translate('SongsPlugin.SongMaintenanceForm', translate('SongsPlugin.SongMaintenanceForm',
'This book can\'t be deleted, it is currently ' 'This book cannot be deleted, it is currently '
'assigned to at least one song.'), 'assigned to at least one song.'),
translate('SongsPlugin.SongMaintenanceForm', u'No book selected!')) translate('SongsPlugin.SongMaintenanceForm', 'No book selected!'))

View File

@ -192,8 +192,30 @@ class SongsPlugin(Plugin):
'This plugin allows songs to be managed and displayed.') 'This plugin allows songs to be managed and displayed.')
return about_text return about_text
def canDeleteTheme(self, theme): def usesTheme(self, theme):
if not self.manager.get_all_objects_filtered(Song, """
Called to find out if the song plugin is currently using a theme.
Returns True if the theme is being used, otherwise returns False.
"""
if self.manager.get_all_objects_filtered(Song,
Song.theme_name == theme): Song.theme_name == theme):
return True return True
return False return False
def renameTheme(self, oldTheme, newTheme):
"""
Renames a theme the song plugin is using making the plugin use the new
name.
``oldTheme``
The name of the theme the plugin should stop using.
``newTheme``
The new name the plugin should now use.
"""
songsUsingTheme = self.manager.get_all_objects_filtered(Song,
Song.theme_name == oldTheme)
for song in songsUsingTheme:
song.theme_name = newTheme
self.custommanager.save_object(song)