Exemplo n.º 1
0
    def add_uri_audio_media_cue():
        """Add audio MediaCue(s) form user-selected files"""

        if get_backend() is None:
            QMessageBox.critical(MainWindow(), 'Error', 'Backend not loaded')
            return

        # Default path to system "music" folder
        path = QStandardPaths.writableLocation(QStandardPaths.MusicLocation)

        # Get the backend extensions and create a filter for the Qt file-dialog
        extensions = get_backend().supported_extensions()
        filters = qfile_filters(extensions, anyfile=False)
        # Display a file-dialog for the user to choose the media-files
        files, _ = QFileDialog.getOpenFileNames(
            MainWindow(), translate('MediaCueMenus', 'Select media files'),
            path, filters)

        QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))

        # Create media cues, and add them to the Application cue_model
        for file in files:
            cue = CueFactory.create_cue('URIAudioCue', uri='file://' + file)
            # Use the filename without extension as cue name
            cue.name = os.path.splitext(os.path.basename(file))[0]
            Application().cue_model.add(cue)

        QApplication.restoreOverrideCursor()
Exemplo n.º 2
0
    def __init__(self):
        super().__init__()

        if not os.path.exists(PRESETS_DIR):
            os.makedirs(PRESETS_DIR, exist_ok=True)

        # Entry in mainWindow menu
        self.manageAction = QAction(MainWindow())
        self.manageAction.triggered.connect(self.__edit_presets)
        self.manageAction.setText(translate('Presets', 'Presets'))

        self.menu_action = MainWindow().menuTools.addAction(self.manageAction)

        self.loadOnCueAction = QAction(None)
        self.loadOnCueAction.triggered.connect(self.__load_on_cue)
        self.loadOnCueAction.setText(translate('Presets', 'Load preset'))

        self.createFromCueAction = QAction(None)
        self.createFromCueAction.triggered.connect(self.__create_from_cue)
        self.createFromCueAction.setText(translate('Presets',
                                                   'Save as preset'))

        CueLayout.cm_registry.add_separator()
        CueLayout.cm_registry.add_item(self.loadOnCueAction)
        CueLayout.cm_registry.add_item(self.createFromCueAction)
        CueLayout.cm_registry.add_separator()
Exemplo n.º 3
0
def select_preset_dialog():
    try:
        presets = tuple(sorted(scan_presets(), key=natural_keys))

        if presets:
            item, confirm = QInputDialog.getItem(
                MainWindow(), translate('Presets', 'Select Preset'), '',
                presets)

            if confirm:
                return item
    except OSError as e:
        scan_presets_error(e, parent=MainWindow())
