Example #1
0
    def __init__(self):
        QListView.__init__(self)
        self.setIconSize(QtCore.QSize(20, 20))
        self.model = self.ItemModel(self)
        self.proxy = self.SortProxyModel()
        self.proxy.setSourceModel(self.model)

        self.setModel(self.proxy)
Example #2
0
    def outputContextMenu(self, listview: QListView, pos: QPoint):
        current_model = listview.model()
        menu = QMenu()
        edit_action = QAction(
            "Edit clicked" if len(listview.selectedIndexes()) > 1 else "Edit",
            menu)
        edit_action.triggered.connect(self.editCurrentTarget)
        menu.addAction(edit_action)

        delete_action = QAction("Remove", menu)
        delete_action.triggered.connect(lambda: current_model.__delitem__(
            [i.row() for i in listview.selectedIndexes()]))
        menu.addAction(delete_action)

        selected_istemp = current_model[
            listview.currentIndex().row()].temporary
        mark_temp_action = QAction("Mark temp", menu)
        mark_temp_action.setCheckable(True)
        mark_temp_action.setChecked(selected_istemp)
        mark_temp_action.triggered.connect(lambda: [
            current_model[i.row()].switch_temporary(not selected_istemp)
            for i in listview.selectedIndexes()
        ])
        menu.addAction(mark_temp_action)

        self.addTargetActions(menu)
        menu.exec_(listview.mapToGlobal(pos))
Example #3
0
 def createProgramsList(self):
     self.programsListModel = QStandardItemModel(0, 1, self)
     self.programsList = QListView()
     self.programsList.setModel(self.programsListModel)
     self.programsListPage = QWidget()
     self.programsListLayout = QVBoxLayout()
     self.programsListButtons = QHBoxLayout()
     self.programsListButtonNew = QPushButton("New")
     self.programsListButtonDelete = QPushButton("Delete")
     self.programsListButtonEdit = QPushButton("Edit")
     self.programsListButtonSet = QPushButton("Set")
     self.programsListButtons.addWidget(self.programsListButtonNew)
     self.programsListButtons.addWidget(self.programsListButtonSet)
     self.programsListButtons.addWidget(self.programsListButtonEdit)
     self.programsListButtons.addWidget(self.programsListButtonDelete)
     self.programsListLayout.addLayout(self.programsListButtons)
     self.programsListLayout.addWidget(self.programsList)
     self.programsListPage.setLayout(self.programsListLayout)
     self.programsListButtonNew.clicked.connect(self.newProgram)
     self.programsListButtonEdit.clicked.connect(self.editProgram)
     self.programsListButtonDelete.clicked.connect(self.deleteProgram)
     self.programsListButtonSet.clicked.connect(self.setProgram)
Example #4
0
    def setup_ui(self):
        super(AtlasUI, self).setup_ui()

        image_clear_btn = QPushButton(text="清空")
        image_clear_btn.clicked.connect(self.on_image_clear_btn_clicked)

        image_listview = QListView()
        image_listview.setAcceptDrops(True)
        image_list_model = QStandardItemModel(image_listview)
        image_list_model.itemChanged.connect(self.on_image_changed)
        image_listview.setModel(image_list_model)

        self.main_layout.addWidget(image_clear_btn, 0, 0, 1, 1, QtCore.Qt.AlignLeft)
        self.main_layout.addWidget(image_listview, 1, 0, 1, 1)

        self.image_clear_btn = image_clear_btn
        self.image_listview = image_listview
        self.image_list_model = image_list_model

        self.refresh_images()
Example #5
0
    def __init__(self, model):
        """Initialize MNELAB main window.

        Parameters
        ----------
        model : mnelab.model.Model instance
            The main window needs to connect to a model containing all data sets. This
            decouples the GUI from the data (model/view).
        """
        super().__init__()
        self.model = model  # data model
        self.setWindowTitle("MNELAB")

        # restore settings
        settings = read_settings()
        self.recent = settings["recent"]  # list of recent files
        self.resize(settings["size"])
        self.move(settings["pos"])

        # remove None entries from self.recent
        self.recent = [recent for recent in self.recent if recent is not None]

        # trigger theme setting
        QIcon.setThemeSearchPaths([str(Path(__file__).parent / "icons")])
        self.event(QEvent(QEvent.PaletteChange))

        self.actions = {}  # contains all actions

        # initialize menus
        file_menu = self.menuBar().addMenu("&File")
        icon = QIcon.fromTheme("open-file")
        self.actions["open_file"] = file_menu.addAction(
            icon, "&Open...", self.open_data, QKeySequence.Open)
        self.recent_menu = file_menu.addMenu("Open recent")
        self.recent_menu.aboutToShow.connect(self._update_recent_menu)
        self.recent_menu.triggered.connect(self._load_recent)
        if not self.recent:
            self.recent_menu.setEnabled(False)
        self.actions["close_file"] = file_menu.addAction(
            "&Close", self.model.remove_data, QKeySequence.Close)
        self.actions["close_all"] = file_menu.addAction(
            "Close all", self.close_all)
        file_menu.addSeparator()
        icon = QIcon.fromTheme("meta-info")
        self.actions["meta_info"] = file_menu.addAction(
            icon, "Show information...", self.meta_info)
        file_menu.addSeparator()
        self.actions["import_bads"] = file_menu.addAction(
            "Import bad channels...", lambda: self.import_file(
                model.import_bads, "Import bad channels", "*.csv"))
        self.actions["import_events"] = file_menu.addAction(
            "Import events...", lambda: self.import_file(
                model.import_events, "Import events", "*.csv"))
        self.actions["import_annotations"] = file_menu.addAction(
            "Import annotations...", lambda: self.import_file(
                model.import_annotations, "Import annotations", "*.csv"))
        self.actions["import_ica"] = file_menu.addAction(
            "Import &ICA...", lambda: self.open_file(
                model.import_ica, "Import ICA", "*.fif *.fif.gz"))
        file_menu.addSeparator()
        self.export_menu = file_menu.addMenu("Export data")
        for ext, description in writers.items():
            action = "export_data" + ext.replace(".", "_")
            self.actions[action] = self.export_menu.addAction(
                f"{ext[1:].upper()} ({description[1]})...",
                partial(self.export_file, model.export_data, "Export data",
                        "*" + ext))
        self.actions["export_bads"] = file_menu.addAction(
            "Export &bad channels...", lambda: self.export_file(
                model.export_bads, "Export bad channels", "*.csv"))
        self.actions["export_events"] = file_menu.addAction(
            "Export &events...", lambda: self.export_file(
                model.export_events, "Export events", "*.csv"))
        self.actions["export_annotations"] = file_menu.addAction(
            "Export &annotations...", lambda: self.export_file(
                model.export_annotations, "Export annotations", "*.csv"))
        self.actions["export_ica"] = file_menu.addAction(
            "Export ICA...", lambda: self.export_file(
                model.export_ica, "Export ICA", "*.fif *.fif.gz"))
        file_menu.addSeparator()
        self.actions["xdf_chunks"] = file_menu.addAction(
            "Show XDF chunks...", self.xdf_chunks)
        file_menu.addSeparator()
        self.actions["quit"] = file_menu.addAction("&Quit", self.close,
                                                   QKeySequence.Quit)

        edit_menu = self.menuBar().addMenu("&Edit")
        self.actions["pick_chans"] = edit_menu.addAction(
            "P&ick channels...", self.pick_channels)
        icon = QIcon.fromTheme("chan-props")
        self.actions["chan_props"] = edit_menu.addAction(
            icon, "Channel &properties...", self.channel_properties)
        self.actions["set_montage"] = edit_menu.addAction(
            "Set &montage...", self.set_montage)
        edit_menu.addSeparator()
        self.actions["set_ref"] = edit_menu.addAction("Set &reference...",
                                                      self.set_reference)
        edit_menu.addSeparator()
        self.actions["annotations"] = edit_menu.addAction(
            "&Annotations...", self.edit_annotations)
        self.actions["events"] = edit_menu.addAction("&Events...",
                                                     self.edit_events)

        edit_menu.addSeparator()
        self.actions["crop"] = edit_menu.addAction("&Crop data...", self.crop)
        self.actions["append_data"] = edit_menu.addAction(
            "Appen&d data...", self.append_data)

        plot_menu = self.menuBar().addMenu("&Plot")
        icon = QIcon.fromTheme("plot-data")
        self.actions["plot_data"] = plot_menu.addAction(
            icon, "&Data", self.plot_data)
        icon = QIcon.fromTheme("plot-psd")
        self.actions["plot_psd"] = plot_menu.addAction(
            icon, "&Power spectral density", self.plot_psd)
        icon = QIcon.fromTheme("plot-locations")
        self.actions["plot_locations"] = plot_menu.addAction(
            icon, "&Channel locations", self.plot_locations)
        self.actions["plot_erds"] = plot_menu.addAction(
            "&ERDS maps...", self.plot_erds)
        plot_menu.addSeparator()
        self.actions["plot_ica_components"] = plot_menu.addAction(
            "ICA &components...", self.plot_ica_components)
        self.actions["plot_ica_sources"] = plot_menu.addAction(
            "ICA &sources...", self.plot_ica_sources)

        tools_menu = self.menuBar().addMenu("&Tools")
        icon = QIcon.fromTheme("filter-data")
        self.actions["filter"] = tools_menu.addAction(icon, "&Filter data...",
                                                      self.filter_data)
        icon = QIcon.fromTheme("find-events")
        self.actions["find_events"] = tools_menu.addAction(
            icon, "Find &events...", self.find_events)
        self.actions["events_from_annotations"] = tools_menu.addAction(
            "Create events from annotations", self.events_from_annotations)
        self.actions["annotations_from_events"] = tools_menu.addAction(
            "Create annotations from events", self.annotations_from_events)
        tools_menu.addSeparator()
        nirs_menu = tools_menu.addMenu("NIRS")
        self.actions["convert_od"] = nirs_menu.addAction(
            "Convert to &optical density", self.convert_od)
        self.actions["convert_bl"] = nirs_menu.addAction(
            "Convert to &haemoglobin", self.convert_bl)

        tools_menu.addSeparator()
        icon = QIcon.fromTheme("run-ica")
        self.actions["run_ica"] = tools_menu.addAction(icon, "Run &ICA...",
                                                       self.run_ica)
        self.actions["apply_ica"] = tools_menu.addAction(
            "Apply &ICA", self.apply_ica)
        tools_menu.addSeparator()
        self.actions["interpolate_bads"] = tools_menu.addAction(
            "Interpolate bad channels...", self.interpolate_bads)
        tools_menu.addSeparator()
        icon = QIcon.fromTheme("epoch-data")
        self.actions["epoch_data"] = tools_menu.addAction(
            icon, "Create epochs...", self.epoch_data)

        view_menu = self.menuBar().addMenu("&View")
        self.actions["history"] = view_menu.addAction("&History...",
                                                      self.show_history)
        self.actions["toolbar"] = view_menu.addAction("&Toolbar",
                                                      self._toggle_toolbar)
        self.actions["toolbar"].setCheckable(True)
        self.actions["statusbar"] = view_menu.addAction(
            "&Statusbar", self._toggle_statusbar)
        self.actions["statusbar"].setCheckable(True)

        help_menu = self.menuBar().addMenu("&Help")
        self.actions["about"] = help_menu.addAction("&About", self.show_about)
        self.actions["about_qt"] = help_menu.addAction("About &Qt",
                                                       self.show_about_qt)

        # actions that are always enabled
        self.always_enabled = [
            "open_file", "about", "about_qt", "quit", "xdf_chunks", "toolbar",
            "statusbar"
        ]

        # set up toolbar
        self.toolbar = self.addToolBar("toolbar")
        self.toolbar.setObjectName("toolbar")
        self.toolbar.addAction(self.actions["open_file"])
        self.toolbar.addAction(self.actions["meta_info"])
        self.toolbar.addSeparator()
        self.toolbar.addAction(self.actions["chan_props"])
        self.toolbar.addSeparator()
        self.toolbar.addAction(self.actions["plot_data"])
        self.toolbar.addAction(self.actions["plot_psd"])
        self.toolbar.addAction(self.actions["plot_locations"])
        self.toolbar.addSeparator()
        self.toolbar.addAction(self.actions["filter"])
        self.toolbar.addAction(self.actions["find_events"])
        self.toolbar.addAction(self.actions["epoch_data"])
        self.toolbar.addAction(self.actions["run_ica"])
        self.toolbar.setMovable(False)
        self.setUnifiedTitleAndToolBarOnMac(True)
        if settings["toolbar"]:
            self.toolbar.show()
            self.actions["toolbar"].setChecked(True)
        else:
            self.toolbar.hide()
            self.actions["toolbar"].setChecked(False)

        # set up data model for sidebar (list of open files)
        self.names = QStringListModel()
        self.names.dataChanged.connect(self._update_names)
        splitter = QSplitter()
        self.sidebar = QListView()
        self.sidebar.setFrameStyle(QFrame.NoFrame)
        self.sidebar.setFocusPolicy(Qt.NoFocus)
        self.sidebar.setModel(self.names)
        self.sidebar.clicked.connect(self._update_data)
        splitter.addWidget(self.sidebar)
        self.infowidget = InfoWidget()
        splitter.addWidget(self.infowidget)
        width = splitter.size().width()
        splitter.setSizes((int(width * 0.3), int(width * 0.7)))
        self.setCentralWidget(splitter)

        self.status_label = QLabel()
        self.statusBar().addPermanentWidget(self.status_label)
        if settings["statusbar"]:
            self.statusBar().show()
            self.actions["statusbar"].setChecked(True)
        else:
            self.statusBar().hide()
            self.actions["statusbar"].setChecked(False)

        self.setAcceptDrops(True)
        self.data_changed()
