Skip to content

Commit

Permalink
Implement a session-level preferences dialog
Browse files Browse the repository at this point in the history
Accessible as the first item on the "Tools" menu.
  • Loading branch information
s0600204 committed Nov 28, 2018
1 parent bcfc55e commit 354fb4f
Show file tree
Hide file tree
Showing 6 changed files with 189 additions and 2 deletions.
3 changes: 3 additions & 0 deletions lisp/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,9 @@ def _load_from_file(self, session_file):
self.__session.update_properties(session_dict["session"])
self.__session.session_file = session_file

for plugin_name, plugin_config in session_dict['session']['plugins'].items():
self.__session.set_plugin_session_config(plugin_name, plugin_config)

# Load cues
for cues_dict in session_dict.get("cues", {}):
cue_type = cues_dict.pop("_type_", "Undefined")
Expand Down
34 changes: 33 additions & 1 deletion lisp/core/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@
# You should have received a copy of the GNU General Public License
# along with Linux Show Player. If not, see <http://www.gnu.org/licenses/>.

from lisp.core.configuration import DummyConfiguration
import inspect
from os import path

from lisp.core.configuration import DummyConfiguration, JSONFileConfiguration


# TODO: add possible additional metadata (Icon, Version, ...)
Expand All @@ -42,3 +45,32 @@ def app(self):

def finalize(self):
"""Called when the application is getting closed."""

@property
def SessionConfig(self):
"""Returns the plugin's session-specific config.
Fallsback to a "session.json" file (much like the app-level config "default.json" file)
or, if that doesn't exist, returns the plugin's app-level config file.
This second fallback is for plugins that might wish to set defaults on an app-level,
but also permit the user to override those defaults in/for specific sessions.
"""
plugin_name = self.__module__.split('.')[-1]
if plugin_name in self.__app.session.plugins:
return self.__app.session.plugins[plugin_name]

plugin_path = path.join(
path.split(inspect.getfile(self.__class__))[0],
'session.json')
if path.exists(plugin_path):
return JSONFileConfiguration._read_json(plugin_path)

return self.Config

def WriteSessionConfig(self, config):
"""Writes the plugin's session-specific config to the active session.
When the user saves the showfile, it will then be written to disk.
"""
self.__app.session.set_plugin_session_config(self.__module__.split('.')[-1], config)
4 changes: 4 additions & 0 deletions lisp/core/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class Session(HasInstanceProperties):
layout = Property(default={})

session_file = Property(default="")
plugins = Property(default={})

def __init__(self, layout):
super().__init__()
Expand Down Expand Up @@ -60,6 +61,9 @@ def abs_path(self, rel_path):

return rel_path

def set_plugin_session_config(self, plugin_name, plugin_session_config):
self.plugins[plugin_name] = plugin_session_config

def rel_path(self, abs_path):
"""Return a relative (to the session-file) version of the given path."""
return os.path.relpath(abs_path, start=self.path())
Expand Down
2 changes: 1 addition & 1 deletion lisp/plugins/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def load_plugins(application):
else:
default_config_path = FALLBACK_CONFIG_PATH

# Load plugin configuration
# Load plugin (app-level) configuration
config = JSONFileConfiguration(
path.join(USER_DIR, mod_name + ".json"), default_config_path
)
Expand Down
12 changes: 12 additions & 0 deletions lisp/ui/mainwindow.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
from lisp.ui.logging.status import LogStatusView
from lisp.ui.logging.viewer import LogViewer
from lisp.ui.settings.app_configuration import AppConfigurationDialog
from lisp.ui.settings.session_configuration import SessionConfigurationDialog
from lisp.ui.ui_utils import translate


Expand Down Expand Up @@ -150,6 +151,12 @@ def __init__(self, conf, title="Linux Show Player", **kwargs):
self.menuEdit.addSeparator()
self.menuEdit.addAction(self.multiEdit)

# menuTools
self.sessionPreferences = QAction(self)
self.sessionPreferences.triggered.connect(self._show_session_preferences)
self.menuTools.addAction(self.sessionPreferences)
self.menuTools.addSeparator()

# menuAbout
self.actionAbout = QAction(self)
self.actionAbout.triggered.connect(self._show_about)
Expand Down Expand Up @@ -225,6 +232,7 @@ def retranslateUi(self):
# menuTools
self.menuTools.setTitle(translate("MainWindow", "&Tools"))
self.multiEdit.setText(translate("MainWindow", "Edit selection"))
self.sessionPreferences.setText(translate("MainWindow", "Session Preferences"))
# menuAbout
self.menuAbout.setTitle(translate("MainWindow", "&About"))
self.actionAbout.setText(translate("MainWindow", "About"))
Expand Down Expand Up @@ -310,6 +318,10 @@ def _show_preferences(self):
prefUi = AppConfigurationDialog(parent=self)
prefUi.exec_()