Exemplo n.º 4
0
    def show_info(self, clicked):
        media_uri = Application().layout.get_context_cue().media.input_uri()
        if not media_uri:
            QMessageBox.critical(MainWindow(), translate('MediaInfo', 'Error'),
                                 translate('MediaInfo', 'No info to display'))
        else:
            gst_info = gst_uri_metadata(media_uri)
            info = {'URI': unquote(gst_info.get_uri())}

            # Audio streams info
            for stream in gst_info.get_audio_streams():
                name = stream.get_stream_type_nick().capitalize()
                info[name] = {
                    'Bitrate': str(stream.get_bitrate() // 1000) + ' Kb/s',
                    'Channels': str(stream.get_channels()),
                    'Sample rate': str(stream.get_sample_rate()) + ' Hz',
                    'Sample size': str(stream.get_depth()) + ' bit'
                }

            # Video streams info
            for stream in gst_info.get_video_streams():
                name = stream.get_stream_type_nick().capitalize()
                info[name] = {
                    'Height':
                    str(stream.get_height()) + ' px',
                    'Width':
                    str(stream.get_width()) + ' px',
                    'Framerate':
                    str(
                        round(stream.get_framerate_num() /
                              stream.get_framerate_denom()))
                }

            # Media tags
            info['Tags'] = {}

            tags = gst_info.get_tags()
            if tags is not None:
                tags = gst_parse_tags_list(tags)
                for tag in tags:
                    if type(tags[tag]).__str__ is not object.__str__:
                        info['Tags'][tag.capitalize()] = str(tags[tag])

            if not info['Tags']:
                info.pop('Tags')

            # Show the dialog
            dialog = InfoDialog(MainWindow(), info,
                                Application().layout.get_context_cue().name)
            dialog.exec_()
Exemplo n.º 5
0
    def __init__(self):
        QtGui.QIcon.setThemeSearchPaths(styles.QLiSPIconsThemePaths)
        QtGui.QIcon.setThemeName(styles.QLiSPIconsThemeName)

        self.mainWindow = MainWindow()
        self.collector = Collector()
        self.collectorUi = None
        self.programConf = {}
        try:
            midi.initialize()
            self.midiHandler = midi.MidiEventHandler(int(cfg.config['MIDI']['DeviceID']))
            self.midiHandler.start()
        except:
            self.midiHandler = None
            QtGui.QMessageBox.critical(self.mainWindow, 'Error', 'MIDI event handler cannot be started!')

        self.mainWindow.newProgramAction.connect(self.newProgram)
        self.mainWindow.saveProgramAction.connect(self.saveProgram)
        self.mainWindow.openProgramAction.connect(self.openProgram)
        self.mainWindow.addMediaAction.connect(self.addMedia, QtCore.Qt.QueuedConnection)

        if(cfg.config['Layout']['UseDefault'] == 'True'):
            self.createLayout(cfg.config['Layout']['Default'])
        else:
            self.mainWindow.setEnabled(False)
            while(not self.layoutDialog()):
                pass
            self.mainWindow.setEnabled(True)

        try:
            self.plugins = plugin.initPlugins(self)
        except Exception as e:
            QtGui.QMessageBox.critical(None, 'Error Message', ' '.join([str(i) for i in e.args]))
Exemplo n.º 6
0
 def show_ip(self):
     ip = translate('Synchronizer', 'Your IP is:') + ' '
     try:
         ip += socket.gethostbyname(socket.gethostname())
     except OSError:
         ip = '127.0.0.1'
     QMessageBox.information(MainWindow(), ' ', ip)
Exemplo n.º 7
0
    def gain(self):
        gainUi = GainUi(MainWindow())
        gainUi.exec_()

        if gainUi.result() == QDialog.Accepted:

            files = {}
            if gainUi.only_selected():
                cues = Application().layout.get_selected_cues(MediaCue)
            else:
                cues = Application().cue_model.filter(MediaCue)

            for cue in cues:
                media = cue.media
                uri = media.input_uri()
                if uri is not None:
                    if uri not in files:
                        files[uri] = [media]
                    else:
                        files[uri].append(media)

            # Gain (main) thread
            self._gain_thread = GainMainThread(files, gainUi.threads(),
                                               gainUi.mode(),
                                               gainUi.ref_level(),
                                               gainUi.norm_level())

            # Progress dialog
            self._progress = GainProgressDialog(len(files))
            self._gain_thread.on_progress.connect(self._progress.on_progress,
                                                  mode=Connection.QtQueued)

            self._progress.show()
            self._gain_thread.start()
Exemplo n.º 8
0
    def finalize(self):
        MainWindow().menuLayout.clear()

        # Disconnect menu-actions signals
        self.edit_action.triggered.disconnect()
        self.remove_action.triggered.disconnect()
        self.select_action.triggered.disconnect()
        self.play_action.triggered.disconnect()
        self.pause_action.triggered.disconnect()
        self.stop_action.triggered.disconnect()
        self.reset_volume_action.disconnect()

        # Remove context-items
        self.cm_registry.remove_item(self.edit_action)
        self.cm_registry.remove_item(self.sep1)
        self.cm_registry.remove_item(self.remove_action)
        self.cm_registry.remove_item(self.select_action)
        self.cm_registry.remove_item(self.sep2)
        self.cm_registry.remove_item(self.play_action)
        self.cm_registry.remove_item(self.pause_action)
        self.cm_registry.remove_item(self.stop_action)
        self.cm_registry.remove_item(self.reset_volume_action)

        # Delete the layout
        self.deleteLater()
Exemplo n.º 9
0
    def __init__(self):
        self.syncMenu = QMenu(translate('Synchronizer', 'Synchronization'))
        self.menu_action = MainWindow().menuTools.addMenu(self.syncMenu)

        self.addPeerAction = QAction(
            translate('Synchronizer', 'Manage connected peers'), MainWindow())
        self.addPeerAction.triggered.connect(self.manage_peers)
        self.syncMenu.addAction(self.addPeerAction)

        self.showIpAction = QAction(
            translate('Synchronizer', 'Show your IP'), MainWindow())
        self.showIpAction.triggered.connect(self.show_ip)
        self.syncMenu.addAction(self.showIpAction)

        self.peers = []
        self.cue_media = {}
Exemplo n.º 10
0
 def __load_on_cue():
     preset_name = select_preset_dialog()
     if preset_name is not None:
         try:
             load_on_cue(preset_name,
                         Application().layout.get_context_cue())
         except OSError as e:
             load_preset_error(e, preset_name, parent=MainWindow())
Exemplo n.º 11
0
def save_preset_dialog(base_name=''):
    name, confirm = QInputDialog.getText(MainWindow(),
                                         translate('Presets', 'Presets'),
                                         translate('Presets', 'Preset name'),
                                         text=base_name)

    if confirm:
        return name
Exemplo n.º 12
0
def check_override_dialog(preset_name):
    answer = QMessageBox.question(MainWindow(),
                                  translate('Presets', 'Presets'),
                                  translate(
                                      'Presets',
                                      'Preset already exists, overwrite?'),
                                  buttons=QMessageBox.Yes | QMessageBox.Cancel)

    return answer == QMessageBox.Yes
Exemplo n.º 13
0
    def edit_cue(self, cue):
        edit_ui = CueSettings(cue, parent=MainWindow())

        def on_apply(settings):
            action = UpdateCueAction(settings, cue)
            MainActionsHandler.do_action(action)

        edit_ui.on_apply.connect(on_apply)
        edit_ui.exec_()
Exemplo n.º 14
0
    def __init__(self):
        self._mainWindow = MainWindow()
        self._app_conf = {}
        self._layout = None
        self._memento_model = None
        self._cue_model = CueModel()

        # Connect mainWindow actions
        self._mainWindow.new_session.connect(self.new_session_dialog)
        self._mainWindow.save_session.connect(self._save_to_file)
        self._mainWindow.open_session.connect(self._load_from_file)

        # Register general settings widget
        AppSettings.register_settings_widget(AppGeneral)
        AppSettings.register_settings_widget(CueAppSettings)

        # Show the mainWindow maximized
        self._mainWindow.showMaximized()
Exemplo n.º 15
0
 def __load_on_selected(self):
     item = self.presetsList.currentItem()
     if item is not None:
         preset_name = item.text()
         try:
             cues = Application().layout.get_selected_cues()
             if cues:
                 load_on_cues(preset_name, cues)
         except OSError as e:
             load_preset_error(e, preset_name, parent=MainWindow())
Exemplo n.º 16
0
 def testOla(self):
     if self.activateBox.isChecked():
         try:
             client = OlaClient()
             del client
         except OLADNotRunningException:
             QMessageBox.warning(
                 MainWindow(), translate('TimecodeSettings', 'OLA status'),
                 translate('TimecodeSettings',
                           'OLA is not running - start the OLA daemon.'))
Exemplo n.º 17
0
    def __init__(self):
        for name, cue in load_classes(__package__, path.dirname(__file__)):
            # Register the action-cue in the cue-factory
            CueFactory.register_factory(cue.__name__, cue)
            # Register the menu action for adding the action-cue
            add_function = ActionCues.create_add_action_cue_method(cue)
            MainWindow().register_cue_menu_action(
                translate('CueName', cue.Name),
                add_function, 'Action cues')

            logging.debug('ACTION-CUES: Loaded "' + name + '"')
Exemplo n.º 18
0
    def __init__(self):
        self._gain_thread = None

        # Entry in mainWindow menu
        self.menu = QMenu(translate('ReplayGain',
                                    'ReplayGain / Normalization'))
        self.menu_action = MainWindow().menuTools.addMenu(self.menu)

        self.actionGain = QAction(MainWindow())
        self.actionGain.triggered.connect(self.gain)
        self.actionGain.setText(translate('ReplayGain', 'Calculate'))
        self.menu.addAction(self.actionGain)

        self.actionReset = QAction(MainWindow())
        self.actionReset.triggered.connect(self._reset_all)
        self.actionReset.setText(translate('ReplayGain', 'Reset all'))
        self.menu.addAction(self.actionReset)

        self.actionResetSelected = QAction(MainWindow())
        self.actionResetSelected.triggered.connect(self._reset_selected)
        self.actionResetSelected.setText(
            translate('ReplayGain', 'Reset selected'))
        self.menu.addAction(self.actionResetSelected)
Exemplo n.º 19
0
    def __create_from_cue():
        cue = Application().layout.get_context_cue()
        name = save_preset_dialog(cue.name)

        if name is not None:
            if not (preset_exists(name) and not check_override_dialog(name)):
                preset = cue.properties(only_changed=True)

                # Discard id and index
                preset.pop('id')
                preset.pop('index')

                try:
                    write_preset(name, preset)
                except OSError as e:
                    write_preset_error(e, name, parent=MainWindow())
Exemplo n.º 20
0
    def __init__(self):
        self._mainWindow = MainWindow()
        self._app_conf = {}
        self._layout = None
        self._memento_model = None
        self._cue_model = CueModel()

        # Connect mainWindow actions
        self._mainWindow.new_session.connect(self.new_session_dialog)
        self._mainWindow.save_session.connect(self._save_to_file)
        self._mainWindow.open_session.connect(self._load_from_file)

        # Register general settings widget
        AppSettings.register_settings_widget(General)

        # Show the mainWindow maximized
        self._mainWindow.showMaximized()
Exemplo n.º 21
0
    def __init__(self):
        # Create the mainWindow
        self.mainWindow = MainWindow()
        # Create a layout 'reference' and set to None
        self.layout = None
        # Create an empty configuration
        self.app_conf = {}

        # Initialize modules
        failed = modules.init_modules()
        for err in failed:
            msg = 'Module "' + err[0] + '" loading failed'
            QDetailedMessageBox.dcritical('Module error', msg, str(err[1]))

        # Connect mainWindow actions
        self.mainWindow.new_session.connect(self._startup)
        self.mainWindow.save_session.connect(self._save_to_file)
        self.mainWindow.open_session.connect(self._load_from_file)

        # Register general settings widget
        AppSettings.register_settings_widget(General)

        # Show the mainWindow maximized
        self.mainWindow.showMaximized()
Exemplo n.º 22
0
 def show_ip(self):
     ip = translate('Synchronizer', 'Your IP is:') + ' ' + str(get_lan_ip())
     QMessageBox.information(MainWindow(), ' ', ip)
Exemplo n.º 23
0
class Application(metaclass=Singleton):
    def __init__(self):
        self._mainWindow = MainWindow()
        self._app_conf = {}
        self._layout = None
        self._memento_model = None
        self._cue_model = CueModel()

        # Connect mainWindow actions
        self._mainWindow.new_session.connect(self.new_session_dialog)
        self._mainWindow.save_session.connect(self._save_to_file)
        self._mainWindow.open_session.connect(self._load_from_file)

        # Register general settings widget
        AppSettings.register_settings_widget(AppGeneral)
        AppSettings.register_settings_widget(CueAppSettings)

        # Show the mainWindow maximized
        self._mainWindow.showMaximized()

    @property
    def layout(self):
        """:rtype: lisp.layouts.cue_layout.CueLayout"""
        return self._layout

    @property
    def cue_model(self):
        """:rtype: lisp.cues.cue_model.CueModel"""
        return self._cue_model

    def start(self, session_file=''):
        if exists(session_file):
            self._load_from_file(session_file)
        elif cfg.config['Layout']['Default'].lower() != 'nodefault':
            layout = layouts.get_layout(cfg.config['Layout']['Default'])
            self._new_session(layout)
        else:
            self.new_session_dialog()

    def new_session_dialog(self):
        """Show the layout-selection dialog"""
        try:
            # Prompt the user for a new layout
            dialog = LayoutSelect()
            if dialog.exec_() != QDialog.Accepted:
                if self._layout is None:
                    # If the user close the dialog, and no layout exists
                    # the application is closed
                    self.finalize()
                    qApp.quit()
                    exit()
                else:
                    return

            # If a valid file is selected load it, otherwise load the layout
            if exists(dialog.filepath):
                self._load_from_file(dialog.filepath)
            else:
                self._new_session(dialog.selected())

        except Exception as e:
            elogging.exception('Startup error', e)
            qApp.quit()
            exit(-1)

    def _new_session(self, layout):
        self._delete_session()

        self._layout = layout(self._cue_model)
        self._memento_model = AdapterMementoModel(self.layout.model_adapter)
        self._mainWindow.set_layout(self._layout)
        self._app_conf['layout'] = layout.NAME

        plugins.init_plugins()

    def _delete_session(self):
        if self._layout is not None:
            MainActionsHandler.clear()
            plugins.reset_plugins()

            self._app_conf.clear()
            self._cue_model.reset()

            self._layout.finalize()
            self._layout = None
            self._memento_model = None
            self._cue_model.reset()

    def finalize(self):
        modules.terminate_modules()

        self._delete_session()
        self._mainWindow.deleteLater()

    def _save_to_file(self, session_file):
        """Save the current session into a file."""
        session = {"cues": [], "plugins": {}, "application": []}

        # Add the cues
        for cue in self._cue_model:
            session['cues'].append(cue.properties(only_changed=True))
        # Sort cues by index, allow sorted-models to load properly
        session['cues'].sort(key=lambda cue: cue['index'])

        session['plugins'] = plugins.get_plugin_settings()
        session['application'] = self._app_conf

        # Write to a file the json-encoded dictionary
        with open(session_file, mode='w', encoding='utf-8') as file:
            file.write(json.dumps(session, sort_keys=True, indent=4))

        MainActionsHandler.set_saved()
        self._mainWindow.update_window_title()

    def _load_from_file(self, session_file):
        """ Load a saved session from file """
        try:
            with open(session_file, mode='r', encoding='utf-8') as file:
                session = json.load(file)

            # New session
            self._new_session(
                layouts.get_layout(session['application']['layout']))
            # Get the application settings
            self._app_conf = session['application']

            # Load cues
            for cue_conf in session['cues']:
                cue_type = cue_conf.pop('_type_', 'Undefined')
                cue_id = cue_conf.pop('id')
                try:
                    cue = CueFactory.create_cue(cue_type, cue_id=cue_id)
                    cue.update_properties(cue_conf)
                    self._cue_model.add(cue)
                except Exception as e:
                    elogging.exception('Unable to create the cue', e)

            MainActionsHandler.set_saved()
            self._mainWindow.update_window_title()

            # Load plugins settings
            plugins.set_plugins_settings(session['plugins'])

            # Update the main-window
            self._mainWindow.filename = session_file
            self._mainWindow.update()
        except Exception as e:
            elogging.exception('Error during file reading', e)
            self.new_session_dialog()
Exemplo n.º 24
0
class Application(metaclass=Singleton):

    def __init__(self):
        # Create the mainWindow
        self.mainWindow = MainWindow()
        # Create a layout 'reference' and set to None
        self.layout = None
        # Create an empty configuration
        self.app_conf = {}

        # Initialize modules
        failed = modules.init_modules()
        for err in failed:
            msg = 'Module "' + err[0] + '" loading failed'
            QDetailedMessageBox.dcritical('Module error', msg, str(err[1]))

        # Connect mainWindow actions
        self.mainWindow.new_session.connect(self._startup)
        self.mainWindow.save_session.connect(self._save_to_file)
        self.mainWindow.open_session.connect(self._load_from_file)

        # Register general settings widget
        AppSettings.register_settings_widget(General)

        # Show the mainWindow maximized
        self.mainWindow.showMaximized()

    def start(self, filepath=''):
        if exists(filepath):
            # Load the file
            self.mainWindow.file = filepath
            self._load_from_file(filepath)
        else:
            # Create the layout
            self._startup(first=True)

    def finalize(self):
        self.layout.destroy_layout()

        # Terminate the loaded modules
        modules.terminate_modules()

    def _create_layout(self, layout):
        '''
            Clear ActionHandler session;
            Reset plugins;
            Creates a new layout;
            Init plugins.
        '''

        ActionsHandler().clear()
        plugins.reset_plugins()

        if self.layout is not None:
            self.layout.destroy_layout()
            self.layout = None

        try:
            self.layout = layout(self)
            self.mainWindow.set_layout(self.layout)
            self.app_conf['layout'] = layout.NAME
            self._init_plugins()
        except Exception:
            QMessageBox.critical(None, 'Error', 'Layout init failed')
            print(traceback.format_exc(), file=sys.stderr)

    def _layout_dialog(self):
        ''' Show the layout-selection dialog '''
        try:
            select = LayoutSelect()
            select.exec_()
        except Exception as e:
            QMessageBox.critical(None, 'Fatal error', str(e))
            qApp.quit()
            exit(-1)

        if select.result() != QDialog.Accepted:
            qApp.quit()
            exit()

        if exists(select.filepath):
            self._load_from_file(select.filepath)
        else:
            self._create_layout(select.slected())

    def _startup(self, first=False):
        ''' Initializes the basic components '''
        self.mainWindow.file = ''
        self.app_conf = {}

        if first and cfg.config['Layout']['Default'].lower() != 'nodefault':
            layout = layouts.get_layout(cfg.config['Layout']['Default'])
            self._create_layout(layout)
        else:
            self._layout_dialog()

    def _save_to_file(self, filepath):
        ''' Save the current program into "filepath" '''

        # Empty structure
        program = {"cues": [], "plugins": {}, "application": []}

        # Add the cues
        for cue in self.layout.get_cues():
            if cue is not None:
                program['cues'].append(cue.properties())

        # Add the plugins
        failed = program['plugins'] = plugins.get_plugin_settings()
        for err in failed:
            msg = 'Plugin "' + err[0] + '" saving failed'
            QDetailedMessageBox.dcritical('Plugin error', msg, str(err[1]))

        # Add the app settings
        program['application'] = self.app_conf

        # Write to a file the json-encoded dictionary
        with open(filepath, mode='w', encoding='utf-8') as file:
            file.write(json.dumps(program, sort_keys=True, indent=4))

        ActionsHandler().set_saved()
        self.mainWindow.update_window_title()

    def _load_from_file(self, filepath):
        ''' Loads a saved program from "filepath" '''
        try:
            # Read the file
            with open(filepath, mode='r', encoding='utf-8') as file:
                program = json.load(file)

            # Get the application settings
            self.app_conf = program['application']

            # Create the layout
            self._create_layout(layouts.get_layout(self.app_conf['layout']))

            # Load cues
            for cue_conf in program['cues']:
                cue = CueFactory.create_cue(cue_conf)
                if cue is not None:
                    self.layout.add_cue(cue, cue['index'])

            ActionsHandler().set_saved()
            self.mainWindow.update_window_title()

            # Load plugins settings
            self._load_plugins_settings(program['plugins'])

            # Update the main-window
            self.mainWindow.file = filepath
            self.mainWindow.update()
        except Exception:
            QMessageBox.critical(None, 'Error', 'Error during file reading')
            print(traceback.format_exc(), file=sys.stderr)

            self._startup()

    def _init_plugins(self):
        ''' Initialize all the plugins '''
        failed = plugins.init_plugins()

        for err in failed:
            msg = 'Plugin "' + err[0] + '" initialization failed'
            QDetailedMessageBox.dcritical('Plugin error', msg, str(err[1]))

    def _load_plugins_settings(self, settings):
        ''' Loads all the plugins settings '''

        failed = plugins.set_plugins_settings(settings)

        for err in failed:
            msg = 'Plugin "' + err[0] + '" loading failed'
            QDetailedMessageBox.dcritical('Plugin error', msg, str(err[1]))
Exemplo n.º 25
0
    def __init__(self, cue_model, **kwargs):
        super().__init__(cue_model=cue_model, **kwargs)
        self.tabBar().setObjectName('CartTabBar')

        self.__columns = int(config['CartLayout']['GridColumns'])
        self.__rows = int(config['CartLayout']['GridRows'])
        self.__pages = []
        self.__context_widget = None

        self._show_seek = config['CartLayout'].getboolean('ShowSeek')
        self._show_dbmeter = config['CartLayout'].getboolean('ShowDbMeters')
        self._show_volume = config['CartLayout'].getboolean('ShowVolume')
        self._accurate_timing = config['CartLayout'].getboolean('ShowAccurate')
        self._countdown_mode = config['CartLayout'].getboolean('CountDown')
        self._auto_add_page = config['CartLayout'].getboolean('AutoAddPage')

        self._model_adapter = CueCartModel(cue_model, self.__rows,
                                           self.__columns)
        self._model_adapter.item_added.connect(self.__cue_added,
                                               Connection.QtQueued)
        self._model_adapter.item_removed.connect(self.__cue_removed,
                                                 Connection.QtQueued)
        self._model_adapter.item_moved.connect(self.__cue_moved,
                                               Connection.QtQueued)
        self._model_adapter.model_reset.connect(self.__model_reset)

        # Add layout-specific menus
        self.new_page_action = QAction(self)
        self.new_page_action.triggered.connect(self.add_page)
        self.new_pages_action = QAction(self)
        self.new_pages_action.triggered.connect(self.add_pages)
        self.rm_current_page_action = QAction(self)
        self.rm_current_page_action.triggered.connect(self.remove_current_page)

        self.countdown_mode = QAction(self)
        self.countdown_mode.setCheckable(True)
        self.countdown_mode.setChecked(self._countdown_mode)
        self.countdown_mode.triggered.connect(self.set_countdown_mode)

        self.show_seek_action = QAction(self)
        self.show_seek_action.setCheckable(True)
        self.show_seek_action.setChecked(self._show_seek)
        self.show_seek_action.triggered.connect(self.set_seek_visible)

        self.show_dbmeter_action = QAction(self)
        self.show_dbmeter_action.setCheckable(True)
        self.show_dbmeter_action.setChecked(self._show_dbmeter)
        self.show_dbmeter_action.triggered.connect(self.set_dbmeter_visible)

        self.show_volume_action = QAction(self)
        self.show_volume_action.setCheckable(True)
        self.show_volume_action.setChecked(self._show_volume)
        self.show_volume_action.triggered.connect(self.set_volume_visible)

        self.show_accurate_action = QAction(self)
        self.show_accurate_action.setCheckable(True)
        self.show_accurate_action.setChecked(self._accurate_timing)
        self.show_accurate_action.triggered.connect(self.set_accurate)

        layoutMenu = MainWindow().menuLayout
        layoutMenu.addAction(self.new_page_action)
        layoutMenu.addAction(self.new_pages_action)
        layoutMenu.addAction(self.rm_current_page_action)
        layoutMenu.addSeparator()
        layoutMenu.addAction(self.countdown_mode)
        layoutMenu.addAction(self.show_seek_action)
        layoutMenu.addAction(self.show_dbmeter_action)
        layoutMenu.addAction(self.show_volume_action)
        layoutMenu.addAction(self.show_accurate_action)

        # TODO: maybe can be moved outside the layout
        # Add cue preferences widgets
        CueSettingsRegistry().add_item(CueGeneralSettings, Cue)
        CueSettingsRegistry().add_item(MediaCueSettings, MediaCue)
        CueSettingsRegistry().add_item(Appearance)

        # Cue(s) context-menu actions
        self.edit_action = QAction(self)
        self.edit_action.triggered.connect(self._edit_cue_action)
        self.cm_registry.add_item(self.edit_action)

        self.sep1 = self.cm_registry.add_separator()

        self.remove_action = QAction(self)
        self.remove_action.triggered.connect(self._remove_cue_action)
        self.cm_registry.add_item(self.remove_action)

        self.select_action = QAction(self)
        self.select_action.triggered.connect(self.select_context_cue)
        self.cm_registry.add_item(self.select_action)

        self.sep2 = self.cm_registry.add_separator(MediaCue)

        # MediaCue(s) context-menu actions
        self.play_action = QAction(self)
        self.play_action.triggered.connect(self._play_context_cue)
        self.cm_registry.add_item(self.play_action, MediaCue)

        self.pause_action = QAction(self)
        self.pause_action.triggered.connect(self._pause_context_cue)
        self.cm_registry.add_item(self.pause_action, MediaCue)

        self.stop_action = QAction(self)
        self.stop_action.triggered.connect(self._stop_context_cue)
        self.cm_registry.add_item(self.stop_action, MediaCue)

        self.reset_volume_action = QAction(self)
        self.reset_volume_action.triggered.connect(self._reset_cue_volume)
        self.cm_registry.add_item(self.reset_volume_action, MediaCue)

        self.setAcceptDrops(True)
        self.retranslateUi()

        self.add_page()
Exemplo n.º 26
0
 def terminate(self):
     self.stop()
     MainWindow().menuTools.removeAction(self.menu_action)
Exemplo n.º 27
0
    def show_context_menu(position):
        menu_edit = MainWindow().menuEdit
        menu_edit.move(position)
        menu_edit.show()

        # Adjust the menu position
        desktop = qApp.desktop().availableGeometry()
        menu_rect = menu_edit.geometry()

        if menu_rect.bottom() > desktop.bottom():
            menu_edit.move(menu_edit.x(), menu_edit.y() - menu_edit.height())
        if menu_rect.right() > desktop.right():
            menu_edit.move(menu_edit.x() - menu_edit.width(), menu_edit.y())
Exemplo n.º 28
0
 def __edit_presets():
     ui = PresetsDialog(parent=MainWindow())
     ui.show()
Exemplo n.º 29
0
class Application(metaclass=Singleton):
    def __init__(self):
        self._mainWindow = MainWindow()
        self._app_conf = {}
        self._layout = None
        self._memento_model = None
        self._cue_model = CueModel()

        # Connect mainWindow actions
        self._mainWindow.new_session.connect(self.new_session_dialog)
        self._mainWindow.save_session.connect(self._save_to_file)
        self._mainWindow.open_session.connect(self._load_from_file)

        # Register general settings widget
        AppSettings.register_settings_widget(General)

        # Show the mainWindow maximized
        self._mainWindow.showMaximized()

    @property
    def layout(self):
        """:rtype: lisp.layouts.cue_layout.CueLayout"""
        return self._layout

    @property
    def cue_model(self):
        """:rtype: lisp.model_view.cue_model.CueModel"""
        return self._cue_model

    def start(self, session_file=''):
        if exists(session_file):
            self._load_from_file(session_file)
        elif cfg.config['Layout']['Default'].lower() != 'nodefault':
            layout = layouts.get_layout(cfg.config['Layout']['Default'])
            self._new_session(layout)
        else:
            self.new_session_dialog()

    def new_session_dialog(self):
        """Show the layout-selection dialog"""
        try:
            # Prompt the user for a new layout
            dialog = LayoutSelect()
            if dialog.exec_() != QDialog.Accepted:
                if self._layout is None:
                    # If the user close the dialog, and no layout exists
                    # the application is closed
                    self.finalize()
                    qApp.quit()
                    exit()
                else:
                    return

            # If a valid file is selected load it, otherwise load the layout
            if exists(dialog.filepath):
                self._load_from_file(dialog.filepath)
            else:
                self._new_session(dialog.selected())

        except Exception as e:
            logging.exception('Startup error', e)
            qApp.quit()
            exit(-1)

    def _new_session(self, layout):
        self._delete_session()

        self._layout = layout(self._cue_model)
        self._memento_model = AdapterMementoModel(self.layout.model_adapter)
        self._mainWindow.set_layout(self._layout)
        self._app_conf['layout'] = layout.NAME

        plugins.init_plugins()

    def _delete_session(self):
        if self._layout is not None:
            MainActionsHandler().clear()
            plugins.reset_plugins()

            self._app_conf.clear()
            self._cue_model.reset()

            self._layout.finalize()
            self._layout = None
            self._memento_model = None

    def finalize(self):
        self._delete_session()
        modules.terminate_modules()

    def _save_to_file(self, session_file):
        """ Save the current session into a file """
        session = {"cues": [], "plugins": {}, "application": []}

        # Add the cues
        for cue in self._cue_model:
            session['cues'].append(cue.properties())
        # Sort cues by index, allow sorted-models to load properly
        session['cues'].sort(key=lambda cue: cue['index'])

        session['plugins'] = plugins.get_plugin_settings()
        session['application'] = self._app_conf

        # Write to a file the json-encoded dictionary
        with open(session_file, mode='w', encoding='utf-8') as file:
            file.write(json.dumps(session, sort_keys=True, indent=4))

        MainActionsHandler().set_saved()
        self._mainWindow.update_window_title()

    def _load_from_file(self, session_file):
        """ Load a saved session from file """
        try:
            with open(session_file, mode='r', encoding='utf-8') as file:
                session = json.load(file)

            # New session
            self._new_session(
                layouts.get_layout(session['application']['layout']))
            # Get the application settings
            self._app_conf = session['application']

            # Load cues
            for cue_conf in session['cues']:
                cue_type = cue_conf.pop('_type_', 'Undefined')
                try:
                    cue = CueFactory.create_cue(cue_type)
                    cue.update_properties(cue_conf)
                    self._cue_model.add(cue)
                except Exception as e:
                    logging.exception('Unable to create the cue', e)

            MainActionsHandler().set_saved()
            self._mainWindow.update_window_title()

            # Load plugins settings
            plugins.set_plugins_settings(session['plugins'])

            # Update the main-window
            self._mainWindow.filename = session_file
            self._mainWindow.update()
        except Exception as e:
            logging.exception('Error during file reading', e)
            self.new_session_dialog()
Exemplo n.º 30
0
 def manage_peers(self):
     manager = PeersDialog(self.peers, parent=MainWindow())
     manager.exec_()
Exemplo n.º 31
0
 def __init__(self):
     MainWindow().register_cue_menu_action(translate(
         'MediaCueMenus', 'Audio cue (from file)'),
                                           self.add_uri_audio_media_cue,
                                           category='Media cues',
                                           shortcut='CTRL+M')
Exemplo n.º 32
0
 def terminate(self):
     MainWindow().menuTools.removeAction(self.menuAction)
Exemplo n.º 33
0
 def show_dialog(self):
     dialog = UriChangerDialog(parent=MainWindow())
     dialog.exec_()
Exemplo n.º 34
0
class Main:

    def __init__(self):
        QtGui.QIcon.setThemeSearchPaths(styles.QLiSPIconsThemePaths)
        QtGui.QIcon.setThemeName(styles.QLiSPIconsThemeName)

        self.mainWindow = MainWindow()
        self.collector = Collector()
        self.collectorUi = None
        self.programConf = {}
        try:
            midi.initialize()
            self.midiHandler = midi.MidiEventHandler(int(cfg.config['MIDI']['DeviceID']))
            self.midiHandler.start()
        except:
            self.midiHandler = None
            QtGui.QMessageBox.critical(self.mainWindow, 'Error', 'MIDI event handler cannot be started!')

        self.mainWindow.newProgramAction.connect(self.newProgram)
        self.mainWindow.saveProgramAction.connect(self.saveProgram)
        self.mainWindow.openProgramAction.connect(self.openProgram)
        self.mainWindow.addMediaAction.connect(self.addMedia, QtCore.Qt.QueuedConnection)

        if(cfg.config['Layout']['UseDefault'] == 'True'):
            self.createLayout(cfg.config['Layout']['Default'])
        else:
            self.mainWindow.setEnabled(False)
            while(not self.layoutDialog()):
                pass
            self.mainWindow.setEnabled(True)

        try:
            self.plugins = plugin.initPlugins(self)
        except Exception as e:
            QtGui.QMessageBox.critical(None, 'Error Message', ' '.join([str(i) for i in e.args]))

    def layoutDialog(self):
        select = LayoutSelect()
        select.exec_()
        if(select.result() == QtGui.QDialog.Accepted):
            self.createLayout(select.getSlected())
            return True
        else:
            return False

    def createLayout(self, lay):
        self.mainWindow.menuLayout.clear()

        if(self.collectorUi is not None):
            self.collectorUi.destroyLayout()
            self.mainWindow.multiEditAction.disconnect()
            self.mainWindow.selectAllAction.disconnect()
            self.mainWindow.deselectAllAction.disconnect()
            self.mainWindow.invertSelectionAction.disconnect()

        self.collectorUi = layout.buildLayout(lay, self, parent=self.mainWindow.centralwidget)

        self.mainWindow.multiEditAction.connect(self.collectorUi.editSelectedMedia)
        self.mainWindow.selectAllAction.connect(self.collectorUi.selectAll)
        self.mainWindow.deselectAllAction.connect(self.collectorUi.deselectAll)
        self.mainWindow.invertSelectionAction.connect(self.collectorUi.invertSelection)

        self.mainWindow.setProgramLayout(self.collectorUi)
        self.programConf['layout'] = lay

    def addMedia(self, files):
        try:
            for file in files:

                conf = {}
                conf['location'] = file
                conf['name'] = file.split('/')[len(file.split('/')) - 1]

                media = self.collectorUi.getMediaClass()(conf)

                self.collectorUi.addMedia(media)
        except Exception as e:
            QtGui.QMessageBox.critical(None, 'Error Message', ' '.join([str(i) for i in e.args]))

    def playByPos(self, pos):
        media = self.collectorUi.mediaAt(pos)
        if(media is not None):
            media.play()

    def stopByPos(self, pos):
        media = self.collectorUi.mediaAt(pos)
        if(media is not None):
            media.stop()

    def pauseByPos(self, pos):
        media = self.collectorUi.mediaAt(pos)
        if(media is not None):
            media.pause()

    def seekByPos(self, pos, time_ms):
        media = self.collectorUi.mediaAt(pos)
        if(media is not None):
            media.seek(time_ms)

    def newProgram(self):
        self.collectorUi.destroyLayout()
        self.collector.resetCollector()
        self.collectorUi = None
        self.mainWindow.file = ''
        while(not self.layoutDialog()):
            pass
        self.resetPlugin()

    def saveProgram(self, filepath):
        writer = xml_writer.XmlWriter(filepath)
        for media in self.collectorUi.mediaList():
            if(media is not None):
                if(media.state != Media.NONE):
                    writer.appendMedia(media.conf, media.uuid)

        writer.appendProgramSettings(self.programConf)

        for plug in self.plugins:
            data = self.getPluginData(plug)
            if(data is not None):
                writer.appendPluginSettings(plug.PLUGIN_NAME, data)

        writer.writeFile()

    def openProgram(self, filepath):
        progress = Progress(title='Loading ' + filepath)
        progress.show()
        try:
            progress.setLabelText('Reading file ...')
            reader = xml_reader.XmlReader(filepath)
            conf = reader.readMediaList()
            progress.setMaximum(len(conf) + 10)
            pluginsConf = reader.readPluginsSettings()
            self.programConf = reader.readProgramSettings()

            progress.setLabelText('Creating layout ...')
            self.createLayout(self.programConf['layout'])
            self.mainWindow.update()
            progress.setValue(progress.value() + 5)

            progress.setLabelText('Loading media ...')
            pfunc = lambda w: progress.setValue(progress.value() + 1)
            self.collector.onMediaAdded.connect(pfunc)
            self.collectorUi.readProgramConf(conf)
            self.collector.onMediaAdded.disconnect(pfunc)

            progress.setLabelText('Loading plugins ...')
            self.resetPlugin()
            self.reloadPlugins()
            self.setPluginsData(pluginsConf)
            progress.hide()
        except Exception as e:
            progress.hide()
            QtGui.QMessageBox.critical(None, 'Error Message', 'Error during file reading: ' + ' '.join([str(i) for i in e.args]))
            self.newProgram()

    def setPluginsData(self, conf, debug=False):
        for plug in self.plugins:
            if(plug.PLUGIN_NAME in conf):
                if(debug):
                    plug.setData(conf[plug.PLUGIN_NAME])
                else:
                    try:
                        plug.setData(conf[plug.PLUGIN_NAME])
                    except:
                        raise Exception('Plugin data read failed: ' + plug.PLUGIN_NAME)

    def reloadPlugins(self, debug=False):
        for plug in self.plugins:
            if(debug):
                plug.reload()
            else:
                try:
                    plug.reload()
                except:
                    raise Exception('Plugin load failed: ' + plug.PLUGIN_NAME)

    def resetPlugin(self, debug=False):
        for plug in self.plugins:
            if(debug):
                plug.reset()
            else:
                try:
                    plug.reset()
                except:
                    raise Exception('Plugin reset failed: ' + plug.PLUGIN_NAME)

    def getPluginData(self, plugin, debug=False):
        if(debug):
            return plugin.getData()
        else:
            try:
                return plugin.getData()
            except:
                raise Exception('Plugin data save failed: ' + plugin.PLUGIN_NAME)
Exemplo n.º 35
0
    def __init__(self):
        self.menuAction = QAction(
            translate('UriChanger', 'Session URI change'), MainWindow())
        self.menuAction.triggered.connect(self.show_dialog)

        MainWindow().menuTools.addAction(self.menuAction)
Exemplo n.º 36
0
    def __init__(self, cue_model, **kwargs):
        super().__init__(cue_model=cue_model, **kwargs)
        self.setLayout(QGridLayout())
        self.layout().setContentsMargins(0, 0, 0, 0)

        self._model_adapter = CueListModel(self._cue_model)
        self._model_adapter.item_added.connect(self.__cue_added)
        self._model_adapter.item_removed.connect(self.__cue_removed)

        self._playing_model = RunningCueModel(self._cue_model)
        self._context_item = None
        self._next_cue_index = 0

        self._show_dbmeter = config['ListLayout'].getboolean('ShowDbMeters')
        self._seek_visible = config['ListLayout'].getboolean('ShowSeek')
        self._accurate_time = config['ListLayout'].getboolean('ShowAccurate')
        self._auto_continue = config['ListLayout'].getboolean('AutoContinue')
        self._show_playing = config['ListLayout'].getboolean('ShowPlaying')
        self._go_key = config['ListLayout']['GoKey']
        self._go_key_sequence = QKeySequence(self._go_key,
                                             QKeySequence.NativeText)

        try:
            self._end_list = EndListBehavior(config['ListLayout']['EndList'])
        except ValueError:
            self._end_list = EndListBehavior.Stop

        # Add layout-specific menus
        self.showPlayingAction = QAction(self)
        self.showPlayingAction.setCheckable(True)
        self.showPlayingAction.setChecked(self._show_playing)
        self.showPlayingAction.triggered.connect(self.set_playing_visible)

        self.showDbMeterAction = QAction(self)
        self.showDbMeterAction.setCheckable(True)
        self.showDbMeterAction.setChecked(self._show_dbmeter)
        self.showDbMeterAction.triggered.connect(self.set_dbmeter_visible)

        self.showSeekAction = QAction(self)
        self.showSeekAction.setCheckable(True)
        self.showSeekAction.setChecked(self._seek_visible)
        self.showSeekAction.triggered.connect(self.set_seek_visible)

        self.accurateTimingAction = QAction(self)
        self.accurateTimingAction.setCheckable(True)
        self.accurateTimingAction.setChecked(self._accurate_time)
        self.accurateTimingAction.triggered.connect(self.set_accurate_time)

        self.autoNextAction = QAction(self)
        self.autoNextAction.setCheckable(True)
        self.autoNextAction.setChecked(self._auto_continue)
        self.autoNextAction.triggered.connect(self.set_auto_next)

        MainWindow().menuLayout.addAction(self.showPlayingAction)
        MainWindow().menuLayout.addAction(self.showDbMeterAction)
        MainWindow().menuLayout.addAction(self.showSeekAction)
        MainWindow().menuLayout.addAction(self.accurateTimingAction)
        MainWindow().menuLayout.addAction(self.autoNextAction)

        # GO-BUTTON (top-left)
        self.goButton = QPushButton('GO', self)
        self.goButton.setFocusPolicy(Qt.NoFocus)
        self.goButton.setFixedWidth(120)
        self.goButton.setFixedHeight(100)
        self.goButton.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Expanding)
        self.goButton.setStyleSheet('font-size: 48pt;')
        self.goButton.clicked.connect(self.__go_slot)
        self.layout().addWidget(self.goButton, 0, 0)

        # INFO PANEL (top center)
        self.infoPanel = InfoPanel()
        self.infoPanel.setFixedHeight(100)
        self.layout().addWidget(self.infoPanel, 0, 1)

        # CONTROL-BUTTONS (top-right)
        self.controlButtons = ShowControlButtons(parent=self)
        self.controlButtons.setFixedHeight(100)
        self.controlButtons.stopButton.clicked.connect(self.stop_all)
        self.controlButtons.pauseButton.clicked.connect(self.pause_all)
        self.controlButtons.fadeInButton.clicked.connect(self.fadein_all)
        self.controlButtons.fadeOutButton.clicked.connect(self.fadeout_all)
        self.controlButtons.resumeButton.clicked.connect(self.restart_all)
        self.controlButtons.interruptButton.clicked.connect(self.interrupt_all)
        self.layout().addWidget(self.controlButtons, 0, 2)

        # CUE VIEW (center left)
        self.listView = CueListView(self._model_adapter, self)
        self.listView.itemDoubleClicked.connect(self.double_clicked)
        self.listView.currentItemChanged.connect(self.__current_changed)
        self.listView.context_event.connect(self.context_event)
        self.listView.key_event.connect(self.onKeyPressEvent)
        self.listView.select_cue_event.connect(self.select_event)
        self.layout().addWidget(self.listView, 1, 0, 1, 2)

        # PLAYING VIEW (center right)
        self.playView = RunningCuesListWidget(self._playing_model, parent=self)
        self.playView.dbmeter_visible = self._show_dbmeter
        self.playView.accurate_time = self._accurate_time
        self.playView.seek_visible = self._seek_visible
        self.playView.setMinimumWidth(300)
        self.playView.setMaximumWidth(300)
        self.layout().addWidget(self.playView, 1, 2)

        self.set_playing_visible(self._show_playing)

        # TODO: maybe can be moved outside the layout
        # Add cue preferences widgets
        CueSettingsRegistry().add_item(CueGeneralSettings, Cue)
        CueSettingsRegistry().add_item(MediaCueSettings, MediaCue)
        CueSettingsRegistry().add_item(Appearance)

        # Context menu actions
        self.edit_action = QAction(self)
        self.edit_action.triggered.connect(self.edit_context_cue)

        self.remove_action = QAction(self)
        self.remove_action.triggered.connect(self.remove_context_cue)

        self.select_action = QAction(self)
        self.select_action.triggered.connect(self.select_context_cue)

        self.cm_registry.add_item(self.edit_action)
        self.sep1 = self.cm_registry.add_separator()
        self.cm_registry.add_item(self.remove_action)
        self.cm_registry.add_item(self.select_action)

        self.retranslateUi()