Example #6
0
class MainWindow(QMainWindow):
    """MNELAB main window."""
    def __init__(self, model):
        """Initialize MNELAB main window.

        Parameters
        ----------
        model : mnelab.model.Model instance
            The main window needs to connect to a model containing all data sets. This
            decouples the GUI from the data (model/view).
        """
        super().__init__()
        self.model = model  # data model
        self.setWindowTitle("MNELAB")

        # restore settings
        settings = read_settings()
        self.recent = settings["recent"]  # list of recent files
        self.resize(settings["size"])
        self.move(settings["pos"])

        # remove None entries from self.recent
        self.recent = [recent for recent in self.recent if recent is not None]

        # trigger theme setting
        QIcon.setThemeSearchPaths([str(Path(__file__).parent / "icons")])
        self.event(QEvent(QEvent.PaletteChange))

        self.actions = {}  # contains all actions

        # initialize menus
        file_menu = self.menuBar().addMenu("&File")
        icon = QIcon.fromTheme("open-file")
        self.actions["open_file"] = file_menu.addAction(
            icon, "&Open...", self.open_data, QKeySequence.Open)
        self.recent_menu = file_menu.addMenu("Open recent")
        self.recent_menu.aboutToShow.connect(self._update_recent_menu)
        self.recent_menu.triggered.connect(self._load_recent)
        if not self.recent:
            self.recent_menu.setEnabled(False)
        self.actions["close_file"] = file_menu.addAction(
            "&Close", self.model.remove_data, QKeySequence.Close)
        self.actions["close_all"] = file_menu.addAction(
            "Close all", self.close_all)
        file_menu.addSeparator()
        icon = QIcon.fromTheme("meta-info")
        self.actions["meta_info"] = file_menu.addAction(
            icon, "Show information...", self.meta_info)
        file_menu.addSeparator()
        self.actions["import_bads"] = file_menu.addAction(
            "Import bad channels...", lambda: self.import_file(
                model.import_bads, "Import bad channels", "*.csv"))
        self.actions["import_events"] = file_menu.addAction(
            "Import events...", lambda: self.import_file(
                model.import_events, "Import events", "*.csv"))
        self.actions["import_annotations"] = file_menu.addAction(
            "Import annotations...", lambda: self.import_file(
                model.import_annotations, "Import annotations", "*.csv"))
        self.actions["import_ica"] = file_menu.addAction(
            "Import &ICA...", lambda: self.open_file(
                model.import_ica, "Import ICA", "*.fif *.fif.gz"))
        file_menu.addSeparator()
        self.export_menu = file_menu.addMenu("Export data")
        for ext, description in writers.items():
            action = "export_data" + ext.replace(".", "_")
            self.actions[action] = self.export_menu.addAction(
                f"{ext[1:].upper()} ({description[1]})...",
                partial(self.export_file, model.export_data, "Export data",
                        "*" + ext))
        self.actions["export_bads"] = file_menu.addAction(
            "Export &bad channels...", lambda: self.export_file(
                model.export_bads, "Export bad channels", "*.csv"))
        self.actions["export_events"] = file_menu.addAction(
            "Export &events...", lambda: self.export_file(
                model.export_events, "Export events", "*.csv"))
        self.actions["export_annotations"] = file_menu.addAction(
            "Export &annotations...", lambda: self.export_file(
                model.export_annotations, "Export annotations", "*.csv"))
        self.actions["export_ica"] = file_menu.addAction(
            "Export ICA...", lambda: self.export_file(
                model.export_ica, "Export ICA", "*.fif *.fif.gz"))
        file_menu.addSeparator()
        self.actions["xdf_chunks"] = file_menu.addAction(
            "Show XDF chunks...", self.xdf_chunks)
        file_menu.addSeparator()
        self.actions["quit"] = file_menu.addAction("&Quit", self.close,
                                                   QKeySequence.Quit)

        edit_menu = self.menuBar().addMenu("&Edit")
        self.actions["pick_chans"] = edit_menu.addAction(
            "P&ick channels...", self.pick_channels)
        icon = QIcon.fromTheme("chan-props")
        self.actions["chan_props"] = edit_menu.addAction(
            icon, "Channel &properties...", self.channel_properties)
        self.actions["set_montage"] = edit_menu.addAction(
            "Set &montage...", self.set_montage)
        edit_menu.addSeparator()
        self.actions["set_ref"] = edit_menu.addAction("Set &reference...",
                                                      self.set_reference)
        edit_menu.addSeparator()
        self.actions["annotations"] = edit_menu.addAction(
            "&Annotations...", self.edit_annotations)
        self.actions["events"] = edit_menu.addAction("&Events...",
                                                     self.edit_events)

        edit_menu.addSeparator()
        self.actions["crop"] = edit_menu.addAction("&Crop data...", self.crop)
        self.actions["append_data"] = edit_menu.addAction(
            "Appen&d data...", self.append_data)

        plot_menu = self.menuBar().addMenu("&Plot")
        icon = QIcon.fromTheme("plot-data")
        self.actions["plot_data"] = plot_menu.addAction(
            icon, "&Data", self.plot_data)
        icon = QIcon.fromTheme("plot-psd")
        self.actions["plot_psd"] = plot_menu.addAction(
            icon, "&Power spectral density", self.plot_psd)
        icon = QIcon.fromTheme("plot-locations")
        self.actions["plot_locations"] = plot_menu.addAction(
            icon, "&Channel locations", self.plot_locations)
        self.actions["plot_erds"] = plot_menu.addAction(
            "&ERDS maps...", self.plot_erds)
        plot_menu.addSeparator()
        self.actions["plot_ica_components"] = plot_menu.addAction(
            "ICA &components...", self.plot_ica_components)
        self.actions["plot_ica_sources"] = plot_menu.addAction(
            "ICA &sources...", self.plot_ica_sources)

        tools_menu = self.menuBar().addMenu("&Tools")
        icon = QIcon.fromTheme("filter-data")
        self.actions["filter"] = tools_menu.addAction(icon, "&Filter data...",
                                                      self.filter_data)
        icon = QIcon.fromTheme("find-events")
        self.actions["find_events"] = tools_menu.addAction(
            icon, "Find &events...", self.find_events)
        self.actions["events_from_annotations"] = tools_menu.addAction(
            "Create events from annotations", self.events_from_annotations)
        self.actions["annotations_from_events"] = tools_menu.addAction(
            "Create annotations from events", self.annotations_from_events)
        tools_menu.addSeparator()
        nirs_menu = tools_menu.addMenu("NIRS")
        self.actions["convert_od"] = nirs_menu.addAction(
            "Convert to &optical density", self.convert_od)
        self.actions["convert_bl"] = nirs_menu.addAction(
            "Convert to &haemoglobin", self.convert_bl)

        tools_menu.addSeparator()
        icon = QIcon.fromTheme("run-ica")
        self.actions["run_ica"] = tools_menu.addAction(icon, "Run &ICA...",
                                                       self.run_ica)
        self.actions["apply_ica"] = tools_menu.addAction(
            "Apply &ICA", self.apply_ica)
        tools_menu.addSeparator()
        self.actions["interpolate_bads"] = tools_menu.addAction(
            "Interpolate bad channels...", self.interpolate_bads)
        tools_menu.addSeparator()
        icon = QIcon.fromTheme("epoch-data")
        self.actions["epoch_data"] = tools_menu.addAction(
            icon, "Create epochs...", self.epoch_data)

        view_menu = self.menuBar().addMenu("&View")
        self.actions["history"] = view_menu.addAction("&History...",
                                                      self.show_history)
        self.actions["toolbar"] = view_menu.addAction("&Toolbar",
                                                      self._toggle_toolbar)
        self.actions["toolbar"].setCheckable(True)
        self.actions["statusbar"] = view_menu.addAction(
            "&Statusbar", self._toggle_statusbar)
        self.actions["statusbar"].setCheckable(True)

        help_menu = self.menuBar().addMenu("&Help")
        self.actions["about"] = help_menu.addAction("&About", self.show_about)
        self.actions["about_qt"] = help_menu.addAction("About &Qt",
                                                       self.show_about_qt)

        # actions that are always enabled
        self.always_enabled = [
            "open_file", "about", "about_qt", "quit", "xdf_chunks", "toolbar",
            "statusbar"
        ]

        # set up toolbar
        self.toolbar = self.addToolBar("toolbar")
        self.toolbar.setObjectName("toolbar")
        self.toolbar.addAction(self.actions["open_file"])
        self.toolbar.addAction(self.actions["meta_info"])
        self.toolbar.addSeparator()
        self.toolbar.addAction(self.actions["chan_props"])
        self.toolbar.addSeparator()
        self.toolbar.addAction(self.actions["plot_data"])
        self.toolbar.addAction(self.actions["plot_psd"])
        self.toolbar.addAction(self.actions["plot_locations"])
        self.toolbar.addSeparator()
        self.toolbar.addAction(self.actions["filter"])
        self.toolbar.addAction(self.actions["find_events"])
        self.toolbar.addAction(self.actions["epoch_data"])
        self.toolbar.addAction(self.actions["run_ica"])
        self.toolbar.setMovable(False)
        self.setUnifiedTitleAndToolBarOnMac(True)
        if settings["toolbar"]:
            self.toolbar.show()
            self.actions["toolbar"].setChecked(True)
        else:
            self.toolbar.hide()
            self.actions["toolbar"].setChecked(False)

        # set up data model for sidebar (list of open files)
        self.names = QStringListModel()
        self.names.dataChanged.connect(self._update_names)
        splitter = QSplitter()
        self.sidebar = QListView()
        self.sidebar.setFrameStyle(QFrame.NoFrame)
        self.sidebar.setFocusPolicy(Qt.NoFocus)
        self.sidebar.setModel(self.names)
        self.sidebar.clicked.connect(self._update_data)
        splitter.addWidget(self.sidebar)
        self.infowidget = InfoWidget()
        splitter.addWidget(self.infowidget)
        width = splitter.size().width()
        splitter.setSizes((int(width * 0.3), int(width * 0.7)))
        self.setCentralWidget(splitter)

        self.status_label = QLabel()
        self.statusBar().addPermanentWidget(self.status_label)
        if settings["statusbar"]:
            self.statusBar().show()
            self.actions["statusbar"].setChecked(True)
        else:
            self.statusBar().hide()
            self.actions["statusbar"].setChecked(False)

        self.setAcceptDrops(True)
        self.data_changed()

    def data_changed(self):
        # update sidebar
        self.names.setStringList(self.model.names)
        self.sidebar.setCurrentIndex(self.names.index(self.model.index))

        # update info widget
        if self.model.data:
            self.infowidget.set_values(self.model.get_info())
        else:
            self.infowidget.clear()

        # update status bar
        if self.model.data:
            mb = self.model.nbytes / 1024**2
            self.status_label.setText(f"Total Memory: {mb:.2f} MB")
        else:
            self.status_label.clear()

        # toggle actions
        if len(self.model) == 0:  # disable if no data sets are currently open
            enabled = False
        else:
            enabled = True

        for name, action in self.actions.items():  # toggle
            if name not in self.always_enabled:
                action.setEnabled(enabled)

        if self.model.data:  # toggle if specific conditions are met
            bads = bool(self.model.current["data"].info["bads"])
            self.actions["export_bads"].setEnabled(enabled and bads)
            events = self.model.current["events"] is not None
            self.actions["export_events"].setEnabled(enabled and events)
            if self.model.current["dtype"] == "raw":
                annot = bool(self.model.current["data"].annotations)
            else:
                annot = False
            self.actions["export_annotations"].setEnabled(enabled and annot)
            self.actions["annotations"].setEnabled(enabled and annot)
            locations = has_locations(self.model.current["data"].info)
            self.actions["plot_locations"].setEnabled(enabled and locations)
            ica = bool(self.model.current["ica"])
            self.actions["apply_ica"].setEnabled(enabled and ica)
            self.actions["export_ica"].setEnabled(enabled and ica)
            self.actions["plot_erds"].setEnabled(
                enabled and self.model.current["dtype"] == "epochs")
            self.actions["plot_ica_components"].setEnabled(enabled and ica
                                                           and locations)
            self.actions["plot_ica_sources"].setEnabled(enabled and ica)
            self.actions["interpolate_bads"].setEnabled(enabled and locations
                                                        and bads)
            self.actions["events"].setEnabled(enabled and events)
            self.actions["events_from_annotations"].setEnabled(enabled
                                                               and annot)
            self.actions["annotations_from_events"].setEnabled(enabled
                                                               and events)
            self.actions["find_events"].setEnabled(
                enabled and self.model.current["dtype"] == "raw")
            self.actions["epoch_data"].setEnabled(
                enabled and events and self.model.current["dtype"] == "raw")
            self.actions["crop"].setEnabled(
                enabled and self.model.current["dtype"] == "raw")
            append = bool(self.model.get_compatibles())
            self.actions["append_data"].setEnabled(
                enabled and append
                and (self.model.current["dtype"] in ("raw", "epochs")))
            self.actions["meta_info"].setEnabled(
                enabled
                and self.model.current["ftype"] in ["XDF", "XDFZ", "XDF.GZ"])
            self.actions["convert_od"].setEnabled(
                len(
                    mne.pick_types(self.model.current["data"].info,
                                   fnirs="fnirs_cw_amplitude")))
            self.actions["convert_bl"].setEnabled(
                len(
                    mne.pick_types(self.model.current["data"].info,
                                   fnirs="fnirs_od")))
            # disable unsupported exporters for epochs (all must support raw)
            if self.model.current["dtype"] == "epochs":
                for ext, description in writers.items():
                    action = "export_data" + ext.replace(".", "_")
                    if "epoch" in description[2]:
                        self.actions[action].setEnabled(True)
                    else:
                        self.actions[action].setEnabled(False)
        # add to recent files
        if len(self.model) > 0:
            self._add_recent(self.model.current["fname"])

    def open_data(self, fname=None):
        """Open raw file."""
        if fname is None:
            fname = QFileDialog.getOpenFileName(self, "Open raw")[0]
        if fname:
            if not (Path(fname).is_file() or Path(fname).is_dir()):
                self._remove_recent(fname)
                QMessageBox.critical(self, "File does not exist",
                                     f"File {fname} does not exist anymore.")
                return

            ext = "".join(Path(fname).suffixes)

            if any([ext.endswith(e) for e in (".xdf", ".xdfz", ".xdf.gz")]):
                rows, disabled = [], []
                for idx, s in enumerate(resolve_streams(fname)):
                    rows.append([
                        s["stream_id"], s["name"], s["type"],
                        s["channel_count"], s["channel_format"],
                        s["nominal_srate"]
                    ])
                    is_marker = (s["nominal_srate"] == 0
                                 or s["channel_format"] == "string")
                    if is_marker:  # disable marker streams
                        disabled.append(idx)

                enabled = list(set(range(len(rows))) - set(disabled))
                if enabled:
                    selected = enabled[0]
                else:
                    selected = None
                dialog = XDFStreamsDialog(self,
                                          rows,
                                          selected=selected,
                                          disabled=disabled)
                if dialog.exec():
                    row = dialog.view.selectionModel().selectedRows()[0].row()
                    stream_id = dialog.model.data(dialog.model.index(row, 0))
                    srate = "effective" if dialog.effective_srate else "nominal"
                    prefix_markers = dialog.prefix_markers
                    kwargs = {}
                    if srate == "nominal":
                        kwargs["srate"] = srate
                    if prefix_markers:
                        kwargs["prefix_markers"] = prefix_markers
                    self.model.load(fname, stream_id=stream_id, **kwargs)
            else:  # all other file formats
                try:
                    self.model.load(fname)
                except FileNotFoundError as e:
                    QMessageBox.critical(self, "File not found", str(e))
                except ValueError as e:
                    QMessageBox.critical(self, "Unknown file type", str(e))

    def open_file(self, f, text, ffilter="*"):
        """Open file."""
        fname = QFileDialog.getOpenFileName(self, text, filter=ffilter)[0]
        if fname:
            f(fname)

    def xdf_chunks(self):
        """Show XDF chunks."""
        fname = QFileDialog.getOpenFileName(self,
                                            "Select XDF file",
                                            filter="*.xdf *.xdfz *.xdf.gz")[0]
        if fname:
            chunks = list_chunks(fname)
            dialog = XDFChunksDialog(self, chunks, fname)
            dialog.exec_()

    def export_file(self, f, text, ffilter="*"):
        """Export to file."""
        fname = QFileDialog.getSaveFileName(self, text, filter=ffilter)[0]
        if fname:
            if ffilter != "*":
                exts = [ext.replace("*", "") for ext in ffilter.split()]

                maxsuffixes = max([ext.count(".") for ext in exts])
                suffixes = Path(fname).suffixes
                for i in range(-maxsuffixes, 0):
                    ext = "".join(suffixes[i:])
                    if ext in exts:
                        return f(fname)
                fname = fname + exts[0]
                return f(fname)

    def import_file(self, f, text, ffilter="*"):
        """Import file."""
        fname = QFileDialog.getOpenFileName(self, text, filter=ffilter)[0]
        if fname:
            try:
                f(fname)
            except LabelsNotFoundError as e:
                QMessageBox.critical(self, "Channel labels not found", str(e))
            except InvalidAnnotationsError as e:
                QMessageBox.critical(self, "Invalid annotations", str(e))

    def close_all(self):
        """Close all currently open data sets."""
        msg = QMessageBox.question(self, "Close all data sets",
                                   "Close all data sets?")
        if msg == QMessageBox.Yes:
            while len(self.model) > 0:
                self.model.remove_data()

    def meta_info(self):
        """Show XDF meta info."""
        xml = get_xml(self.model.current["fname"])
        dialog = MetaInfoDialog(self, xml)
        dialog.exec()

    def pick_channels(self):
        """Pick channels in current data set."""
        channels = self.model.current["data"].info["ch_names"]
        dialog = PickChannelsDialog(self, channels, selected=channels)
        if dialog.exec():
            picks = [item.data(0) for item in dialog.channels.selectedItems()]
            drops = list(set(channels) - set(picks))
            if drops:
                self.auto_duplicate()
                self.model.drop_channels(drops)
                self.model.history.append(f"data.drop_channels({drops})")

    def channel_properties(self):
        """Show channel properties dialog."""
        info = self.model.current["data"].info
        dialog = ChannelPropertiesDialog(self, info)
        if dialog.exec():
            dialog.model.sort(0)
            bads = []
            renamed = {}
            types = {}
            for i in range(dialog.model.rowCount()):
                new_label = dialog.model.item(i, 1).data(Qt.DisplayRole)
                old_label = info["ch_names"][i]
                if new_label != old_label:
                    renamed[old_label] = new_label
                new_type = dialog.model.item(i, 2).data(Qt.DisplayRole).lower()
                old_type = channel_type(info, i).lower()
                if new_type != old_type:
                    types[new_label] = new_type
                if dialog.model.item(i, 3).checkState() == Qt.Checked:
                    bads.append(info["ch_names"][i])
            self.model.set_channel_properties(bads, renamed, types)

    def set_montage(self):
        """Set montage."""
        montages = mne.channels.get_builtin_montages()
        # TODO: currently it is not possible to remove an existing montage
        dialog = MontageDialog(self, montages)
        if dialog.exec():
            name = dialog.montages.selectedItems()[0].data(0)
            montage = mne.channels.make_standard_montage(name)
            ch_names = self.model.current["data"].info["ch_names"]
            # check if at least one channel name matches a name in the montage
            if set(ch_names) & set(montage.ch_names):
                self.model.set_montage(name)
            else:
                QMessageBox.critical(
                    self, "No matching channel names",
                    "Channel names defined in the "
                    "montage do not match any channel name in the data.")

    def edit_annotations(self):
        fs = self.model.current["data"].info["sfreq"]
        pos = self.model.current["data"].annotations.onset
        pos = (pos * fs).astype(int).tolist()
        dur = self.model.current["data"].annotations.duration
        dur = (dur * fs).astype(int).tolist()
        desc = self.model.current["data"].annotations.description.tolist()
        dialog = AnnotationsDialog(self, pos, dur, desc)
        if dialog.exec():
            rows = dialog.table.rowCount()
            onset, duration, description = [], [], []
            for i in range(rows):
                data = dialog.table.item(i, 0).data(Qt.DisplayRole)
                onset.append(float(data) / fs)
                data = dialog.table.item(i, 1).data(Qt.DisplayRole)
                duration.append(float(data) / fs)
                data = dialog.table.item(i, 2).data(Qt.DisplayRole)
                description.append(data)
            self.model.set_annotations(onset, duration, description)

    def edit_events(self):
        pos = self.model.current["events"][:, 0].tolist()
        desc = self.model.current["events"][:, 2].tolist()
        dialog = EventsDialog(self, pos, desc)
        if dialog.exec():
            rows = dialog.table.rowCount()
            events = np.zeros((rows, 3), dtype=int)
            for i in range(rows):
                pos = int(dialog.table.item(i, 0).data(Qt.DisplayRole))
                desc = int(dialog.table.item(i, 1).data(Qt.DisplayRole))
                events[i] = pos, 0, desc
            self.model.set_events(events)

    def crop(self):
        """Crop data."""
        fs = self.model.current["data"].info["sfreq"]
        length = self.model.current["data"].n_times / fs
        dialog = CropDialog(self, 0, length)
        if dialog.exec():
            self.auto_duplicate()
            self.model.crop(dialog.start or 0, dialog.stop)

    def append_data(self):
        """Concatenate raw data objects to current one."""
        compatibles = self.model.get_compatibles()
        dialog = AppendDialog(self, compatibles)
        if dialog.exec():
            self.auto_duplicate()
            self.model.append_data(dialog.names)

    def plot_data(self):
        """Plot data."""
        # self.bad is needed to update history if bad channels are selected in
        # the interactive plot window (see also self.eventFilter)
        self.bads = self.model.current["data"].info["bads"]
        events = self.model.current["events"]
        nchan = self.model.current["data"].info["nchan"]
        fig = self.model.current["data"].plot(events=events,
                                              n_channels=nchan,
                                              title=self.model.current["name"],
                                              scalings="auto",
                                              show=False)
        if events is not None:
            hist = f"data.plot(events=events, n_channels={nchan})"
        else:
            hist = f"data.plot(n_channels={nchan})"
        self.model.history.append(hist)
        win = fig.canvas.manager.window
        win.setWindowTitle(self.model.current["name"])
        win.statusBar().hide()  # not necessary since matplotlib 3.3
        win.installEventFilter(self)  # detect if the figure is closed

        # prevent closing the window with the escape key
        try:
            fig._mne_params["close_key"] = None
        except AttributeError:  # does not exist in older MNE versions
            pass

        fig.show()

    def plot_psd(self):
        """Plot power spectral density (PSD)."""
        kwds = {}
        if self.model.current["dtype"] == "raw":
            kwds.update({"average": False, "spatial_colors": False})
        fig = self.model.current["data"].plot_psd(show=False, **kwds)
        if kwds:
            tmp = ", ".join(f"{key}={value}" for key, value in kwds.items())
            hist = f"data.plot_psd({tmp})"
        else:
            hist = "data.plot_psd()"
        self.model.history.append(hist)
        win = fig.canvas.manager.window
        win.setWindowTitle("Power spectral density")
        fig.show()

    def plot_locations(self):
        """Plot current montage."""
        fig = self.model.current["data"].plot_sensors(show_names=True,
                                                      show=False)
        win = fig.canvas.manager.window
        win.setWindowTitle("Montage")
        win.statusBar().hide()  # not necessary since matplotlib 3.3
        fig.show()

    def plot_ica_components(self):
        self.model.current["ica"].plot_components(
            inst=self.model.current["data"])

    def plot_ica_sources(self):
        self.model.current["ica"].plot_sources(inst=self.model.current["data"])

    def plot_erds(self):
        """Plot ERDS maps."""
        data = self.model.current["data"]
        t_range = [data.tmin, data.tmax]
        f_range = [1, data.info["sfreq"] / 2]

        dialog = ERDSDialog(self, t_range, f_range)

        if dialog.exec():
            freqs = np.arange(dialog.f1, dialog.f2, dialog.step)
            baseline = [dialog.b1, dialog.b2]
            times = [dialog.t1, dialog.t2]
            figs = plot_erds(data, freqs, freqs, baseline, times)
            for fig in figs:
                fig.show()

    def run_ica(self):
        """Run ICA calculation."""

        methods = ["Infomax"]
        if have["picard"]:
            methods.insert(0, "Picard")
        if have["sklearn"]:
            methods.append("FastICA")

        dialog = RunICADialog(self, self.model.current["data"].info["nchan"],
                              methods)

        if dialog.exec():
            calc = CalcDialog(self, "Calculating ICA", "Calculating ICA.")

            method = dialog.method.currentText().lower()
            exclude_bad_segments = dialog.exclude_bad_segments.isChecked()

            fit_params = {}
            if dialog.extended.isEnabled():
                fit_params["extended"] = dialog.extended.isChecked()
            if dialog.ortho.isEnabled():
                fit_params["ortho"] = dialog.ortho.isChecked()

            ica = mne.preprocessing.ICA(method=method, fit_params=fit_params)
            history = f"ica = mne.preprocessing.ICA(method='{method}'"
            if fit_params:
                history += f", fit_params={fit_params})"
            else:
                history += ")"
            self.model.history.append(history)

            pool = mp.Pool(processes=1)

            def callback(x):
                QMetaObject.invokeMethod(calc, "accept", Qt.QueuedConnection)

            res = pool.apply_async(
                func=ica.fit,
                args=(self.model.current["data"], ),
                kwds={"reject_by_annotation": exclude_bad_segments},
                callback=callback)
            pool.close()

            if not calc.exec():
                pool.terminate()
                print("ICA calculation aborted...")
            else:
                self.model.current["ica"] = res.get(timeout=1)
                self.model.history.append(
                    f"ica.fit(inst=raw, reject_by_annotation="
                    f"{exclude_bad_segments})")
                self.data_changed()

    def apply_ica(self):
        """Apply current fitted ICA."""
        self.auto_duplicate()
        self.model.apply_ica()

    def interpolate_bads(self):
        """Interpolate bad channels"""
        dialog = InterpolateBadsDialog(self)
        if dialog.exec():
            duplicated = self.auto_duplicate()
            try:
                self.model.interpolate_bads(dialog.reset_bads, dialog.mode,
                                            dialog.origin)
            except ValueError as e:
                if duplicated:  # undo
                    self.model.remove_data()
                    self.model.index -= 1
                    self.data_changed()
                msgbox = ErrorMessageBox(self,
                                         "Could not interpolate bad channels",
                                         str(e), traceback.format_exc())
                msgbox.show()

    def filter_data(self):
        """Filter data."""
        dialog = FilterDialog(self)
        if dialog.exec():
            self.auto_duplicate()
            self.model.filter(dialog.low, dialog.high)

    def find_events(self):
        info = self.model.current["data"].info

        # use first stim channel as default in dialog
        default_stim = 0
        for i in range(info["nchan"]):
            if mne.io.pick.channel_type(info, i) == "stim":
                default_stim = i
                break
        dialog = FindEventsDialog(self, info["ch_names"], default_stim)
        if dialog.exec():
            stim_channel = dialog.stimchan.currentText()
            consecutive = dialog.consecutive.isChecked()
            initial_event = dialog.initial_event.isChecked()
            uint_cast = dialog.uint_cast.isChecked()
            min_dur = dialog.minduredit.value()
            shortest_event = dialog.shortesteventedit.value()
            self.model.find_events(stim_channel=stim_channel,
                                   consecutive=consecutive,
                                   initial_event=initial_event,
                                   uint_cast=uint_cast,
                                   min_duration=min_dur,
                                   shortest_event=shortest_event)

    def events_from_annotations(self):
        self.model.events_from_annotations()

    def annotations_from_events(self):
        self.model.annotations_from_events()

    def epoch_data(self):
        """Epoch raw data."""
        dialog = EpochDialog(self, self.model.current["events"])
        if dialog.exec():
            events = [
                int(item.text()) for item in dialog.events.selectedItems()
            ]
            tmin = dialog.tmin.value()
            tmax = dialog.tmax.value()

            if dialog.baseline.isChecked():
                baseline = dialog.a.value(), dialog.b.value()
            else:
                baseline = None

            duplicated = self.auto_duplicate()
            try:
                self.model.epoch_data(events, tmin, tmax, baseline)
            except ValueError as e:
                if duplicated:  # undo
                    self.model.remove_data()
                    self.model.index -= 1
                    self.data_changed()
                msgbox = ErrorMessageBox(self, "Could not create epochs",
                                         str(e), traceback.format_exc())
                msgbox.show()

    def convert_od(self):
        """Convert to optical density."""
        self.auto_duplicate()
        self.model.convert_od()

    def convert_bl(self):
        """Convert to haemoglobin."""
        self.auto_duplicate()
        self.model.convert_beer_lambert()

    def set_reference(self):
        """Set reference."""
        dialog = ReferenceDialog(self)
        if dialog.exec():
            self.auto_duplicate()
            if dialog.average.isChecked():
                self.model.set_reference("average")
            else:
                ref = [c.strip() for c in dialog.channellist.text().split(",")]
                self.model.set_reference(ref)

    def show_history(self):
        """Show history."""
        dialog = HistoryDialog(self, "\n".join(self.model.history))
        dialog.exec()

    def show_about(self):
        """Show About dialog."""
        from . import __version__

        msg_box = QMessageBox(self)
        text = (
            f"<img src='{image_path('mnelab_logo.png')}'><p>MNELAB {__version__}</p>"
        )
        msg_box.setText(text)

        mnelab_url = "github.com/cbrnr/mnelab"
        mne_url = "github.com/mne-tools/mne-python"

        pkgs = []
        for key, value in have.items():
            if value:
                pkgs.append(f"{key}&nbsp;({value})")
            else:
                pkgs.append(f"{key}&nbsp;(not installed)")
        version = ".".join(str(k) for k in version_info[:3])
        text = (
            f"<nobr><p>This program uses Python {version} and the following packages:"
            f"</p></nobr><p>{', '.join(pkgs)}</p>"
            f"<nobr><p>MNELAB repository: <a href=https://{mnelab_url}>{mnelab_url}</a>"
            f"</p></nobr><nobr><p>MNE repository: "
            f"<a href=https://{mne_url}>{mne_url}</a></p></nobr>"
            f"<p>Licensed under the BSD 3-clause license.</p>"
            f"<p>Copyright 2017&ndash;2021 by Clemens Brunner.</p>")
        msg_box.setInformativeText(text)
        msg_box.exec()

    def show_about_qt(self):
        """Show About Qt dialog."""
        QMessageBox.aboutQt(self, "About Qt")

    def auto_duplicate(self):
        """Automatically duplicate current data set.

        If the current data set is stored in a file (i.e. was loaded directly from a file),
        a new data set is automatically created. If the current data set is not stored in a
        file (i.e. was created by operations in MNELAB), a dialog box asks the user if the
        current data set should be overwritten or duplicated.

        Returns
        -------
        duplicated : bool
            True if the current data set was automatically duplicated, False if the current
            data set was overwritten.
        """
        # if current data is stored in a file create a new data set
        if self.model.current["fname"]:
            self.model.duplicate_data()
            return True
        # otherwise ask the user
        else:
            msg = QMessageBox.question(self, "Overwrite existing data set",
                                       "Overwrite existing data set?")
            if msg == QMessageBox.No:  # create new data set
                self.model.duplicate_data()
                return True
        return False

    def _add_recent(self, fname):
        """Add a file to recent file list.

        Parameters
        ----------
        fname : str
            File name.
        """
        if fname in self.recent:  # avoid duplicates
            self.recent.remove(fname)
        self.recent.insert(0, fname)
        while len(self.recent) > MAX_RECENT:  # prune list
            self.recent.pop()
        write_settings(recent=self.recent)
        if not self.recent_menu.isEnabled():
            self.recent_menu.setEnabled(True)

    def _remove_recent(self, fname):
        """Remove file from recent file list.

        Parameters
        ----------
        fname : str
            File name.
        """
        if fname in self.recent:
            self.recent.remove(fname)
            write_settings(recent=self.recent)
            if not self.recent:
                self.recent_menu.setEnabled(False)

    @Slot(QModelIndex)
    def _update_data(self, selected):
        """Update index and information based on the state of the sidebar.

        Parameters
        ----------
        selected : QModelIndex
            Index of the selected row.
        """
        if selected.row() != self.model.index:
            self.model.index = selected.row()
            self.data_changed()
            self.model.history.append(f"data = datasets[{self.model.index}]")

    @Slot(QModelIndex, QModelIndex)
    def _update_names(self, start, stop):
        """Update names in DataSets after changes in sidebar."""
        for index in range(start.row(), stop.row() + 1):
            self.model.data[index]["name"] = self.names.stringList()[index]

    @Slot()
    def _update_recent_menu(self):
        self.recent_menu.clear()
        for recent in self.recent:
            self.recent_menu.addAction(recent)

    @Slot(QAction)
    def _load_recent(self, action):
        self.open_data(fname=action.text())

    @Slot()
    def _toggle_toolbar(self):
        if self.toolbar.isHidden():
            self.toolbar.show()
        else:
            self.toolbar.hide()
        write_settings(toolbar=not self.toolbar.isHidden())

    @Slot()
    def _toggle_statusbar(self):
        if self.statusBar().isHidden():
            self.statusBar().show()
        else:
            self.statusBar().hide()
        write_settings(statusbar=not self.statusBar().isHidden())

    @Slot(QDropEvent)
    def dragEnterEvent(self, event):
        if event.mimeData().hasUrls():
            event.acceptProposedAction()

    @Slot(QDropEvent)
    def dropEvent(self, event):
        mime = event.mimeData()
        if mime.hasUrls():
            urls = mime.urls()
            for url in urls:
                try:
                    self.open_data(url.toLocalFile())
                except FileNotFoundError as e:
                    QMessageBox.critical(self, "File not found", str(e))

    @Slot(QEvent)
    def closeEvent(self, event):
        """Close application.

        Parameters
        ----------
        event : QEvent
            Close event.
        """
        write_settings(size=self.size(), pos=self.pos())
        if self.model.history:
            print("\n# Command History\n")
            print("\n".join(self.model.history))
        QApplication.quit()

    def eventFilter(self, source, event):
        # currently the only source is the raw plot window
        if event.type() == QEvent.Close:
            self.data_changed()
            bads = self.model.current["data"].info["bads"]
            if self.bads != bads:
                self.model.history.append(f'data.info["bads"] = {bads}')
        return QObject.eventFilter(self, source, event)

    def event(self, ev):
        """Catch system events."""
        if ev.type() == QEvent.PaletteChange:  # detect theme switches
            style = interface_style()  # light or dark
            if style is not None:
                QIcon.setThemeName(style)
            else:
                QIcon.setThemeName("light")  # fallback
        return super().event(ev)