def _show_session_preferences(self):
prefUi = SessionConfigurationDialog(parent=self)
prefUi.exec_()

def _load_from_file(self):
if self._check_saved():
path, _ = QFileDialog.getOpenFileName(
Expand Down
136 changes: 136 additions & 0 deletions lisp/ui/settings/session_configuration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
# -*- coding: utf-8 -*-
#
# This file is part of Linux Show Player
#
# Copyright 2012-2016 Francesco Ceruti <[email protected]>
#
# Linux Show Player 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, either version 3 of the License, or
# (at your option) any later version.
#
# Linux Show Player 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 Linux Show Player. If not, see <http://www.gnu.org/licenses/>.

import logging
from collections import namedtuple

from PyQt5 import QtCore
from PyQt5.QtCore import QModelIndex
from PyQt5.QtWidgets import QVBoxLayout, QDialogButtonBox, QDialog

from lisp.core.dicttree import DictNode
from lisp.core.util import typename
from lisp.ui.settings.pages import ConfigurationPage, TreeMultiConfigurationWidget
from lisp.ui.settings.pages_tree_model import PagesTreeModel
from lisp.ui.ui_utils import translate

logger = logging.getLogger(__name__)

PageEntry = namedtuple('PageEntry', ('page', 'plugin'))


class SessionConfigurationDialog(QDialog):
PagesRegistry = DictNode()

def __init__(self, **kwargs):
super().__init__(**kwargs)
self.setWindowTitle(translate('SessionConfiguration', 'Session preferences'))
self.setWindowModality(QtCore.Qt.WindowModal)
self.setMaximumSize(800, 510)
self.setMinimumSize(800, 510)
self.resize(800, 510)
self.setLayout(QVBoxLayout())

self.model = PagesTreeModel()
for r_node in SessionConfigurationDialog.PagesRegistry.children:
self._populateModel(QModelIndex(), r_node)

self.mainPage = TreeMultiConfigurationWidget(self.model)
if len(self.PagesRegistry.children):
self.mainPage.selectFirst()
self.layout().addWidget(self.mainPage)

self.dialogButtons = QDialogButtonBox(self)
self.dialogButtons.setStandardButtons(
QDialogButtonBox.Cancel |
QDialogButtonBox.Apply |
QDialogButtonBox.Ok
)
self.layout().addWidget(self.dialogButtons)

self.dialogButtons.button(QDialogButtonBox.Cancel).clicked.connect(
self.reject)
self.dialogButtons.button(QDialogButtonBox.Apply).clicked.connect(
self.applySettings)
self.dialogButtons.button(QDialogButtonBox.Ok).clicked.connect(
self.__onOk)

def applySettings(self):
self.mainPage.applySettings()

def _populateModel(self, m_parent, r_parent):
if r_parent.value is not None:
page = r_parent.value.page
config = r_parent.value.plugin.SessionConfig
else:
page = None
config = None

try:
if page is None:
# The current node have no page, use the parent model-index
# as parent for it's children
mod_index = m_parent
elif issubclass(page, ConfigurationPage):
mod_index = self.model.addPage(page(config), parent=m_parent)
else:
mod_index = self.model.addPage(page(), parent=m_parent)
except Exception:
if not isinstance(page, type):
page_name = 'NoPage'
elif issubclass(page, ConfigurationPage):
page_name = page.Name
else:
page_name = page.__name__

logger.warning(
'Cannot load configuration page: "{}" ({})'.format(
page_name, r_parent.path()), exc_info=True)
else:
for r_node in r_parent.children:
self._populateModel(mod_index, r_node)

def __onOk(self):
self.applySettings()
self.accept()

@staticmethod
def registerSettingsPage(path, page, plugin):
"""
:param path: indicate the page "position": 'category.sub.key'
:type path: str
:type page: Type[lisp.ui.settings.settings_page.ConfigurationPage]
#:type config: lisp.core.configuration.Configuration
"""
if issubclass(page, ConfigurationPage):
SessionConfigurationDialog.PagesRegistry.set(
path, PageEntry(page=page, plugin=plugin))
else:
raise TypeError(
'SessionConfiguration pages must be ConfigurationPage(s), not {}'
.format(typename(page))
)

@staticmethod
def unregisterSettingsPage(path):
"""
:param path: indicate the page "position": 'category.sub.key'
:type path: str
"""
SessionConfigurationDialog.PagesRegistry.pop(path)

0 comments on commit 354fb4f

Please sign in to comment.