Exemplo n.º 37
0
    def __init__(self, cue_model, **kwargs):
        super().__init__(cue_model=cue_model, **kwargs)
        self.tabBar().setObjectName('CartTabBar')

        self.__columns = int(config['CartLayout']['GridColumns'])
        self.__rows = int(config['CartLayout']['GridRows'])
        self.__pages = []
        self.__context_widget = None

        self._show_seek = config['CartLayout']['ShowSeek'] == 'True'
        self._show_dbmeter = config['CartLayout']['ShowDbMeters'] == 'True'
        self._accurate_timing = config['CartLayout']['ShowAccurate'] == 'True'
        self._countdown_mode = config['CartLayout']['countDown'] == 'True'
        self._auto_add_page = config['CartLayout']['autoAddPage'] == 'True'

        self._model_adapter = CueCartModel(cue_model, self.__rows, self.__columns)
        self._model_adapter.item_added.connect(self.__cue_added,Connection.QtQueued)
        self._model_adapter.item_removed.connect(self.__cue_removed, Connection.QtQueued)
        self._model_adapter.item_moved.connect(self.__cue_moved, Connection.QtQueued)

        # Add layout-specific menus
        self.new_page_action = QAction(self)
        self.new_page_action.triggered.connect(self.add_page)
        self.new_pages_action = QAction(self)
        self.new_pages_action.triggered.connect(self.add_pages)
        self.rm_current_page_action = QAction(self)
        self.rm_current_page_action.triggered.connect(self.remove_current_page)

        self.countdown_mode = QAction(self)
        self.countdown_mode.setCheckable(True)
        self.countdown_mode.setChecked(self._countdown_mode)
        self.countdown_mode.triggered.connect(self.set_countdown_mode)

        self.show_sliders_action = QAction(self)
        self.show_sliders_action.setCheckable(True)
        self.show_sliders_action.setChecked(self._show_seek)
        self.show_sliders_action.triggered.connect(self.set_seek_visible)

        self.show_vumeter_action = QAction(self)
        self.show_vumeter_action.setCheckable(True)
        self.show_vumeter_action.setChecked(self._show_dbmeter)
        self.show_vumeter_action.triggered.connect(self.set_dbmeter_visible)

        self.show_accurate_action = QAction(self)
        self.show_accurate_action.setCheckable(True)
        self.show_accurate_action.setChecked(self._accurate_timing)
        self.show_accurate_action.triggered.connect(self.set_accurate)

        layoutMenu = MainWindow().menuLayout
        layoutMenu.addAction(self.new_page_action)
        layoutMenu.addAction(self.new_pages_action)
        layoutMenu.addAction(self.rm_current_page_action)
        layoutMenu.addSeparator()
        layoutMenu.addAction(self.countdown_mode)
        layoutMenu.addAction(self.show_sliders_action)
        layoutMenu.addAction(self.show_vumeter_action)
        layoutMenu.addAction(self.show_accurate_action)

        # TODO: maybe can be moved outside the layout
        # Add cue preferences widgets
        CueSettingsRegistry().add_item(CueGeneralSettings, Cue)
        CueSettingsRegistry().add_item(MediaCueSettings, MediaCue)
        CueSettingsRegistry().add_item(Appearance)

        # Cue(s) context-menu actions
        self.edit_action = QAction(self)
        self.edit_action.triggered.connect(self._edit_cue_action)
        self.cm_registry.add_item(self.edit_action)

        self.sep1 = self.cm_registry.add_separator()

        self.remove_action = QAction(self)
        self.remove_action.triggered.connect(self._remove_cue_action)
        self.cm_registry.add_item(self.remove_action)

        self.select_action = QAction(self)
        self.select_action.triggered.connect(self.select_context_cue)
        self.cm_registry.add_item(self.select_action)

        self.sep2 = self.cm_registry.add_separator(MediaCue)

        # MediaCue(s) context-menu actions
        self.play_action = QAction(self)
        self.play_action.triggered.connect(self._play_context_cue)
        self.cm_registry.add_item(self.play_action, MediaCue)

        self.pause_action = QAction(self)
        self.pause_action.triggered.connect(self._pause_context_cue)
        self.cm_registry.add_item(self.pause_action, MediaCue)

        self.stop_action = QAction(self)
        self.stop_action.triggered.connect(self._stop_context_cue)
        self.cm_registry.add_item(self.stop_action, MediaCue)

        self.setAcceptDrops(True)
        self.retranslateUi()

        self.add_page()