Example #7
0
    def _setup_gui(self):
        # setup 3 widget model view : QTreeView - QListView - QListView
        self.dataset_tree_view, self.dataset_count_label, dataset_widget = self._setup_view_widget(
            QTreeView(), 'Dataset', self.dataset_model, self.select_dataset,
            -1)
        self.image_label_list_view, self.image_label_count_label, image_label_widget = self._setup_view_widget(
            QListView(), 'Label', self.label_model, self.select_label)
        self.image_list_view, self.image_count_label, image_widget = self._setup_view_widget(
            QListView(), 'Image', self.image_model, self.select_image)

        # configure QTreeView
        self.dataset_tree_view.header_hidden = False
        self.dataset_tree_view.horizontal_scroll_mode = QTreeView.ScrollPerPixel
        self.dataset_tree_view.horizontal_scroll_bar_policy = Qt.ScrollBarAsNeeded
        self.dataset_tree_view.selection_mode = QTreeView.SingleSelection
        self.dataset_tree_view.edit_triggers = QtWidgets.QAbstractItemView.NoEditTriggers

        # setup image view widget
        self.image_info_widget = KlustRImageInfoWidget()

        # layouting main widget
        view_widget = QWidget()
        view_layout = QHBoxLayout(view_widget)
        view_layout.add_widget(dataset_widget)
        view_layout.add_widget(image_label_widget)
        view_layout.add_widget(image_widget)

        view_splitter = QSplitter()
        view_splitter.orientation = Qt.Horizontal
        view_splitter.add_widget(view_widget)
        view_splitter.add_widget(self.image_info_widget)
        view_splitter.size_policy = QSizePolicy(QSizePolicy.Preferred,
                                                QSizePolicy.MinimumExpanding)

        # setup transformation filter widget
        self.filter_box = QGroupBox('Filter')
        self.filter_box.enabled = False

        self.translation_checkbox = QCheckBox('Translation')
        self.rotation_checkbox = QCheckBox('Rotation')
        self.scaling_checkbox = QCheckBox('Scaling')
        self.exclusive_checkbox = QCheckBox('Exclusive')

        self.translation_checkbox.checked = True
        self.rotation_checkbox.checked = True
        self.scaling_checkbox.checked = True
        self.exclusive_checkbox.checked = False

        # setup transformation filter widget
        filter_layout = QHBoxLayout(self.filter_box)
        filter_layout.add_widget(self.translation_checkbox)
        filter_layout.add_widget(self.rotation_checkbox)
        filter_layout.add_widget(self.scaling_checkbox)
        filter_layout.add_widget(self.exclusive_checkbox)
        filter_layout.add_stretch()

        # connect transformation filter widget
        self.translation_checkbox.stateChanged.connect(self._update_models)
        self.rotation_checkbox.stateChanged.connect(self._update_models)
        self.scaling_checkbox.stateChanged.connect(self._update_models)
        self.exclusive_checkbox.stateChanged.connect(self._update_models)

        # main layouting
        layout = QVBoxLayout(self)
        layout.add_widget(view_splitter)
        layout.add_widget(self.filter_box)
Example #8
0
 def init_ui(self):
     self.setLayout(QHBoxLayout())
     self.layout().addWidget(QListView())
Example #9
0
    def setupUi(self, tfm):
        if not tfm.objectName():
            tfm.setObjectName(u"tfm")
        tfm.resize(800, 600)
        tfm.setContextMenuPolicy(Qt.NoContextMenu)
        tfm.setLocale(QLocale(QLocale.English, QLocale.UnitedStates))
        self.action_menu = QAction(tfm)
        self.action_menu.setObjectName(u"action_menu")
        self.action_new_dir = QAction(tfm)
        self.action_new_dir.setObjectName(u"action_new_dir")
        self.action_new_file = QAction(tfm)
        self.action_new_file.setObjectName(u"action_new_file")
        self.action_back = QAction(tfm)
        self.action_back.setObjectName(u"action_back")
        self.action_up = QAction(tfm)
        self.action_up.setObjectName(u"action_up")
        self.action_home = QAction(tfm)
        self.action_home.setObjectName(u"action_home")
        self.action_go = QAction(tfm)
        self.action_go.setObjectName(u"action_go")
        self.action_forward = QAction(tfm)
        self.action_forward.setObjectName(u"action_forward")
        self.action_copy = QAction(tfm)
        self.action_copy.setObjectName(u"action_copy")
        self.action_paste = QAction(tfm)
        self.action_paste.setObjectName(u"action_paste")
        self.action_cut = QAction(tfm)
        self.action_cut.setObjectName(u"action_cut")
        self.action_show_hidden = QAction(tfm)
        self.action_show_hidden.setObjectName(u"action_show_hidden")
        self.action_show_hidden.setCheckable(True)
        self.action_delete = QAction(tfm)
        self.action_delete.setObjectName(u"action_delete")
        self.action_rename = QAction(tfm)
        self.action_rename.setObjectName(u"action_rename")
        self.action_add_to_bookmarks = QAction(tfm)
        self.action_add_to_bookmarks.setObjectName(u"action_add_to_bookmarks")
        self.action_remove_bookmark = QAction(tfm)
        self.action_remove_bookmark.setObjectName(u"action_remove_bookmark")
        self.action_copy_path = QAction(tfm)
        self.action_copy_path.setObjectName(u"action_copy_path")
        self.action_extract_here = QAction(tfm)
        self.action_extract_here.setObjectName(u"action_extract_here")
        self.action_mount_iso = QAction(tfm)
        self.action_mount_iso.setObjectName(u"action_mount_iso")
        self.centralwidget = QWidget(tfm)
        self.centralwidget.setObjectName(u"centralwidget")
        self.horizontalLayout = QHBoxLayout(self.centralwidget)
        self.horizontalLayout.setObjectName(u"horizontalLayout")
        self.vsplit = QSplitter(self.centralwidget)
        self.vsplit.setObjectName(u"vsplit")
        self.vsplit.setOrientation(Qt.Horizontal)
        self.hsplit = QSplitter(self.vsplit)
        self.hsplit.setObjectName(u"hsplit")
        self.hsplit.setOrientation(Qt.Vertical)
        self.fs_tree = QTreeView(self.hsplit)
        self.fs_tree.setObjectName(u"fs_tree")
        sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(1)
        sizePolicy.setHeightForWidth(
            self.fs_tree.sizePolicy().hasHeightForWidth())
        self.fs_tree.setSizePolicy(sizePolicy)
        self.fs_tree.setTabKeyNavigation(True)
        self.fs_tree.setProperty("showDropIndicator", True)
        self.fs_tree.setDragEnabled(False)
        self.fs_tree.setDragDropMode(QAbstractItemView.DropOnly)
        self.fs_tree.setDefaultDropAction(Qt.MoveAction)
        self.fs_tree.setRootIsDecorated(False)
        self.hsplit.addWidget(self.fs_tree)
        self.fs_tree.header().setVisible(False)
        self.bookmark_view = QListView(self.hsplit)
        self.bookmark_view.setObjectName(u"bookmark_view")
        self.bookmark_view.setContextMenuPolicy(Qt.ActionsContextMenu)
        self.bookmark_view.setEditTriggers(QAbstractItemView.DoubleClicked
                                           | QAbstractItemView.EditKeyPressed
                                           | QAbstractItemView.SelectedClicked)
        self.bookmark_view.setTabKeyNavigation(True)
        self.bookmark_view.setDragDropOverwriteMode(True)
        self.bookmark_view.setDragDropMode(QAbstractItemView.DropOnly)
        self.bookmark_view.setDefaultDropAction(Qt.CopyAction)
        self.bookmark_view.setAlternatingRowColors(True)
        self.hsplit.addWidget(self.bookmark_view)
        self.mounts_view = QListView(self.hsplit)
        self.mounts_view.setObjectName(u"mounts_view")
        self.mounts_view.setTabKeyNavigation(True)
        self.mounts_view.setAlternatingRowColors(True)
        self.hsplit.addWidget(self.mounts_view)
        self.vsplit.addWidget(self.hsplit)
        self.table_view = QTableView(self.vsplit)
        self.table_view.setObjectName(u"table_view")
        sizePolicy1 = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        sizePolicy1.setHorizontalStretch(1)
        sizePolicy1.setVerticalStretch(0)
        sizePolicy1.setHeightForWidth(
            self.table_view.sizePolicy().hasHeightForWidth())
        self.table_view.setSizePolicy(sizePolicy1)
        self.table_view.viewport().setProperty("cursor",
                                               QCursor(Qt.ArrowCursor))
        self.table_view.setContextMenuPolicy(Qt.ActionsContextMenu)
        self.table_view.setAcceptDrops(True)
        self.table_view.setToolTipDuration(-3)
        self.table_view.setEditTriggers(QAbstractItemView.AnyKeyPressed
                                        | QAbstractItemView.EditKeyPressed
                                        | QAbstractItemView.SelectedClicked)
        self.table_view.setDragEnabled(True)
        self.table_view.setDragDropMode(QAbstractItemView.DragDrop)
        self.table_view.setDefaultDropAction(Qt.MoveAction)
        self.table_view.setAlternatingRowColors(True)
        self.table_view.setSelectionMode(QAbstractItemView.ExtendedSelection)
        self.table_view.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.table_view.setTextElideMode(Qt.ElideMiddle)
        self.table_view.setShowGrid(False)
        self.table_view.setSortingEnabled(True)
        self.table_view.setWordWrap(False)
        self.vsplit.addWidget(self.table_view)
        self.table_view.horizontalHeader().setDefaultSectionSize(150)
        self.table_view.horizontalHeader().setHighlightSections(False)
        self.table_view.horizontalHeader().setStretchLastSection(True)
        self.table_view.verticalHeader().setVisible(False)

        self.horizontalLayout.addWidget(self.vsplit)

        tfm.setCentralWidget(self.centralwidget)
        self.statusbar = QStatusBar(tfm)
        self.statusbar.setObjectName(u"statusbar")
        self.statusbar.setContextMenuPolicy(Qt.NoContextMenu)
        self.statusbar.setSizeGripEnabled(False)
        tfm.setStatusBar(self.statusbar)
        self.toolbar = QToolBar(tfm)
        self.toolbar.setObjectName(u"toolbar")
        self.toolbar.setContextMenuPolicy(Qt.PreventContextMenu)
        self.toolbar.setMovable(False)
        tfm.addToolBar(Qt.TopToolBarArea, self.toolbar)

        self.toolbar.addAction(self.action_back)
        self.toolbar.addAction(self.action_forward)
        self.toolbar.addAction(self.action_up)
        self.toolbar.addAction(self.action_home)
        self.toolbar.addAction(self.action_go)

        self.retranslateUi(tfm)

        QMetaObject.connectSlotsByName(tfm)
Example #10
0
    def setupUi(self, Dialog):
        if not Dialog.objectName():
            Dialog.setObjectName(u"Dialog")
        Dialog.resize(524, 356)
        self.verticalLayout = QVBoxLayout(Dialog)
        self.verticalLayout.setObjectName(u"verticalLayout")
        self.widget = QWidget(Dialog)
        self.widget.setObjectName(u"widget")
        sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(
            self.widget.sizePolicy().hasHeightForWidth())
        self.widget.setSizePolicy(sizePolicy)
        self.widget.setMinimumSize(QSize(0, 25))
        self.horizontalLayout = QHBoxLayout(self.widget)
        self.horizontalLayout.setObjectName(u"horizontalLayout")
        self.tbCreate = QToolButton(self.widget)
        self.tbCreate.setObjectName(u"tbCreate")
        sizePolicy1 = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Maximum)
        sizePolicy1.setHorizontalStretch(0)
        sizePolicy1.setVerticalStretch(0)
        sizePolicy1.setHeightForWidth(
            self.tbCreate.sizePolicy().hasHeightForWidth())
        self.tbCreate.setSizePolicy(sizePolicy1)
        self.tbCreate.setMinimumSize(QSize(50, 25))

        self.horizontalLayout.addWidget(self.tbCreate)

        self.tbAdd = QToolButton(self.widget)
        self.tbAdd.setObjectName(u"tbAdd")
        self.tbAdd.setMinimumSize(QSize(50, 25))

        self.horizontalLayout.addWidget(self.tbAdd)

        self.tbRemove = QToolButton(self.widget)
        self.tbRemove.setObjectName(u"tbRemove")
        self.tbRemove.setMinimumSize(QSize(50, 25))

        self.horizontalLayout.addWidget(self.tbRemove)

        self.verticalLayout.addWidget(self.widget, 0, Qt.AlignLeft)

        self.keyListView = QListView(Dialog)
        self.keyListView.setObjectName(u"keyListView")

        self.verticalLayout.addWidget(self.keyListView)

        self.buttonBox = QDialogButtonBox(Dialog)
        self.buttonBox.setObjectName(u"buttonBox")
        self.buttonBox.setOrientation(Qt.Horizontal)
        self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel
                                          | QDialogButtonBox.Ok)

        self.verticalLayout.addWidget(self.buttonBox)

        self.retranslateUi(Dialog)
        self.buttonBox.accepted.connect(Dialog.accept)
        self.buttonBox.rejected.connect(Dialog.reject)

        QMetaObject.connectSlotsByName(Dialog)
Example #11
0
    def __init__(self, stages: list[Stage], *args, **kwargs):
        QMainWindow.__init__(self, *args, **kwargs)
        if not self.objectName():
            self.setObjectName("MainWindow")
        self.resize(800, 600)
        size_policy = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        size_policy.setHorizontalStretch(0)
        size_policy.setVerticalStretch(0)
        size_policy.setHeightForWidth(self.sizePolicy().hasHeightForWidth())
        self.setSizePolicy(size_policy)
        self.setWindowTitle("Interfan")

        self.central_widget = QWidget(self)
        self.central_widget.setObjectName("central_widget")

        self.main_horizontal_layout = QHBoxLayout(self.central_widget)
        self.main_horizontal_layout.setObjectName("main_horizontal_layout")

        self.left_grid_layout = QGridLayout()
        self.left_grid_layout.setObjectName("left_grid_layout")

        self.slice_slider = QSlider(self.central_widget)
        self.slice_slider.setObjectName("slice_slider")
        self.slice_slider.setEnabled(False)
        self.slice_slider.setOrientation(Qt.Horizontal)

        self.left_grid_layout.addWidget(self.slice_slider, 1, 0, 1, 1)

        self.slice_label = QLabel(self.central_widget)
        self.slice_label.setObjectName("slice_label")
        self.slice_label.setGeometry(QRect(0, 10, 58, 18))
        self.slice_label.setText("0")
        self.left_grid_layout.addWidget(self.slice_label, 1, 1, 1, 1)

        self.main_image_scene = QGraphicsScene()
        self.main_image_view = QGraphicsView(self.main_image_scene)
        self.main_image_view.setObjectName("main_image_view")
        self.left_grid_layout.addWidget(self.main_image_view, 0, 0, 1, 1)

        self.slice_image_scene = QGraphicsScene()
        self.slice_image_view = QGraphicsView(self.slice_image_scene)
        self.slice_image_view.setObjectName("slice_image_view")
        self.left_grid_layout.addWidget(self.slice_image_view, 0, 1, 1, 1)

        self.left_grid_layout.setRowStretch(0, 12)
        self.left_grid_layout.setRowStretch(1, 1)
        self.left_grid_layout.setColumnStretch(0, 6)
        self.left_grid_layout.setColumnStretch(1, 1)

        self.main_horizontal_layout.addLayout(self.left_grid_layout)

        self.right_vertical_layout = QVBoxLayout()
        self.right_vertical_layout.setObjectName("right_vertical_layout")

        self.settings_widget = SettingWidget(self.central_widget)
        self.right_vertical_layout.addWidget(self.settings_widget.group_box)

        self.proceed_button = QPushButton(self.central_widget)
        self.proceed_button.setObjectName("proceed_button")
        self.proceed_button.setText("Выполнить")
        self.proceed_button.setEnabled(False)
        self.right_vertical_layout.addWidget(self.proceed_button)

        self.right_down_layout = QHBoxLayout()
        self.right_down_layout.setObjectName("right_down_layout")
        self.stages_layout = QVBoxLayout()
        self.stages_layout.setObjectName("stages_layout")

        self.stages_label = QLabel(self.central_widget)
        self.stages_label.setObjectName("stages_label")
        self.stages_label.setText("Этапы")
        self.stages_layout.addWidget(self.stages_label, 1)

        self.stages_radio_buttons = []
        for s in stages:
            rb = QRadioButton(self.central_widget)
            rb.setObjectName("stages_radio_button")
            rb.setEnabled(False)
            rb.setText(s.name)
            self.stages_layout.addWidget(rb, 1)
            self.stages_radio_buttons.append(rb)

        self.stages_radio_buttons[0].setEnabled(True)
        self.stages_radio_buttons[0].setChecked(True)
        self.set_stage(stages[0])

        self.right_down_layout.addLayout(self.stages_layout)

        self.history_layout = QVBoxLayout()
        self.history_layout.setObjectName("history_layout")
        self.history_header_layout = QHBoxLayout()
        self.history_header_layout.setObjectName("history_header_layout")

        self.history_label = QLabel(self.central_widget)
        self.history_label.setObjectName("history_label")
        self.history_label.setText("История операций")
        self.history_header_layout.addWidget(self.history_label)

        self.history_minus_button = QToolButton(self.central_widget)
        self.history_minus_button.setObjectName("history_minus_button")
        self.history_minus_button.setEnabled(False)
        self.history_minus_button.setText("-")
        self.history_header_layout.addWidget(self.history_minus_button)

        self.history_header_layout.setStretch(0, 8)
        self.history_header_layout.setStretch(1, 1)

        self.history_layout.addLayout(self.history_header_layout)

        self.history_list_view = QListView(self.central_widget)
        self.history_list_view.setObjectName("history_list_view")
        self.history_layout.addWidget(self.history_list_view)

        self.history_script_button = QPushButton(self.central_widget)
        self.history_script_button.setObjectName("history_script_button")
        self.history_script_button.setText("Просмотреть как скрипт")
        self.history_layout.addWidget(self.history_script_button)

        self.right_down_layout.addLayout(self.history_layout)

        self.right_down_layout.setStretch(0, 1)
        self.right_down_layout.setStretch(1, 1)

        self.right_vertical_layout.addLayout(self.right_down_layout)
        self.right_vertical_layout.setStretch(0, 1)
        self.right_vertical_layout.setStretch(1, 0)
        self.right_vertical_layout.setStretch(2, 1)

        self.main_horizontal_layout.addLayout(self.right_vertical_layout)
        self.main_horizontal_layout.setStretch(0, 1)
        self.main_horizontal_layout.setStretch(1, 1)

        self.setCentralWidget(self.central_widget)
        self.menu_bar = QMenuBar(self)
        self.menu_bar.setObjectName("menu_bar")
        self.menu_bar.setGeometry(QRect(0, 0, 800, 30))
        self.menu = QMenu(self.menu_bar)
        self.menu.setObjectName("men")
        self.menu.setTitle("Меню")
        self.setMenuBar(self.menu_bar)

        self.open_file = QAction(self)
        self.open_file.setObjectName("open_file")
        self.open_file.setText("Открыть файл")
        self.menu.addAction(self.open_file)

        self.status_bar = QStatusBar(self)
        self.status_bar.setObjectName("status_bar")
        self.setStatusBar(self.status_bar)

        self.menu_bar.addAction(self.menu.menuAction())

        QMetaObject.connectSlotsByName(self)
Example #12
0
class Window(QDialog):
    def __init__(self, parent=None):
        super(Window, self).__init__(parent)
        self.createTrayIcon()
        self.createProgramsList()
        self.createCodeEditPage()
        self.logsPage = QTextBrowser()
        self.documentation = QTextBrowser()

        self.tabWidget = QTabWidget()
        self.tabWidget.setIconSize(QSize(64, 64))
        self.tabWidget.addTab(self.programsListPage, QIcon(":/images/Adventure-Map-icon.png"), "Programs")
        self.tabWidget.addTab(self.codeEditPage, QIcon(":/images/Sword-icon.png"), "Edit Program")
        self.tabWidget.addTab(self.logsPage, QIcon(":/images/Spell-Scroll-icon.png"), "Logs")
        self.tabWidget.addTab(self.documentation, QIcon(":/images/Spell-Book-icon.png"), "Documentation")

        self.mainLayout = QVBoxLayout()
        self.mainLayout.addWidget(self.tabWidget)
        self.setLayout(self.mainLayout)

        self.setWindowTitle(APP_NAME)
        self.setWindowFlags(Qt.WindowStaysOnTopHint | Qt.Dialog)
        self.resize(800, 600)

        self.systrayHintMsgShowed = False
        self.firstShow = True
        self.fromQuit = False

    def createProgramsList(self):
        self.programsListModel = QStandardItemModel(0, 1, self)
        self.programsList = QListView()
        self.programsList.setModel(self.programsListModel)
        self.programsListPage = QWidget()
        self.programsListLayout = QVBoxLayout()
        self.programsListButtons = QHBoxLayout()
        self.programsListButtonNew = QPushButton("New")
        self.programsListButtonDelete = QPushButton("Delete")
        self.programsListButtonEdit = QPushButton("Edit")
        self.programsListButtonSet = QPushButton("Set")
        self.programsListButtons.addWidget(self.programsListButtonNew)
        self.programsListButtons.addWidget(self.programsListButtonSet)
        self.programsListButtons.addWidget(self.programsListButtonEdit)
        self.programsListButtons.addWidget(self.programsListButtonDelete)
        self.programsListLayout.addLayout(self.programsListButtons)
        self.programsListLayout.addWidget(self.programsList)
        self.programsListPage.setLayout(self.programsListLayout)
        self.programsListButtonNew.clicked.connect(self.newProgram)
        self.programsListButtonEdit.clicked.connect(self.editProgram)
        self.programsListButtonDelete.clicked.connect(self.deleteProgram)
        self.programsListButtonSet.clicked.connect(self.setProgram)

    def createCodeEditPage(self):
        self.codeEditPage = QWidget()
        self.codeEditLayout = QVBoxLayout()
        self.codeEditNameBox = QHBoxLayout()
        self.codeEditNameBoxNameLabel = QLabel("Name:")
        self.codeEditNameBoxNameInput = QLineEdit()
        self.codeEditNameBoxSaveButton = QPushButton("Save")
        self.codeEditNameBoxCancelButton = QPushButton("Cancel")
        self.codeEditNameBox.addWidget(self.codeEditNameBoxNameLabel)
        self.codeEditNameBox.addWidget(self.codeEditNameBoxNameInput)
        self.codeEditNameBox.addWidget(self.codeEditNameBoxSaveButton)
        self.codeEditNameBox.addWidget(self.codeEditNameBoxCancelButton)
        self.codeEdit = QTextEdit()
        self.codeEditLayout.addLayout(self.codeEditNameBox)
        self.codeEditLayout.addWidget(self.codeEdit)
        self.codeEditPage.setLayout(self.codeEditLayout)
        self.codeEditLastCode = ''
        self.codeEditNameBoxSaveButton.clicked.connect(self.saveEditProgram)
        self.codeEditNameBoxCancelButton.clicked.connect(self.cancelEditProgram)

    def showEvent(self, event):
        super().showEvent(event)
        if self.firstShow:
            self.firstShow = False
            self.createWaitDialog()
            self.findKeyboard()

    def closeWaitDialog(self):
        time.sleep(1)
        self.waitDialog.close()

    @Slot()
    def newProgram(self):
        self.showNormal()
        self.tabWidget.setCurrentWidget(self.codeEditPage)
        self.codeEdit.setPlainText("")
        self.codeEditLastCode = ''
        self.codeEditNameBoxNameInput.setText("")
    
    @Slot()
    def editProgram(self):
        selected = self.programsList.selectedIndexes()
        if not selected:
            return
        selected = selected[0].data()
        loadProgram = LoadProgram(self.cmdSocket, selected)
        loadProgram.loaded.connect(self.programLoaded)
        loadProgram.start()
        self.showWaitDialog("Loading program ...")

    @Slot()
    def saveEditProgram(self):
        name = self.codeEditNameBoxNameInput.text()
        program = self.codeEdit.toPlainText()
        if not name:
            return
        editProgramWorker = EditProgram(self.cmdSocket, name, program)
        editProgramWorker.edited.connect(self.programSaved)
        editProgramWorker.start()
        self.showWaitDialog("Saving program ...")

    @Slot()
    def cancelEditProgram(self):
        self.codeEdit.setPlainText(self.codeEditLastCode)

    @Slot()
    def deleteProgram(self):
        selected = self.programsList.selectedIndexes()
        if not selected:
            return
        selected = selected[0].data()
        deleteWorker = DeleteProgram(self.cmdSocket, selected)
        deleteWorker.deleted.connect(self.programDeleted)
        deleteWorker.start()
        self.showWaitDialog("Deleting program ...")

    @Slot()
    def setProgram(self):
        selected = self.programsList.selectedIndexes()
        if not selected:
            return
        selected = selected[0].data()
        setWorker = SetProgram(self.cmdSocket, selected)
        setWorker.setDone.connect(self.programSet)
        setWorker.start()
        self.showWaitDialog("Setting program ...")

    @Slot(str, str)
    def programLoaded(self, name, program):
        self.closeWaitDialog()
        self.tabWidget.setCurrentWidget(self.codeEditPage)
        self.codeEdit.setPlainText(program)
        self.codeEditLastCode = program
        self.codeEditNameBoxNameInput.setText(name)

    @Slot(str)
    def programDeleted(self, name):
        self.updateProgramsList()

    @Slot(str)
    def programSet(self, name):
        self.updateProgramsList()

    @Slot(str)
    def programSaved(self, name):
        self.updateProgramsList()
        self.tabWidget.setCurrentWidget(self.programsListPage)

    def createWaitDialog(self):
        self.waitDialog = QDialog(self)
        self.waitDialogLayout = QHBoxLayout()
        self.waitDialogLabel = QLabel()
        self.waitDialogLayout.addWidget(self.waitDialogLabel)
        self.waitDialog.setLayout(self.waitDialogLayout)
        self.waitDialog.setWindowFlags(Qt.Dialog | Qt.FramelessWindowHint)

    def showWaitDialog(self, text='Please wait...'):
        self.waitDialogLabel.setText(text)
        self.waitDialog.exec_()

    def findKeyboard(self):
        find = ConnectKeyboard()
        find.connected.connect(self.keyboardConnected)
        find.start()
        self.showWaitDialog('Finding and connecting Fruit2Pi Keyboard ...')

    @Slot(BluetoothSocket)
    def keyboardConnected(self, socket):
        self.cmdSocket = socket
        self.updateProgramsList()
    
    def updateProgramsList(self):
        self.listProgram = ListProgram(self.cmdSocket)
        self.listProgram.listed.connect(self.programListUpdated)
        self.listProgram.start()
    
    @Slot(list, str)
    def programListUpdated(self, programs, current_program):
        print(programs)
        print(current_program)
        self.closeWaitDialog()
        self.programsListModel.clear()
        for p in programs:
            item = QStandardItem(p)
            if p == current_program:
                item.setForeground(QBrush(QColor(0, 0, 255, 127)))
            self.programsListModel.appendRow(item)

    def setVisible(self, visible):
        super().setVisible(visible)

    def closeEvent(self, event):
        if self.fromQuit:
            return
        if not event.spontaneous() or not self.isVisible():
            return
        if not self.systrayHintMsgShowed:
            self.systrayHintMsgShowed = True
            icon = QIcon(":/images/yammi-banana-icon.png")
            self.trayIcon.showMessage(APP_NAME,
                                      "Running on background"
                                      "To quit, choose <b>Quit</b> in the icon menu",
                                      icon,
                                      5000
                                      )
        self.hide()
        event.ignore()

    @Slot(str)
    def iconActivated(self, reason):
        print(reason)
        if reason == QSystemTrayIcon.Trigger:
            self.showNormal()
        if reason == QSystemTrayIcon.DoubleClick:
            self.showNormal()

    @Slot()
    def showProgramsPage(self):
        self.showNormal()
        self.tabWidget.setCurrentWidget(self.programsListPage)

    @Slot()
    def showLogsPage(self):
        self.showNormal()
        self.tabWidget.setCurrentWidget(self.logsPage)
    
    @Slot()
    def showDocumentation(self):
        self.showNormal()
        self.tabWidget.setCurrentWidget(self.documentation)

    @Slot()
    def quit(self):
        self.fromQuit = True
        qApp.quit()

    def createTrayIcon(self):
        self.showProgramsAction = QAction("Programs", self)
        self.showProgramsAction.triggered.connect(self.showProgramsPage)
        self.showNewProgramAction = QAction("New Program", self)
        self.showNewProgramAction.triggered.connect(self.newProgram)
        self.showSetProgramAction = QAction("Logs", self)
        self.showSetProgramAction.triggered.connect(self.showLogsPage)
        self.showDocumentationAction = QAction("Documentation", self)
        self.showDocumentationAction.triggered.connect(self.showDocumentation)
        self.quitAction = QAction("Quit", self)
        self.quitAction.triggered.connect(self.quit)

        self.trayIconMenu = QMenu(self)
        self.trayIconMenu.addAction(self.showProgramsAction)
        self.trayIconMenu.addAction(self.showSetProgramAction)
        self.trayIconMenu.addAction(self.showNewProgramAction)
        self.trayIconMenu.addAction(self.showDocumentationAction)
        self.trayIconMenu.addSeparator()
        self.trayIconMenu.addAction(self.quitAction)
        self.trayIcon = QSystemTrayIcon(self)

        self.trayIcon.setContextMenu(self.trayIconMenu)
        self.trayIcon.activated.connect(self.iconActivated)
        self.trayIcon.setIcon(QIcon(":/images/yammi-banana-icon.png"))
        self.trayIcon.show()
Example #13
0
    def addOutputFolder(self, name: str):
        listview = QListView(self)
        listview.setObjectName("tab_" + name.lower())

        listview.setSelectionMode(QListView.ExtendedSelection)
        listview.setAcceptDrops(True)
        listview.setMouseTracking(True)
        listview.setModel(
            TargetListModel(listview, self._network, self._shared_states))
        listview.pressed.connect(self.updateSelectedOutput)
        listview.entered.connect(self.updateHighlightOutput)
        listview.enterEvent = self.listOutputViewEnter
        listview.leaveEvent = self.listOutputViewLeave
        listview.setContextMenuPolicy(Qt.CustomContextMenu)
        listview.customContextMenuRequested.connect(
            lambda pos: self.outputContextMenu(listview, pos))

        self._meta.folders[name] = FolderMeta()
        self.tab_folders.addTab(listview, name.upper())

        self.updateFolderNames()