示例#1
0
 def readSettings(self):
     s = QSettings()
     s.beginGroup("pitch-menu")
     self.actionCollection.pitch_relative_assume_first_pitch_absolute.setChecked(
         s.value("relative-first-pitch-absolute", False, bool))
     self.actionCollection.pitch_relative_write_startpitch.setChecked(
         s.value("relative-write-startpitch", True, bool))
示例#2
0
 def writeSettings(self):
     s = QSettings()
     s.beginGroup("pitch-menu")
     s.setValue("relative-first-pitch-absolute",
         self.actionCollection.pitch_relative_assume_first_pitch_absolute.isChecked())
     s.setValue("relative-write-startpitch",
         self.actionCollection.pitch_relative_write_startpitch.isChecked())
示例#3
0
 def loadSettings(self):
     s = QSettings()
     s.beginGroup("helper_applications")
     self.printCommand.setPath(s.value("printcommand", "", str))
     self.printDialogCheck.setChecked(s.value("printcommand/dialog", False, bool))
     with qutil.signalsBlocked(self.resolution):
         self.resolution.setEditText(format(s.value("printcommand/dpi", 300, int)))
示例#4
0
 def loadSettings(self):
     output_port = midihub.default_output()
     input_port = midihub.default_input()
     s = QSettings()
     s.beginGroup("midi")
     self._playerPort.setEditText(s.value("player/output_port", output_port, str))
     self._inputPort.setEditText(s.value("midi/input_port", input_port, str))
示例#5
0
文件: preferences.py 项目: jni/cecog
    def restoreSettings(self):

        settings = QSettings(version.organisation, version.appname)
        settings.beginGroup('preferences')

        if settings.contains('host'):
            self.host = settings.value('host', type=str)

        if settings.contains('port'):
            self.port = settings.value('port', type=int)

        if settings.contains('path_mapping'):
            self.mapping_str = settings.value('path_mapping')

        if settings.contains('target_platform'):
            self.target_platform = settings.value('target_platform')

        if settings.contains('batch_size'):
            self.batch_size = settings.value('batch_size', type=int)

        if settings.contains('cluster_support'):
            self.cluster_support = settings.value('cluster_support', type=bool)

        if settings.contains('stylesheet'):
            self.stylesheet = settings.value('stylesheet', type=str)

        settings.endGroup()
示例#6
0
 def saveSettings(self):
     s = QSettings()
     s.beginGroup("documentation")
     langs = ['default', 'C'] + lilydoc.translations
     s.setValue("language", langs[self.languages.currentIndex()])
     s.setValue("fontfamily", self.fontChooser.currentFont().family())
     s.setValue("fontsize", self.fontSize.value())
示例#7
0
 def saveSettings(self):
     s = QSettings()
     s.beginGroup('scorewiz/instrumentnames')
     s.setValue('enable', self.isChecked())
     s.setValue('first', ('long', 'short')[self.firstSystem.currentIndex()])
     s.setValue('other', ('long', 'short', 'none')[self.otherSystems.currentIndex()])
     s.setValue('language', self._langs[self.language.currentIndex()])
示例#8
0
 def load_settings(self):
     """Load application-wide settings relating to extensions."""
     s = QSettings()
     s.beginGroup('extension-settings')
     self._active = s.value('active', True, bool)
     self._inactive_extensions = s.value('installed/inactive', [], str)
     self._root_directory = s.value('root-directory', '', str)
示例#9
0
    def exportColoredHtml(self):
        doc = self.currentDocument()
        name, ext = os.path.splitext(os.path.basename(doc.url().path()))
        if name:
            if ext.lower() == ".html":
                name += "_html"
            name += ".html"
        dir = os.path.dirname(doc.url().toLocalFile())
        if dir:
            name = os.path.join(dir, name)
        filename = QFileDialog.getSaveFileName(self, app.caption(_("Export as HTML")),
            name, "{0} (*.html)".format("HTML Files"))[0]
        if not filename:
            return #cancelled

        s = QSettings()
        s.beginGroup("source_export")
        number_lines = s.value("number_lines", False, bool)
        inline_style = s.value("inline_export", False, bool)
        wrap_tag = s.value("wrap_tag", "pre", str)
        wrap_attrib = s.value("wrap_attrib", "id", str)
        wrap_attrib_name = s.value("wrap_attrib_name", "document", str)
        import highlight2html
        html = highlight2html.html_document(doc, inline=inline_style, number_lines=number_lines,
            wrap_tag=wrap_tag, wrap_attrib=wrap_attrib, wrap_attrib_name=wrap_attrib_name)
        try:
            with open(filename, "wb") as f:
                f.write(html.encode('utf-8'))
        except IOError as e:
            msg = _("{message}\n\n{strerror} ({errno})").format(
                message = _("Could not write to: {url}").format(url=filename),
                strerror = e.strerror,
                errno = e.errno)
            QMessageBox.critical(self, app.caption(_("Error")), msg)
示例#10
0
 def _run_git_command(self, cmd, args = []):
     """
     run a git command and return its output
     as a string list.
     Raise an exception if it returns an error.
     - cmd is the git command (without 'git')
     - args is a string or a list of strings
     """
     from PyQt5.QtCore import QSettings
     s = QSettings()
     s.beginGroup("helper_applications")
     git_cmd = s.value("git", "git", str)
     git_cmd = git_cmd if git_cmd else "git"
     cmd = [git_cmd, cmd]
     cmd.extend(args)
     pr = subprocess.Popen(cmd, cwd = self.rootDir,
                           stdout = subprocess.PIPE,
                           stderr = subprocess.PIPE,
                           universal_newlines = True)
     (out, error) = pr.communicate()
     if error:
         raise GitError(error)
     result = out.split('\n')
     if result[-1] == '':
         result.pop()
     return result
示例#11
0
 def saveSettings(self):
     s = QSettings()
     s.beginGroup("documentstructure")
     if self.patternList.value() != documentstructure.default_outline_patterns:
         s.setValue("outline_patterns", self.patternList.value())
     else:
         s.remove("outline_patterns")
示例#12
0
def preferred():
    """Return the quotes desired by the Frescobaldi user.
    
    Always returns a quote set.
    Only this function depends on Qt and Frescobaldi.
    
    """
    
    from PyQt5.QtCore import QSettings
    import po.setup

    s = QSettings()
    s.beginGroup("typographical_quotes")
    language = s.value("language", "current", str)
    
    default = _quotes["C"]
    if language == "current":
        language = po.setup.current()
    elif language == "custom":
        return QuoteSet(
            primary = Quotes(
                left = s.value("primary_left", default.primary.left, str),
                right = s.value("primary_right", default.primary.right, str),
            ),
            secondary = Quotes(
                left = s.value("secondary_left", default.secondary.left, str),
                right = s.value("secondary_right", default.secondary.right, str),
            )
        )
    return quotes(language) or default
示例#13
0
    def sessionOpened(self):
        if self.networkSession is not None:
            config = self.networkSession.configuration()

            if config.type() == QNetworkConfiguration.UserChoice:
                id = self.networkSession.sessionProperty('UserChoiceConfiguration')
            else:
                id = config.identifier()

            settings = QSettings(QSettings.UserScope, 'QtProject')
            settings.beginGroup('QtNetwork')
            settings.setValue('DefaultNetworkConfiguration', id)
            settings.endGroup();

        self.tcpServer = QTcpServer(self)
        if not self.tcpServer.listen():
            QMessageBox.critical(self, "Fortune Server",
                    "Unable to start the server: %s." % self.tcpServer.errorString())
            self.close()
            return

        for ipAddress in QNetworkInterface.allAddresses():
            if ipAddress != QHostAddress.LocalHost and ipAddress.toIPv4Address() != 0:
                break
        else:
            ipAddress = QHostAddress(QHostAddress.LocalHost)

        ipAddress = ipAddress.toString()

        self.statusLabel.setText("The server is running on\n\nIP: %s\nport %d\n\n"
                "Run the Fortune Client example now." % (ipAddress, self.tcpServer.serverPort()))
示例#14
0
 def run_command(cls, cmd, args=[], dir=None):
     """
     run a git command and return its output
     as a string list.
     Raise an exception if it returns an error.
     - cmd is the git command (without 'git')
     - args is a string or a list of strings
     If no dir is passed the running dir of
     Frescobaldi is used as default
     """
     dir = os.path.normpath(os.path.join(sys.path[0], '..')) if dir is None else dir
     from PyQt5.QtCore import QSettings
     s = QSettings()
     s.beginGroup("helper_applications")
     git_cmd = s.value("git", "git", str)
     git_cmd = git_cmd if git_cmd else "git"
     cmd = [git_cmd, cmd]
     cmd.extend(args)
     pr = subprocess.Popen(cmd, cwd=dir,
                           stdout=subprocess.PIPE,
                           stderr=subprocess.PIPE,
                           universal_newlines=True)
     (out, error) = pr.communicate()
     if error:
         raise GitError(error)
     result = out.split('\n')
     if result[-1] == '':
         result.pop()
     return result
 def closeEvent(self, event):
     print("Main window close event")
     # Save layout settings
     settings = QSettings("PhotoCalendar")
     settings.beginGroup("MainWindow")
     curSize = self.size()
     settings.setValue("Size", QVariant(curSize))
     curPos = self.pos()
     settings.setValue("Position", QVariant(curPos))
     #settings.setValue("MainWindow/State", QVariant(self.saveState()))
     horzSplitterState = self.horzSplitter.saveState()
     #print("HorzSplitter save", horzSplitterState)
     settings.setValue("HorzSplitter", QVariant(horzSplitterState))
     leftPaneSplitterState = self.leftPane.saveState()
     settings.setValue("LeftPaneSplitter", QVariant(leftPaneSplitterState))
     #print("LeftPaneSplitter save", leftPaneSplitterState)
     rightPaneSplitterState = self.rightPane.saveState()
     settings.setValue("RightPaneSplitter", QVariant(rightPaneSplitterState))
     #print("RightPaneSplitter save", leftPaneSplitterState)
     settings.endGroup()
     # Stop the sub-elements
     self.calendar.stop()
     self.clock.stop()
     self.photos.stop()
     # Accept the close event
     event.accept()
示例#16
0
 def requireScripts(self, urlList):
     """
     Public method to get the sources of all required scripts.
     
     @param urlList list of URLs (list of string)
     @return sources of all required scripts (string)
     """
     requiresDir = QDir(self.requireScriptsDirectory())
     if not requiresDir.exists() or len(urlList) == 0:
         return ""
     
     script = ""
     
     settings = QSettings(
         os.path.join(self.requireScriptsDirectory(), "requires.ini"),
         QSettings.IniFormat)
     settings.beginGroup("Files")
     for url in urlList:
         if settings.contains(url):
             fileName = settings.value(url)
             try:
                 f = open(fileName, "r", encoding="utf-8")
                 source = f.read()
                 f.close()
             except (IOError, OSError):
                 source = ""
             script += source.strip() + "\n"
     
     return script
示例#17
0
 def saveSettings(self):
     s = QSettings()
     s.beginGroup("typographical_quotes")
     s.setValue("language", self._langs[self.languageCombo.currentIndex()])
     s.setValue("primary_left", self.primaryLeft.text())
     s.setValue("primary_right", self.primaryRight.text())
     s.setValue("secondary_left", self.secondaryLeft.text())
     s.setValue("secondary_right", self.secondaryRight.text())
示例#18
0
 def saveSettings(self):
     s = QSettings()
     s.beginGroup("log")
     s.setValue("fontfamily", self.fontChooser.currentFont().family())
     s.setValue("fontsize", self.fontSize.value())
     s.setValue("show_on_start", self.showlog.isChecked())
     s.setValue("rawview", self.rawview.isChecked())
     s.setValue("hide_auto_engrave", self.hideauto.isChecked())
示例#19
0
 def loadSettings(self):
     s = QSettings()
     s.beginGroup("documentstructure")
     try:
         patterns = s.value("outline_patterns", documentstructure.default_outline_patterns, str)
     except TypeError:
         patterns = []
     self.patternList.setValue(patterns)
示例#20
0
 def loadSettings(self):
     """Loads the settings from config."""
     s = QSettings()
     s.beginGroup("sidebar")
     line_numbers = s.value("line_numbers", self._line_numbers, bool)
     self.setLineNumbersVisible(line_numbers)
     folding = s.value("folding", self._folding, bool)
     self.setFoldingVisible(folding)
示例#21
0
 def writeSettings(self):
     s = QSettings()
     s.beginGroup("copy_image")
     s.setValue("dpi", self.dpiCombo.currentText())
     s.setValue("papercolor", self.colorButton.color())
     s.setValue("autocrop", self.crop.isChecked())
     s.setValue("antialias", self.antialias.isChecked())
     s.setValue("scaleup", self.scaleup.isChecked())
示例#22
0
 def saveSettings(self):
     s = QSettings()
     s.beginGroup("hyphenation")
     paths = self.listedit.value()
     if paths:
         s.setValue("paths", paths)
     else:
         s.remove("paths")
示例#23
0
def save_window_geometry(size, pos, is_full_screen, splitter_sizes):
    settings = QSettings()
    settings.beginGroup(group_main_window)
    settings.setValue(key_size, size)
    settings.setValue(key_pos, pos)
    settings.setValue(key_is_full_screen, is_full_screen)
    settings.setValue(key_splitter_sizes, splitter_sizes)
    settings.endGroup()
示例#24
0
 def readSettings(self):
     s = QSettings()
     s.beginGroup("copy_image")
     self.dpiCombo.setEditText(s.value("dpi", "100", str))
     self.colorButton.setColor(s.value("papercolor", QColor(Qt.white), QColor))
     self.crop.setChecked(s.value("autocrop", False, bool))
     self.antialias.setChecked(s.value("antialias", True, bool))
     self.scaleup.setChecked(s.value("scaleup", False, bool))
示例#25
0
 def saveSettings(self):
     s = QSettings()
     s.beginGroup("documentation")
     paths = self.paths.value()
     if paths:
         s.setValue("paths", paths)
     else:
         s.remove("paths")
示例#26
0
 def loadSettings(self):
     """ get users previous settings """
     s = QSettings()
     s.beginGroup('mode_shift')
     key = s.value('key', "", str)
     self.keyInput.setText(key)
     index = s.value('mode', 0, int)
     self.modeCombo.setCurrentIndex(index)
     self.readKeyInput()
示例#27
0
    def slot_saveSettings(self):
        settings = QSettings()
        settings.beginGroup("ConnectDialog")

        settings.setValue("index",     self.ui.comboBox.currentIndex())
        settings.setValue("bt-number", self.ui.sb_devnumber_bt.value())
        settings.setValue("lan-ip",    self.ui.le_ip_lan.text())
        settings.setValue("lan-port",  self.ui.sb_port_lan.value())
        settings.setValue("usb-ip",    self.ui.le_ip_usb.text())
示例#28
0
 def save_state(self):
     assert self.ROLE
     settings = QSettings()
     settings.beginGroup(self.ROLE)
     settings.setValue('geometry', self.saveGeometry())
     if isinstance(self, QMainWindow):
         settings.setValue('state', self.saveState())
     self._save_state(settings)
     settings.endGroup()
示例#29
0
    def loadSettings(self):
        settings = QSettings()
        settings.beginGroup("ConnectDialog")

        self.ui.comboBox.setCurrentIndex(settings.value("index", 0, type=int))
        self.ui.sb_devnumber_bt.setValue(settings.value("bt-number", 1, type=int))
        self.ui.le_ip_lan.setText(settings.value("lan-ip", "127.0.0.1", type=str))
        self.ui.sb_port_lan.setValue(settings.value("lan-port", 8888, type=int))
        self.ui.le_ip_usb.setText(settings.value("usb-ip", "192.168.51.1", type=str))
示例#30
0
 def loadSettings(self):
     s = QSettings()
     s.beginGroup("global_font_dialog")
     roman = s.value("roman", "Century Schoolbook L", str)
     self.romanCombo.setCurrentFont(QFont(roman))
     sans = s.value("sans", "sans-serif", str)
     self.sansCombo.setCurrentFont(QFont(sans))
     typewriter = s.value("typewriter", "monospace", str)
     self.typewriterCombo.setCurrentFont(QFont(typewriter))
示例#31
0
class App(QMainWindow):
    def __init__(self):
        super().__init__(flags=Qt.WindowFlags())
        self.settings = QSettings("SavSoft", "Fast Sweep Viewer")

        # prevent config from being re-written while loading
        self._loading = True

        self.central_widget = QWidget(self, flags=Qt.WindowFlags())
        self.grid_layout = QGridLayout(self.central_widget)
        self.grid_layout.setColumnStretch(0, 1)

        # Frequency box
        self.group_frequency = QGroupBox(self.central_widget)
        self.grid_layout_frequency = QGridLayout(self.group_frequency)

        self.label_frequency_min = QLabel(self.group_frequency)
        self.label_frequency_max = QLabel(self.group_frequency)
        self.label_frequency_center = QLabel(self.group_frequency)
        self.label_frequency_span = QLabel(self.group_frequency)

        self.spin_frequency_min = QDoubleSpinBox(self.group_frequency)
        self.spin_frequency_min.setMinimum(MIN_FREQUENCY)
        self.spin_frequency_min.setMaximum(MAX_FREQUENCY)
        self.spin_frequency_max = QDoubleSpinBox(self.group_frequency)
        self.spin_frequency_max.setMinimum(MIN_FREQUENCY)
        self.spin_frequency_max.setMaximum(MAX_FREQUENCY)
        self.spin_frequency_center = QDoubleSpinBox(self.group_frequency)
        self.spin_frequency_center.setMinimum(MIN_FREQUENCY)
        self.spin_frequency_center.setMaximum(MAX_FREQUENCY)
        self.spin_frequency_span = QDoubleSpinBox(self.group_frequency)
        self.spin_frequency_span.setMinimum(0.01)
        self.spin_frequency_span.setMaximum(MAX_FREQUENCY - MIN_FREQUENCY)

        self.check_frequency_persists = QCheckBox(self.group_frequency)

        # Zoom X
        self.button_zoom_x_out_coarse = QPushButton(self.group_frequency)
        self.button_zoom_x_out_fine = QPushButton(self.group_frequency)
        self.button_zoom_x_in_fine = QPushButton(self.group_frequency)
        self.button_zoom_x_in_coarse = QPushButton(self.group_frequency)

        # Move X
        self.button_move_x_left_coarse = QPushButton(self.group_frequency)
        self.button_move_x_left_fine = QPushButton(self.group_frequency)
        self.button_move_x_right_fine = QPushButton(self.group_frequency)
        self.button_move_x_right_coarse = QPushButton(self.group_frequency)

        # Voltage box
        self.group_voltage = QGroupBox(self.central_widget)
        self.grid_layout_voltage = QGridLayout(self.group_voltage)

        self.label_voltage_min = QLabel(self.group_voltage)
        self.label_voltage_max = QLabel(self.group_voltage)

        self.spin_voltage_min = QDoubleSpinBox(self.group_voltage)
        self.spin_voltage_min.setMinimum(MIN_VOLTAGE)
        self.spin_voltage_min.setMaximum(MAX_VOLTAGE)
        self.spin_voltage_max = QDoubleSpinBox(self.group_voltage)
        self.spin_voltage_max.setMinimum(MIN_VOLTAGE)
        self.spin_voltage_max.setMaximum(MAX_VOLTAGE)

        self.check_voltage_persists = QCheckBox(self.group_voltage)

        # Zoom Y
        self.button_zoom_y_out_coarse = QPushButton(self.group_voltage)
        self.button_zoom_y_out_fine = QPushButton(self.group_voltage)
        self.button_zoom_y_in_fine = QPushButton(self.group_voltage)
        self.button_zoom_y_in_coarse = QPushButton(self.group_voltage)

        # Frequency Mark box
        self.group_mark = QGroupBox(self.central_widget)
        self.grid_layout_mark = QGridLayout(self.group_mark)
        self.grid_layout_mark.setColumnStretch(0, 1)
        self.grid_layout_mark.setColumnStretch(1, 1)

        self.label_mark_min = QLabel(self.group_mark)
        self.label_mark_max = QLabel(self.group_mark)

        self.spin_mark_min = QDoubleSpinBox(self.group_mark)
        self.spin_mark_min.setMinimum(MIN_FREQUENCY)
        self.spin_mark_min.setMaximum(MAX_FREQUENCY)
        self.spin_mark_max = QDoubleSpinBox(self.group_mark)
        self.spin_mark_max.setMinimum(MIN_FREQUENCY)
        self.spin_mark_max.setMaximum(MAX_FREQUENCY)
        self.spin_mark_min.setValue(MIN_FREQUENCY)
        self.spin_mark_max.setValue(MAX_FREQUENCY)
        self.button_mark_min_reset = QPushButton(self.group_mark)
        self.button_mark_max_reset = QPushButton(self.group_mark)
        for b in (self.button_mark_min_reset, self.button_mark_max_reset):
            b.setIcon(backend.load_icon('reset'))
        self.button_zoom_to_selection = QPushButton(self.group_mark)

        # Find Lines box
        self.group_find_lines = QGroupBox(self.central_widget)
        self.grid_layout_find_lines = QGridLayout(self.group_find_lines)
        self.label_threshold = QLabel(self.group_find_lines)
        self.spin_threshold = QDoubleSpinBox(self.group_find_lines)
        self.spin_threshold.setMinimum(1.0)
        self.spin_threshold.setMaximum(1000.0)
        self.button_find_lines = QPushButton(self.group_find_lines)
        self.button_clear_lines = QPushButton(self.group_find_lines)
        self.button_prev_line = QPushButton(self.group_find_lines)
        self.button_next_line = QPushButton(self.group_find_lines)

        # plot
        self.figure = Figure()
        self.canvas = FigureCanvas(self.figure)
        self.canvas.setFocusPolicy(Qt.ClickFocus)
        self.plot_toolbar = NavigationToolbar(
            self.canvas, self, parameters_icon=backend.load_icon('configure'))
        self.legend_figure = Figure(constrained_layout=True, frameon=False)
        self.legend_canvas = FigureCanvas(self.legend_figure)
        self.plot = backend.Plot(figure=self.figure,
                                 legend_figure=self.legend_figure,
                                 toolbar=self.plot_toolbar,
                                 settings=self.settings,
                                 on_xlim_changed=self.on_xlim_changed,
                                 on_ylim_changed=self.on_ylim_changed,
                                 on_data_loaded=self.load_data)

        self.setup_ui()

        # config
        self.load_config()

        # actions
        self.spin_frequency_min.valueChanged.connect(
            self.spin_frequency_min_changed)
        self.spin_frequency_max.valueChanged.connect(
            self.spin_frequency_max_changed)
        self.spin_frequency_center.valueChanged.connect(
            self.spin_frequency_center_changed)
        self.spin_frequency_span.valueChanged.connect(
            self.spin_frequency_span_changed)
        self.button_zoom_x_out_coarse.clicked.connect(
            lambda: self.button_zoom_x_clicked(1. / 0.5))
        self.button_zoom_x_out_fine.clicked.connect(
            lambda: self.button_zoom_x_clicked(1. / 0.9))
        self.button_zoom_x_in_fine.clicked.connect(
            lambda: self.button_zoom_x_clicked(0.9))
        self.button_zoom_x_in_coarse.clicked.connect(
            lambda: self.button_zoom_x_clicked(0.5))
        self.button_move_x_left_coarse.clicked.connect(
            lambda: self.button_move_x_clicked(-500.))
        self.button_move_x_left_fine.clicked.connect(
            lambda: self.button_move_x_clicked(-50.))
        self.button_move_x_right_fine.clicked.connect(
            lambda: self.button_move_x_clicked(50.))
        self.button_move_x_right_coarse.clicked.connect(
            lambda: self.button_move_x_clicked(500.))
        self.check_frequency_persists.toggled.connect(
            self.check_frequency_persists_toggled)

        self.spin_voltage_min.valueChanged.connect(
            self.spin_voltage_min_changed)
        self.spin_voltage_max.valueChanged.connect(
            self.spin_voltage_max_changed)
        self.button_zoom_y_out_coarse.clicked.connect(
            lambda: self.button_zoom_y_clicked(1. / 0.5))
        self.button_zoom_y_out_fine.clicked.connect(
            lambda: self.button_zoom_y_clicked(1. / 0.9))
        self.button_zoom_y_in_fine.clicked.connect(
            lambda: self.button_zoom_y_clicked(0.9))
        self.button_zoom_y_in_coarse.clicked.connect(
            lambda: self.button_zoom_y_clicked(0.5))
        self.check_voltage_persists.toggled.connect(
            self.check_voltage_persists_toggled)

        self.spin_mark_min.valueChanged.connect(self.spin_mark_min_changed)
        self.spin_mark_max.valueChanged.connect(self.spin_mark_max_changed)
        self.button_mark_min_reset.clicked.connect(
            self.button_mark_min_reset_clicked)
        self.button_mark_max_reset.clicked.connect(
            self.button_mark_max_reset_clicked)
        self.button_zoom_to_selection.clicked.connect(
            self.button_zoom_to_selection_clicked)

        self.spin_threshold.valueChanged.connect(
            lambda new_value: self.set_config_value('lineSearch', 'threshold',
                                                    new_value))
        self.button_find_lines.clicked.connect(
            lambda: self.plot.find_lines(self.spin_threshold.value()))
        self.button_clear_lines.clicked.connect(self.plot.clear_lines)
        self.button_prev_line.clicked.connect(self.prev_found_line)
        self.button_next_line.clicked.connect(self.next_found_line)

        self.mpl_connect_cid = self.canvas.mpl_connect('button_press_event',
                                                       self.plot_on_click)

    def setup_ui(self):
        self.resize(484, 441)
        self.setWindowIcon(backend.load_icon('sweep'))

        self.grid_layout_frequency.addWidget(self.label_frequency_min, 1, 0, 1,
                                             2)
        self.grid_layout_frequency.addWidget(self.label_frequency_max, 0, 0, 1,
                                             2)
        self.grid_layout_frequency.addWidget(self.label_frequency_center, 2, 0,
                                             1, 2)
        self.grid_layout_frequency.addWidget(self.label_frequency_span, 3, 0,
                                             1, 2)
        self.grid_layout_frequency.addWidget(self.spin_frequency_min, 1, 2, 1,
                                             2)
        self.grid_layout_frequency.addWidget(self.spin_frequency_max, 0, 2, 1,
                                             2)
        self.grid_layout_frequency.addWidget(self.spin_frequency_center, 2, 2,
                                             1, 2)
        self.grid_layout_frequency.addWidget(self.spin_frequency_span, 3, 2, 1,
                                             2)
        self.grid_layout_frequency.addWidget(self.check_frequency_persists, 4,
                                             0, 1, 4)

        self.grid_layout_voltage.addWidget(self.label_voltage_min, 1, 0, 1, 2)
        self.grid_layout_voltage.addWidget(self.label_voltage_max, 0, 0, 1, 2)
        self.grid_layout_voltage.addWidget(self.spin_voltage_min, 1, 2, 1, 2)
        self.grid_layout_voltage.addWidget(self.spin_voltage_max, 0, 2, 1, 2)
        self.grid_layout_voltage.addWidget(self.check_voltage_persists, 2, 0,
                                           1, 4)

        self.grid_layout_frequency.addWidget(self.button_zoom_x_out_coarse, 5,
                                             0)
        self.grid_layout_frequency.addWidget(self.button_zoom_x_out_fine, 5, 1)
        self.grid_layout_frequency.addWidget(self.button_zoom_x_in_fine, 5, 2)
        self.grid_layout_frequency.addWidget(self.button_zoom_x_in_coarse, 5,
                                             3)

        self.grid_layout_frequency.addWidget(self.button_move_x_left_coarse, 6,
                                             0)
        self.grid_layout_frequency.addWidget(self.button_move_x_left_fine, 6,
                                             1)
        self.grid_layout_frequency.addWidget(self.button_move_x_right_fine, 6,
                                             2)
        self.grid_layout_frequency.addWidget(self.button_move_x_right_coarse,
                                             6, 3)

        self.grid_layout_voltage.addWidget(self.button_zoom_y_out_coarse, 3, 0)
        self.grid_layout_voltage.addWidget(self.button_zoom_y_out_fine, 3, 1)
        self.grid_layout_voltage.addWidget(self.button_zoom_y_in_fine, 3, 2)
        self.grid_layout_voltage.addWidget(self.button_zoom_y_in_coarse, 3, 3)

        self.grid_layout_mark.addWidget(self.label_mark_min, 1, 0)
        self.grid_layout_mark.addWidget(self.label_mark_max, 0, 0)
        self.grid_layout_mark.addWidget(self.spin_mark_min, 1, 1)
        self.grid_layout_mark.addWidget(self.spin_mark_max, 0, 1)
        self.grid_layout_mark.addWidget(self.button_mark_min_reset, 1, 2)
        self.grid_layout_mark.addWidget(self.button_mark_max_reset, 0, 2)
        self.grid_layout_mark.addWidget(self.button_zoom_to_selection, 2, 0, 1,
                                        3)

        self.grid_layout_find_lines.addWidget(self.label_threshold, 0, 0)
        self.grid_layout_find_lines.addWidget(self.spin_threshold, 0, 1)
        self.grid_layout_find_lines.addWidget(self.button_find_lines, 1, 0, 1,
                                              2)
        self.grid_layout_find_lines.addWidget(self.button_clear_lines, 2, 0, 1,
                                              2)
        self.grid_layout_find_lines.addWidget(self.button_prev_line, 3, 0)
        self.grid_layout_find_lines.addWidget(self.button_next_line, 3, 1)

        _value_label_interaction_flags = (Qt.LinksAccessibleByKeyboard
                                          | Qt.LinksAccessibleByMouse
                                          | Qt.TextBrowserInteraction
                                          | Qt.TextSelectableByKeyboard
                                          | Qt.TextSelectableByMouse)

        self.grid_layout.addWidget(self.group_frequency, 2, 1)
        self.grid_layout.addWidget(self.group_voltage, 3, 1)
        self.grid_layout.addWidget(self.group_mark, 4, 1)
        self.grid_layout.addWidget(self.group_find_lines, 5, 1)

        self.grid_layout.addWidget(self.plot_toolbar, 0, 0, 1, 2)
        self.grid_layout.addWidget(self.canvas, 1, 0, 5, 1)
        self.grid_layout.addWidget(self.legend_canvas, 1, 1)

        self.setCentralWidget(self.central_widget)

        self.translate_ui()
        self.adjustSize()

    def translate_ui(self):
        _translate = QCoreApplication.translate
        self.setWindowTitle(_translate('main window', 'Fast Sweep Viewer'))

        self.plot_toolbar.parameters_title = _translate(
            'plot config window title', 'Figure options')

        suffix_mhz = ' ' + _translate('unit', 'MHz')
        suffix_mv = ' ' + _translate('unit', 'mV')

        self.group_frequency.setTitle(_translate('main window', 'Frequency'))
        self.label_frequency_min.setText(
            _translate('main window', 'Minimum') + ':')
        self.label_frequency_max.setText(
            _translate('main window', 'Maximum') + ':')
        self.label_frequency_center.setText(
            _translate('main window', 'Center') + ':')
        self.label_frequency_span.setText(
            _translate('main window', 'Span') + ':')
        self.check_frequency_persists.setText(
            _translate('main window', 'Keep frequency range'))

        self.button_zoom_x_out_coarse.setText(_translate(
            'main window', '−50%'))
        self.button_zoom_x_out_fine.setText(_translate('main window', '−10%'))
        self.button_zoom_x_in_fine.setText(_translate('main window', '+10%'))
        self.button_zoom_x_in_coarse.setText(_translate('main window', '+50%'))

        self.button_move_x_left_coarse.setText(
            _translate('main window', '−500') + suffix_mhz)
        self.button_move_x_left_fine.setText(
            _translate('main window', '−50') + suffix_mhz)
        self.button_move_x_right_fine.setText(
            _translate('main window', '+50') + suffix_mhz)
        self.button_move_x_right_coarse.setText(
            _translate('main window', '+500') + suffix_mhz)

        self.group_voltage.setTitle(_translate('main window', 'Voltage'))
        self.label_voltage_min.setText(
            _translate('main window', 'Minimum') + ':')
        self.label_voltage_max.setText(
            _translate('main window', 'Maximum') + ':')
        self.check_voltage_persists.setText(
            _translate('main window', 'Keep voltage range'))

        self.button_zoom_y_out_coarse.setText(_translate(
            'main window', '−50%'))
        self.button_zoom_y_out_fine.setText(_translate('main window', '−10%'))
        self.button_zoom_y_in_fine.setText(_translate('main window', '+10%'))
        self.button_zoom_y_in_coarse.setText(_translate('main window', '+50%'))

        self.group_mark.setTitle(_translate('main window', 'Selection'))
        self.label_mark_min.setText(_translate('main window', 'Minimum') + ':')
        self.label_mark_max.setText(_translate('main window', 'Maximum') + ':')
        if self.button_mark_min_reset.icon().isNull():
            self.button_mark_min_reset.setText(
                _translate('main window', 'Reset'))
        if self.button_mark_max_reset.icon().isNull():
            self.button_mark_max_reset.setText(
                _translate('main window', 'Reset'))
        self.button_zoom_to_selection.setText(
            _translate('main window', 'Zoom to Selection'))

        self.group_find_lines.setTitle(_translate('main window', 'Find Lines'))
        self.group_find_lines.setToolTip(
            _translate('main window', 'Try to detect lines automatically'))
        self.label_threshold.setText(
            _translate('main window', 'Search threshold') + ':')
        self.button_find_lines.setText(_translate('main window', 'Find Lines'))
        self.button_clear_lines.setText(
            _translate('main window', 'Clear Lines'))
        self.button_prev_line.setText(
            _translate('main window', 'Previous Line'))
        self.button_next_line.setText(_translate('main window', 'Next Line'))

        self.spin_frequency_min.setSuffix(suffix_mhz)
        self.spin_frequency_max.setSuffix(suffix_mhz)
        self.spin_frequency_center.setSuffix(suffix_mhz)
        self.spin_frequency_span.setSuffix(suffix_mhz)
        self.spin_voltage_min.setSuffix(suffix_mv)
        self.spin_voltage_max.setSuffix(suffix_mv)
        self.spin_mark_min.setSuffix(suffix_mhz)
        self.spin_mark_max.setSuffix(suffix_mhz)

    def closeEvent(self, event):
        """ senseless joke in the loop """
        _translate = QCoreApplication.translate
        close_code = QMessageBox.No
        while close_code == QMessageBox.No:
            close = QMessageBox()
            close.setText(_translate('main window', 'Are you sure?'))
            close.setIcon(QMessageBox.Question)
            close.setWindowIcon(self.windowIcon())
            close.setWindowTitle(self.windowTitle())
            close.setStandardButtons(QMessageBox.Yes | QMessageBox.No
                                     | QMessageBox.Cancel)
            close_code = close.exec()

            if close_code == QMessageBox.Yes:
                self.settings.setValue('windowGeometry', self.saveGeometry())
                self.settings.setValue('windowState', self.saveState())
                self.settings.sync()
                event.accept()
            elif close_code == QMessageBox.Cancel:
                event.ignore()
        return

    def load_config(self):
        self._loading = True
        # common settings
        if self.settings.contains('windowGeometry'):
            self.restoreGeometry(self.settings.value('windowGeometry', ''))
        else:
            window_frame = self.frameGeometry()
            desktop_center = QDesktopWidget().availableGeometry().center()
            window_frame.moveCenter(desktop_center)
            self.move(window_frame.topLeft())
        _v = self.settings.value('windowState', '')
        if isinstance(_v, str):
            self.restoreState(_v.encode())
        else:
            self.restoreState(_v)

        min_freq = self.get_config_value('frequency', 'lower',
                                         self.spin_frequency_min.minimum(),
                                         float)
        max_freq = self.get_config_value('frequency', 'upper',
                                         self.spin_frequency_min.maximum(),
                                         float)
        self.spin_frequency_min.setValue(min_freq)
        self.spin_frequency_max.setValue(max_freq)
        self.spin_frequency_min.setMaximum(max_freq)
        self.spin_frequency_max.setMinimum(min_freq)
        self.spin_frequency_span.setValue(max_freq - min_freq)
        self.spin_frequency_center.setValue(0.5 * (max_freq + min_freq))
        self.plot.set_frequency_range(lower_value=min_freq,
                                      upper_value=max_freq)
        self.check_frequency_persists.setChecked(
            self.get_config_value('frequency', 'persists', False, bool))

        min_voltage = self.get_config_value('voltage', 'lower',
                                            self.spin_voltage_min.minimum(),
                                            float)
        max_voltage = self.get_config_value('voltage', 'upper',
                                            self.spin_voltage_min.maximum(),
                                            float)
        self.spin_voltage_min.setValue(min_voltage)
        self.spin_voltage_max.setValue(max_voltage)
        self.spin_voltage_min.setMaximum(max_voltage)
        self.spin_voltage_max.setMinimum(min_voltage)
        self.plot.set_voltage_range(lower_value=min_voltage,
                                    upper_value=max_voltage)
        self.check_voltage_persists.setChecked(
            self.get_config_value('voltage', 'persists', False, bool))

        self.spin_threshold.setValue(
            self.get_config_value('lineSearch', 'threshold', 200.0, float))

        self._loading = False
        return

    def get_config_value(self, section, key, default, _type):
        if section not in self.settings.childGroups():
            return default
        self.settings.beginGroup(section)
        # print(section, key)
        try:
            v = self.settings.value(key, default, _type)
        except TypeError:
            v = default
        self.settings.endGroup()
        return v

    def set_config_value(self, section, key, value):
        if self._loading:
            return
        self.settings.beginGroup(section)
        # print(section, key, value, type(value))
        self.settings.setValue(key, value)
        self.settings.endGroup()

    def load_data(self, limits):
        if self._loading:
            return
        if limits is not None:
            min_freq, max_freq, min_voltage, max_voltage = limits
            self.set_config_value('frequency', 'lower', min_freq)
            self.set_config_value('frequency', 'upper', max_freq)
            self.set_config_value('voltage', 'lower', min_voltage)
            self.set_config_value('voltage', 'upper', max_voltage)
            self._loading = True
            if not self.check_frequency_persists.isChecked():
                self.spin_frequency_min.setValue(min_freq)
                self.spin_frequency_max.setValue(max_freq)
                self.spin_frequency_span.setValue(max_freq - min_freq)
                self.spin_frequency_center.setValue(0.5 *
                                                    (max_freq + min_freq))
                self.spin_frequency_min.setMaximum(max_freq)
                self.spin_frequency_max.setMinimum(min_freq)
            else:
                self.spin_frequency_min.setMaximum(
                    max(max_freq, self.spin_frequency_min.value()))
                self.spin_frequency_max.setMinimum(
                    min(min_freq, self.spin_frequency_max.value()))
            if not self.check_voltage_persists.isChecked():
                self.spin_voltage_min.setValue(min_voltage)
                self.spin_voltage_max.setValue(max_voltage)
                self.spin_voltage_min.setMaximum(max_voltage)
                self.spin_voltage_max.setMinimum(min_voltage)
            else:
                self.spin_voltage_min.setMaximum(
                    max(max_voltage, self.spin_voltage_min.value()))
                self.spin_voltage_max.setMinimum(
                    min(min_voltage, self.spin_voltage_max.value()))
            self.spin_mark_min.setMinimum(
                min(min_freq, self.spin_mark_min.minimum()))
            self.spin_mark_max.setMaximum(
                max(max_freq, self.spin_mark_max.maximum()))
            self._loading = False
            self.plot.set_frequency_range(
                lower_value=self.spin_frequency_min.value(),
                upper_value=self.spin_frequency_max.value())
            self.plot.set_voltage_range(
                lower_value=self.spin_voltage_min.value(),
                upper_value=self.spin_voltage_max.value())
        self.figure.tight_layout()

    def spin_frequency_min_changed(self, new_value):
        if self._loading:
            return
        self.set_config_value('frequency', 'lower', new_value)
        self._loading = True
        self.spin_frequency_max.setMinimum(new_value)
        self.spin_frequency_center.setValue(
            0.5 * (new_value + self.spin_frequency_max.value()))
        self.spin_frequency_span.setValue(self.spin_frequency_max.value() -
                                          new_value)
        self.plot.set_frequency_range(lower_value=new_value)
        self._loading = False

    def spin_frequency_max_changed(self, new_value):
        if self._loading:
            return
        self.set_config_value('frequency', 'upper', new_value)
        self._loading = True
        self.spin_frequency_min.setMaximum(new_value)
        self.spin_frequency_center.setValue(
            0.5 * (self.spin_frequency_min.value() + new_value))
        self.spin_frequency_span.setValue(new_value -
                                          self.spin_frequency_min.value())
        self.plot.set_frequency_range(upper_value=new_value)
        self._loading = False

    def spin_frequency_center_changed(self, new_value):
        if self._loading:
            return
        freq_span = self.spin_frequency_span.value()
        min_freq = new_value - 0.5 * freq_span
        max_freq = new_value + 0.5 * freq_span
        self._loading = True
        self.set_config_value('frequency', 'lower', min_freq)
        self.set_config_value('frequency', 'upper', max_freq)
        self.spin_frequency_min.setMaximum(max_freq)
        self.spin_frequency_max.setMinimum(min_freq)
        self.spin_frequency_min.setValue(min_freq)
        self.spin_frequency_max.setValue(max_freq)
        self.plot.set_frequency_range(upper_value=max_freq,
                                      lower_value=min_freq)
        self._loading = False

    def spin_frequency_span_changed(self, new_value):
        if self._loading:
            return
        freq_center = self.spin_frequency_center.value()
        min_freq = freq_center - 0.5 * new_value
        max_freq = freq_center + 0.5 * new_value
        self._loading = True
        self.set_config_value('frequency', 'lower', min_freq)
        self.set_config_value('frequency', 'upper', max_freq)
        self.spin_frequency_min.setMaximum(max_freq)
        self.spin_frequency_max.setMinimum(min_freq)
        self.spin_frequency_min.setValue(min_freq)
        self.spin_frequency_max.setValue(max_freq)
        self.plot.set_frequency_range(upper_value=max_freq,
                                      lower_value=min_freq)
        self._loading = False

    def button_zoom_x_clicked(self, factor):
        if self._loading:
            return
        freq_span = self.spin_frequency_span.value() * factor
        freq_center = self.spin_frequency_center.value()
        min_freq = freq_center - 0.5 * freq_span
        max_freq = freq_center + 0.5 * freq_span
        self._loading = True
        self.set_config_value('frequency', 'lower', min_freq)
        self.set_config_value('frequency', 'upper', max_freq)
        self.spin_frequency_min.setMaximum(max_freq)
        self.spin_frequency_max.setMinimum(min_freq)
        self.spin_frequency_min.setValue(min_freq)
        self.spin_frequency_max.setValue(max_freq)
        self.spin_frequency_span.setValue(freq_span)
        self.plot.set_frequency_range(upper_value=max_freq,
                                      lower_value=min_freq)
        self._loading = False

    def button_move_x_clicked(self, shift):
        if self._loading:
            return
        freq_span = self.spin_frequency_span.value()
        freq_center = self.spin_frequency_center.value() + shift
        min_freq = freq_center - 0.5 * freq_span
        max_freq = freq_center + 0.5 * freq_span
        self._loading = True
        self.set_config_value('frequency', 'lower', min_freq)
        self.set_config_value('frequency', 'upper', max_freq)
        self.spin_frequency_min.setMaximum(max_freq)
        self.spin_frequency_max.setMinimum(min_freq)
        self.spin_frequency_min.setValue(min_freq)
        self.spin_frequency_max.setValue(max_freq)
        self.spin_frequency_center.setValue(freq_center)
        self.plot.set_frequency_range(upper_value=max_freq,
                                      lower_value=min_freq)
        self._loading = False

    def check_frequency_persists_toggled(self, new_value):
        if self._loading:
            return
        self.set_config_value('frequency', 'persists', new_value)

    def spin_voltage_min_changed(self, new_value):
        if self._loading:
            return
        self.set_config_value('voltage', 'lower', new_value)
        self._loading = True
        self.spin_voltage_max.setMinimum(new_value)
        self.plot.set_voltage_range(lower_value=new_value)
        self._loading = False

    def spin_voltage_max_changed(self, new_value):
        if self._loading:
            return
        self.set_config_value('voltage', 'upper', new_value)
        self._loading = True
        self.spin_voltage_min.setMaximum(new_value)
        self.plot.set_voltage_range(upper_value=new_value)
        self._loading = False

    def button_zoom_y_clicked(self, factor):
        if self._loading:
            return
        min_voltage = self.spin_voltage_min.value()
        max_voltage = self.spin_voltage_max.value()
        voltage_span = abs(max_voltage - min_voltage) * factor
        voltage_center = (max_voltage + min_voltage) * 0.5
        min_voltage = voltage_center - 0.5 * voltage_span
        max_voltage = voltage_center + 0.5 * voltage_span
        self._loading = True
        self.set_config_value('voltage', 'lower', min_voltage)
        self.set_config_value('voltage', 'upper', max_voltage)
        self.spin_voltage_min.setMaximum(max_voltage)
        self.spin_voltage_max.setMinimum(min_voltage)
        self.spin_voltage_min.setValue(min_voltage)
        self.spin_voltage_max.setValue(max_voltage)
        self.plot.set_voltage_range(upper_value=max_voltage,
                                    lower_value=min_voltage)
        self._loading = False

    def check_voltage_persists_toggled(self, new_value):
        if self._loading:
            return
        self.set_config_value('voltage', 'persists', new_value)

    def spin_mark_min_changed(self, new_value):
        if self._loading:
            return
        self._loading = True
        self.spin_mark_max.setMinimum(new_value)
        self.plot.set_mark(lower_value=new_value,
                           upper_value=self.spin_mark_max.value())
        self._loading = False

    def spin_mark_max_changed(self, new_value):
        if self._loading:
            return
        self._loading = True
        self.spin_mark_min.setMaximum(new_value)
        self.plot.set_mark(lower_value=self.spin_mark_min.value(),
                           upper_value=new_value)
        self._loading = False

    def button_mark_min_reset_clicked(self):
        self.spin_mark_min.setValue(self.spin_mark_min.minimum())

    def button_mark_max_reset_clicked(self):
        self.spin_mark_max.setValue(self.spin_mark_max.maximum())

    def button_zoom_to_selection_clicked(self):
        self.spin_frequency_min.setValue(self.spin_mark_min.value())
        self.spin_frequency_max.setValue(self.spin_mark_max.value())

    def prev_found_line(self):
        self.spin_frequency_center.setValue(
            self.plot.prev_found_line(self.spin_frequency_center.value()))

    def next_found_line(self):
        self.spin_frequency_center.setValue(
            self.plot.next_found_line(self.spin_frequency_center.value()))

    def plot_on_click(self, event):
        if self._loading:
            return
        if event.inaxes is not None:
            if event.dblclick \
                    and not self.plot.mark_mode \
                    and not self.plot.trace_mode \
                    and not self.plot.trace_multiple_mode:
                min_freq, max_freq, min_voltage, max_voltage = self.plot.on_double_click(
                    event)
                self.set_config_value('frequency', 'lower', min_freq)
                self.set_config_value('frequency', 'upper', max_freq)
                self.set_config_value('voltage', 'lower', min_voltage)
                self.set_config_value('voltage', 'upper', max_voltage)
                self._loading = True
                self.spin_frequency_min.setValue(min_freq)
                self.spin_frequency_max.setValue(max_freq)
                self.spin_frequency_min.setMaximum(max_freq)
                self.spin_frequency_max.setMinimum(min_freq)
                self.spin_frequency_span.setValue(max_freq - min_freq)
                self.spin_frequency_center.setValue(0.5 *
                                                    (max_freq + min_freq))
                self.spin_voltage_min.setValue(min_voltage)
                self.spin_voltage_max.setValue(max_voltage)
                self.spin_voltage_min.setMaximum(max_voltage)
                self.spin_voltage_max.setMinimum(min_voltage)
                self._loading = False
            elif self.plot.mark_mode:
                if self.plot.mode:
                    self.plot.actions_off()
                else:
                    if event.button == 1:
                        self.spin_mark_min.setValue(event.xdata)
                    else:
                        self.spin_mark_max.setValue(event.xdata)

    def open_file_dialog(self, _filter=''):
        directory = self.get_config_value('open', 'location', '', str)
        # native dialog misbehaves when running inside snap but Qt dialog is tortoise-like in NT
        options = QFileDialog.DontUseNativeDialog if os.name != 'nt' else QFileDialog.DontUseSheet
        filename, _filter = QFileDialog.getOpenFileName(filter=_filter,
                                                        directory=directory,
                                                        options=options)
        self.set_config_value('open', 'location', os.path.split(filename)[0])
        return filename, _filter

    def on_xlim_changed(self, xlim):
        if not hasattr(self, 'plot'):
            return
        min_freq, max_freq = xlim
        self.set_config_value('frequency', 'lower', min_freq)
        self.set_config_value('frequency', 'upper', max_freq)
        self._loading = True
        self.spin_frequency_min.setValue(min_freq)
        self.spin_frequency_max.setValue(max_freq)
        self.spin_frequency_span.setValue(max_freq - min_freq)
        self.spin_frequency_center.setValue(0.5 * (max_freq + min_freq))
        self.spin_frequency_min.setMaximum(max_freq)
        self.spin_frequency_max.setMinimum(min_freq)
        self.spin_mark_min.setMinimum(
            min(min_freq, self.spin_mark_min.minimum()))
        self.spin_mark_max.setMaximum(
            max(max_freq, self.spin_mark_max.maximum()))
        self._loading = False
        self.plot.set_frequency_range(
            lower_value=self.spin_frequency_min.value(),
            upper_value=self.spin_frequency_max.value())

    def on_ylim_changed(self, ylim):
        if not hasattr(self, 'plot'):
            return
        min_voltage, max_voltage = ylim
        self.set_config_value('voltage', 'lower', min_voltage)
        self.set_config_value('voltage', 'upper', max_voltage)
        self._loading = True
        self.spin_voltage_min.setValue(min_voltage)
        self.spin_voltage_max.setValue(max_voltage)
        self.spin_voltage_min.setMaximum(max_voltage)
        self.spin_voltage_max.setMinimum(min_voltage)
        self._loading = False
        self.plot.set_voltage_range(lower_value=self.spin_voltage_min.value(),
                                    upper_value=self.spin_voltage_max.value())
示例#32
0
文件: models.py 项目: peterwup/tdm
class TasmotaDevicesModel(QAbstractTableModel):
    def __init__(self, tasmota_env):
        super().__init__()
        self.settings = QSettings("{}/TDM/tdm.cfg".format(QDir.homePath()), QSettings.IniFormat)
        self.devices = QSettings("{}/TDM/devices.cfg".format(QDir.homePath()), QSettings.IniFormat)
        self.tasmota_env = tasmota_env
        self.columns = []

        self.devices_short_version = self.settings.value("devices_short_version", True, bool)

        for d in self.tasmota_env.devices:
            d.property_changed = self.notify_change
            d.module_changed = self.module_change

    def setupColumns(self, columns):
        self.beginResetModel()
        self.columns = columns
        self.endResetModel()

    def deviceAtRow(self, row):
        if len(self.tasmota_env.devices) > 0:
            return self.tasmota_env.devices[row]
        return None

    def notify_change(self, d, key):
        row = self.tasmota_env.devices.index(d)
        if key.startswith("POWER") and "Power" in self.columns:
            power_idx = self.columns.index("Power")
            idx = self.index(row, power_idx)
            self.dataChanged.emit(idx, idx)

        elif key in ("RSSI", "LWT", "DeviceName", 'FriendlyName1'):
            idx = self.index(row, self.columns.index("Device"))
            self.dataChanged.emit(idx, idx)

        elif key in self.columns:
            col = self.columns.index(key)
            idx = self.index(row, col)
            self.dataChanged.emit(idx, idx)

    def module_change(self, d):
        self.notify_change(d, "Module")

    def columnCount(self, parent=None):
        return len(self.columns)

    def rowCount(self, parent=None):
        return len(self.tasmota_env.devices)

    def flags(self, idx):
        return Qt.ItemIsSelectable | Qt.ItemIsEnabled

    def headerData(self, col, orientation, role=Qt.DisplayRole):
        if orientation == Qt.Horizontal and role == Qt.DisplayRole:
            return self.columns[col]

    def data(self, idx, role=Qt.DisplayRole):
        if idx.isValid():
            row = idx.row()
            col = idx.column()
            col_name = self.columns[col]
            d = self.tasmota_env.devices[row]

            if role in [Qt.DisplayRole, Qt.EditRole]:
                val = d.p.get(col_name, "")

                if col_name == "Device":
                    val = d.name

                elif col_name == "Module":
                    if val == 0:
                        return d.p['Template'].get('NAME', "Fetching template name...")
                    else:
                        return d.module()

                elif col_name == "Version" and val:
                    if self.devices_short_version and "(" in val:
                        return val[0:val.index("(")]
                    return val.replace("(", " (")

                elif col_name in ("Uptime", "Downtime") and val:
                    val = str(val)
                    if val.startswith("0T"):
                        val = val.replace('0T', '')
                    val = val.replace('T', 'd ')

                elif col_name == "Core" and val:
                    return val.replace('_', '.')

                elif col_name == "Time" and val:
                    return val.replace('T', ' ')

                elif col_name == "Power":
                    return d.power()

                elif col_name == "Color":
                    return d.color()

                elif col_name == "CommandTopic":
                    return d.cmnd_topic()

                elif col_name == "StatTopic":
                    return d.stat_topic()

                elif col_name == "TeleTopic":
                    return d.tele_topic()

                elif col_name == "FallbackTopic":
                    return "cmnd/{}_fb/".format(d.p.get('MqttClient'))

                elif col_name == "BSSId":
                    alias = self.settings.value("BSSId/{}".format(val))
                    if alias:
                        return alias

                elif col_name == "RSSI":
                    val = int(d.p.get("RSSI", 0))
                    return val

                return val

            elif role == LWTRole:
                val = d.p.get('LWT', 'Offline')
                return val

            elif role == RestartReasonRole:
                val = d.p.get('RestartReason')
                return val

            elif role == RSSIRole:
                val = int(d.p.get('RSSI', 0))
                return val

            elif role == FirmwareRole:
                val = d.p.get('Version', "")
                return val

            elif role == Qt.TextAlignmentRole:
                # Left-aligned columns
                if col_name in ("Device", "Module", "RestartReason", "OtaUrl", "Hostname") or col_name.endswith("Topic"):
                    return Qt.AlignLeft | Qt.AlignVCenter | Qt.TextWordWrap

                # Right-aligned columns
                elif col_name in ("Uptime"):
                    return Qt.AlignRight | Qt.AlignVCenter

                else:
                    return Qt.AlignCenter

            elif role == Qt.DecorationRole and col_name == "Device":
                if d.p['LWT'] == "Online":
                    rssi = int(d.p.get("RSSI", 0))

                    if rssi > 0 and rssi < 50:
                        return QIcon(":/status_low.png")

                    elif rssi < 75:
                        return QIcon(":/status_medium.png")

                    elif rssi >= 75:
                        return QIcon(":/status_high.png")

                return QIcon(":/status_offline.png")

            elif role == Qt.InitialSortOrderRole:
                if col_name in ("Uptime", "Downtime"):
                    val = d.p.get(col_name, "")
                    if val:
                        d, hms = val.split("T")
                        h, m, s = hms.split(":")
                        return int(s) + int(m) * 60 + int(h) * 3600 + int(d) * 86400
                else:
                    return idx.data()

            elif role == Qt.ToolTipRole:
                if col_name == "Version":
                    val = d.p.get('Version')
                    if val:
                        return val[val.index("(")+1:val.index(")")]
                    return ""

                elif col_name == "BSSId":
                    return d.p.get('BSSId')

                elif col_name == "Device":
                    fns = [d.name]

                    for i in range(2, 5):
                        fn = d.p.get("FriendlyName{}".format(i))
                        if fn:
                            fns.append(fn)
                    return "\n".join(fns)

    def addDevice(self, device):
        self.beginInsertRows(QModelIndex(), 0, 0)
        device.property_changed = self.notify_change
        device.module_changed = self.module_change
        self.endInsertRows()

    def removeRows(self, pos, rows, parent=QModelIndex()):
        if pos + rows <= self.rowCount():
            self.beginRemoveRows(parent, pos, pos + rows - 1)
            device = self.deviceAtRow(pos)
            self.tasmota_env.devices.pop(self.tasmota_env.devices.index(device))

            topic = device.p['Topic']
            self.settings.beginGroup("Devices")
            if topic in self.settings.childGroups():
                self.settings.remove(topic)
            self.settings.endGroup()

            self.endRemoveRows()
            return True
        return False

    def deleteDevice(self, idx):
        row = idx.row()
        mac = self.deviceAtRow(row).p['Mac'].replace(":", "-")
        self.devices.remove(mac)
        self.devices.sync()
        self.removeRows(row, 1)

    def columnIndex(self, column):
        return self.columns.index(column)
示例#33
0
 def saveSettings(self):
     """Saves the settings to config."""
     s = QSettings()
     s.beginGroup("sidebar")
     s.setValue("line_numbers", self.lineNumbersVisible())
     s.setValue("folding", self.foldingVisible())
示例#34
0
 def updateSettings(self):
     from PyQt5.QtCore import QSettings
     s = QSettings()
     s.beginGroup("editor_highlighting")
     self._match_duration = s.value("match", 1, int) * 1000
示例#35
0
class DocumentWindow(QMainWindow, ui_mainwindow.Ui_MainWindow):
    """:class`DocumentWindow` subclasses :class`QMainWindow` and
    :class`Ui_MainWindow`. It performs some initialization operations that
    must be done in code rather than using Qt Creator.

    Attributes:
        controller: DocumentController
    """
    def __init__(self, doc_ctrlr: DocCtrlT, parent=None):
        super(DocumentWindow, self).__init__(parent)

        self.controller = doc_ctrlr
        doc = doc_ctrlr.document()
        self.setupUi(self)
        self.settings = QSettings("cadnano.org", "cadnano2.5")
        # Appearance pref
        if not app().prefs.show_icon_labels:
            self.main_toolbar.setToolButtonStyle(Qt.ToolButtonIconOnly)

        # Outliner & PropertyEditor setup
        self.outliner_widget.configure(window=self, document=doc)
        self.property_widget.configure(window=self, document=doc)
        self.property_buttonbox.setVisible(False)

        self.tool_managers = None  # initialize

        self.views = {}
        self.views[ViewSendEnum.SLICE] = self._initSliceview(doc)
        self.views[ViewSendEnum.GRID] = self._initGridview(doc)
        self.views[ViewSendEnum.PATH] = self._initPathview(doc)

        self._initPathviewToolbar()
        self._initEditMenu()

        self.path_dock_widget.setTitleBarWidget(QWidget())
        self.grid_dock_widget.setTitleBarWidget(QWidget())
        self.slice_dock_widget.setTitleBarWidget(QWidget())
        self.inspector_dock_widget.setTitleBarWidget(QWidget())

        self.setCentralWidget(None)
        if app().prefs.orthoview_style_idx == OrthoViewEnum.SLICE:
            self.splitDockWidget(self.slice_dock_widget, self.path_dock_widget,
                                 Qt.Horizontal)
        elif app().prefs.orthoview_style_idx == OrthoViewEnum.GRID:
            self.splitDockWidget(self.grid_dock_widget, self.path_dock_widget,
                                 Qt.Horizontal)
        self._restoreGeometryandState()
        self._finishInit()

        doc.setViewNames(['slice', 'path', 'inspector'])

    # end def

    def document(self) -> DocT:
        return self.controller.document()

    def destroyWin(self):
        '''Save window state and destroy the tool managers.  Also destroy
        :class`DocumentController` object
        '''
        self.settings.beginGroup("MainWindow")
        # Saves the current state of this mainwindow's toolbars and dockwidgets
        self.settings.setValue("windowState", self.saveState())
        self.settings.endGroup()
        for mgr in self.tool_managers:
            mgr.destroyItem()
        self.controller = None

    ### ACCESSORS ###
    def undoStack(self) -> UndoStack:
        return self.controller.undoStack()

    def activateSelection(self, is_active: bool):
        self.path_graphics_view.activateSelection(is_active)
        self.slice_graphics_view.activateSelection(is_active)
        self.grid_graphics_view.activateSelection(is_active)

    ### EVENT HANDLERS ###
    def focusInEvent(self):
        """Handle an OS focus change into cadnano."""
        app().undoGroup.setActiveStack(self.controller.undoStack())

    def moveEvent(self, event: QMoveEvent):
        """Handle the moving of the cadnano window itself.

        Reimplemented to save state on move.
        """
        self.settings.beginGroup("MainWindow")
        self.settings.setValue("geometry", self.saveGeometry())
        self.settings.setValue("pos", self.pos())
        self.settings.endGroup()

    def resizeEvent(self, event: QResizeEvent):
        """Handle the resizing of the cadnano window itself.

        Reimplemented to save state on resize.
        """
        self.settings.beginGroup("MainWindow")
        self.settings.setValue("geometry", self.saveGeometry())
        self.settings.setValue("size", self.size())
        self.settings.endGroup()
        QWidget.resizeEvent(self, event)

    def changeEvent(self, event: QEvent):
        QWidget.changeEvent(self, event)

    # end def

    ### DRAWING RELATED ###

    ### PRIVATE HELPER METHODS ###
    def _restoreGeometryandState(self):
        settings = self.settings
        settings.beginGroup("MainWindow")
        geometry = settings.value("geometry")
        if geometry is not None:
            result = self.restoreGeometry(geometry)
            if result is False:
                print("MainWindow.restoreGeometry() failed.")
        else:
            print("Setting default MainWindow size: 1100x800")
            self.resize(settings.value("size", QSize(1100, 800)))
            self.move(settings.value("pos", QPoint(200, 200)))
            self.inspector_dock_widget.close()
            self.action_inspector.setChecked(False)

        # Restore the current state of this mainwindow's toolbars and dockwidgets
        window_state = settings.value("windowState")
        if window_state is not None:
            result = self.restoreState(window_state)
            if result is False:
                print("MainWindow.restoreState() failed.")
        settings.endGroup()

    # end def

    def destroyView(self, view_type: EnumType):
        '''
        Args:
            view_type: the name of the view

        Raises:
            ValueError for :obj:`view_type` not existing
        '''
        cnview = self.views.get(view_type)
        if cnview is not None:
            root_item = cnview.rootItem()
            root_item.destroyViewItems()
        else:
            raise ValueError("view_type: %s does not exist" % (view_type))

    def rebuildView(self, view_type: EnumType):
        '''Rebuild views which match view_type ORed argument.
        Only allows SLICE or GRID view to be active.  Not both at the same time

        Args:
            view_type: one or more O
        '''
        doc = self.document()

        # turn OFF all but the views we care about
        doc.changeViewSignaling(view_type)

        delta = 0
        if view_type & ViewSendEnum.SLICE:
            delta = ViewSendEnum.GRID
            self.destroyView(delta)
        elif view_type & ViewSendEnum.GRID:
            delta = ViewSendEnum.GRID
            self.destroyView(delta)

        for part in doc.getParts():
            reEmitPart(part)

        # turn ON all but the views we care about
        doc.changeViewSignaling(ViewSendEnum.ALL - delta)

    # end def

    def _initGridview(self, doc: DocT) -> GraphicsViewT:
        """Initializes Grid View.

        Args:
            doc: The :class:`Document` corresponding to the design

        Returns:
            :class:`CustomQGraphicsView`
        """
        grid_scene = QGraphicsScene(parent=self.grid_graphics_view)
        grid_root = GridRootItem(rect=grid_scene.sceneRect(),
                                 parent=None,
                                 window=self,
                                 document=doc)
        grid_scene.addItem(grid_root)
        grid_scene.setItemIndexMethod(QGraphicsScene.NoIndex)
        assert grid_root.scene() == grid_scene
        ggv = self.grid_graphics_view
        ggv.setScene(grid_scene)
        ggv.scene_root_item = grid_root
        ggv.setName("GridView")
        self.grid_tool_manager = GridToolManager(self, grid_root)
        return ggv

    # end def

    def _initPathview(self, doc: DocT) -> GraphicsViewT:
        """Initializes Path View.

        Args:
            doc: The :class:`Document` corresponding to the design

        Returns:
            :class:`CustomQGraphicsView`
        """
        path_scene = QGraphicsScene(parent=self.path_graphics_view)
        path_root = PathRootItem(rect=path_scene.sceneRect(),
                                 parent=None,
                                 window=self,
                                 document=doc)
        path_scene.addItem(path_root)
        path_scene.setItemIndexMethod(QGraphicsScene.NoIndex)
        assert path_root.scene() == path_scene
        pgv = self.path_graphics_view
        pgv.setScene(path_scene)
        pgv.scene_root_item = path_root
        pgv.setScaleFitFactor(0.7)
        pgv.setName("PathView")
        return pgv

    # end def

    def _initPathviewToolbar(self):
        """Initializes Path View Toolbar.
        """
        self.path_color_panel = ColorPanel()
        self.path_graphics_view.toolbar = self.path_color_panel  # HACK for customqgraphicsview
        path_view = self.views[ViewSendEnum.PATH]
        path_scene = path_view.cnScene()
        path_scene.addItem(self.path_color_panel)
        self.path_tool_manager = PathToolManager(self, path_view.rootItem())

        self.slice_tool_manager.path_tool_manager = self.path_tool_manager
        self.path_tool_manager.slice_tool_manager = self.slice_tool_manager

        self.grid_tool_manager.path_tool_manager = self.path_tool_manager
        self.path_tool_manager.grid_tool_manager = self.grid_tool_manager

        self.tool_managers = (self.path_tool_manager, self.slice_tool_manager,
                              self.grid_tool_manager)

        self.insertToolBarBreak(self.main_toolbar)

        self.path_graphics_view.setupGL()
        self.slice_graphics_view.setupGL()
        self.grid_graphics_view.setupGL()

    # end def

    def _initSliceview(self, doc: DocT) -> GraphicsViewT:
        """Initializes Slice View.

        Args:
            doc: The :class:`Document` corresponding to the design

        Returns:
            :class:`CustomQGraphicsView`
        """
        slice_scene = QGraphicsScene(parent=self.slice_graphics_view)
        slice_root = SliceRootItem(rect=slice_scene.sceneRect(),
                                   parent=None,
                                   window=self,
                                   document=doc)
        slice_scene.addItem(slice_root)
        slice_scene.setItemIndexMethod(QGraphicsScene.NoIndex)
        assert slice_root.scene() == slice_scene
        sgv = self.slice_graphics_view
        sgv.setScene(slice_scene)
        sgv.scene_root_item = slice_root
        sgv.setName("SliceView")
        sgv.setScaleFitFactor(0.7)
        self.slice_tool_manager = SliceToolManager(self, slice_root)
        return sgv

    # end def

    def _initEditMenu(self):
        """Initializes the Edit menu
        """
        us = self.controller.undoStack()
        qatrans = QApplication.translate

        action_undo = us.createUndoAction(self)
        action_undo.setText(qatrans("MainWindow", "Undo", None))
        action_undo.setShortcut(qatrans("MainWindow", "Ctrl+Z", None))
        self.actionUndo = action_undo

        action_redo = us.createRedoAction(self)
        action_redo.setText(qatrans("MainWindow", "Redo", None))
        action_redo.setShortcut(qatrans("MainWindow", "Ctrl+Shift+Z", None))
        self.actionRedo = action_redo

        self.sep = sep = QAction(self)
        sep.setSeparator(True)

        self.menu_edit.insertAction(sep, action_redo)
        self.menu_edit.insertAction(action_redo, action_undo)
        # print([x.text() for x in self.menu_edit.actions()])

        # self.main_splitter.setSizes([400, 400, 180])  # balance main_splitter size
        self.statusBar().showMessage("")

    # end def

    def _finishInit(self):
        """Handle the dockwindow visibility and action checked status.
        The console visibility is explicitly stored in the settings file,
        since it doesn't seem to work if we treat it like a normal dock widget.
        """
        inspector_visible = self.inspector_dock_widget.isVisibleTo(self)
        self.action_inspector.setChecked(inspector_visible)
        path_visible = self.path_dock_widget.isVisibleTo(self)
        self.action_path.setChecked(path_visible)
        slice_visible = self.slice_dock_widget.isVisibleTo(self)

        # NOTE THIS IS ALWAYS FALSE FOR  SOME REASON
        self.action_slice.setChecked(slice_visible)

    # end def

    def getMouseViewTool(self, tool_type_name: str) -> AbstractTool:
        """Give a tool type, return the tool for the view the mouse is over

        Args:
            tool_type_name: the tool which is active or None

        Returns:
            active tool for the view the tool is in
        """
        return_tool = None
        for view in self.views.values():
            if view.underMouse():
                root_item = view.rootItem()
                # print("I am under mouse", view)
                if root_item.manager.isToolActive(tool_type_name):
                    # print("{} is active".format(tool_type_name))
                    return_tool = root_item.manager.activeToolGetter()
                else:
                    # print("no {} HERE!".format(tool_type_name))
                    pass
                break
        return return_tool

    # end def

    def doMouseViewDestroy(self):
        """Destroy the view the mouse is over
        """
        return_tool = None
        for name, view in self.views.items():
            if view.underMouse():
                self.destroyView(name)
示例#36
0
class DocumentController():
    """
    Connects UI buttons to their corresponding actions in the model.
    """

    ### INIT METHODS ###
    def __init__(self, document):
        """docstring for __init__"""
        # initialize variables
        self._document = document
        print("the doc", self._document)
        self._document.setController(self)
        self._active_part = None
        self._filename = None
        self._file_open_path = None  # will be set in _readSettings
        self._has_no_associated_file = True
        self._path_view_instance = None
        self._slice_view_instance = None
        self._undo_stack = None
        self.win = None
        self.fileopendialog = None
        self.filesavedialog = None

        self.settings = QSettings()
        self._readSettings()

        # call other init methods
        self._initWindow()
        app().document_controllers.add(self)

    def _initWindow(self):
        """docstring for initWindow"""
        self.win = DocumentWindow(doc_ctrlr=self)
        # self.win.setWindowIcon(app().icon)
        app().documentWindowWasCreatedSignal.emit(self._document, self.win)
        self._connectWindowSignalsToSelf()
        self.win.show()
        app().active_document = self

    def _initMaya(self):
        """
        Initialize Maya-related state. Delete Maya nodes if there
        is an old document left over from the same session. Set up
        the Maya window.
        """
        # There will only be one document
        if (app().active_document and app().active_document.win
                and not app().active_document.win.close()):
            return
        del app().active_document
        app().active_document = self

        import maya.OpenMayaUI as OpenMayaUI
        import sip
        ptr = OpenMayaUI.MQtUtil.mainWindow()
        mayaWin = sip.wrapinstance(int(ptr), QMainWindow)
        self.windock = QDockWidget("cadnano")
        self.windock.setFeatures(QDockWidget.DockWidgetMovable
                                 | QDockWidget.DockWidgetFloatable)
        self.windock.setAllowedAreas(Qt.LeftDockWidgetArea
                                     | Qt.RightDockWidgetArea)
        self.windock.setWidget(self.win)
        mayaWin.addDockWidget(Qt.DockWidgetArea(Qt.LeftDockWidgetArea),
                              self.windock)
        self.windock.setVisible(True)

    def _connectWindowSignalsToSelf(self):
        """This method serves to group all the signal & slot connections
        made by DocumentController"""
        self.win.action_new.triggered.connect(self.actionNewSlot)
        self.win.action_open.triggered.connect(self.actionOpenSlot)
        self.win.action_close.triggered.connect(self.actionCloseSlot)
        self.win.action_save.triggered.connect(self.actionSaveSlot)
        self.win.action_save_as.triggered.connect(self.actionSaveAsSlot)
        # self.win.action_SVG.triggered.connect(self.actionSVGSlot)
        # self.win.action_autostaple.triggered.connect(self.actionAutostapleSlot)
        # self.win.action_export_staples.triggered.connect(self.actionExportSequencesSlot)
        self.win.action_preferences.triggered.connect(self.actionPrefsSlot)
        self.win.action_modify.triggered.connect(self.actionModifySlot)
        self.win.action_new_honeycomb_part.triggered.connect(\
            self.actionAddHoneycombPartSlot)
        self.win.action_new_square_part.triggered.connect(\
            self.actionAddSquarePartSlot)
        self.win.closeEvent = self.windowCloseEventHandler
        self.win.action_about.triggered.connect(self.actionAboutSlot)
        self.win.action_cadnano_website.triggered.connect(
            self.actionCadnanoWebsiteSlot)
        self.win.action_feedback.triggered.connect(self.actionFeedbackSlot)
        self.win.action_filter_handle.triggered.connect(
            self.actionFilterHandleSlot)
        self.win.action_filter_endpoint.triggered.connect(
            self.actionFilterEndpointSlot)
        self.win.action_filter_strand.triggered.connect(
            self.actionFilterStrandSlot)
        self.win.action_filter_xover.triggered.connect(
            self.actionFilterXoverSlot)
        self.win.action_filter_scaf.triggered.connect(
            self.actionFilterScafSlot)
        self.win.action_filter_stap.triggered.connect(
            self.actionFilterStapSlot)

    ### SLOTS ###
    def undoStackCleanChangedSlot(self):
        """The title changes to include [*] on modification."""
        self.win.setWindowModified(not self.undoStack().isClean())
        self.win.setWindowTitle(self.documentTitle())

    def actionAboutSlot(self):
        """Displays the about cadnano dialog."""
        dialog = QDialog()
        dialog_about = Ui_About()  # reusing this dialog, should rename
        dialog.setStyleSheet(
            "QDialog { background-image: url(ui/dialogs/images/cadnano2-about.png); background-repeat: none; }"
        )
        dialog_about.setupUi(dialog)
        dialog.exec_()

    filter_list = ["strand", "endpoint", "xover", "virtual_helix"]

    def actionFilterHandleSlot(self):
        """Disables all other selection filters when active."""
        fH = self.win.action_filter_handle
        fE = self.win.action_filter_endpoint
        fS = self.win.action_filter_strand
        fX = self.win.action_filter_xover
        fH.setChecked(True)
        if fE.isChecked():
            fE.setChecked(False)
        if fS.isChecked():
            fS.setChecked(False)
        if fX.isChecked():
            fX.setChecked(False)
        self._document.documentSelectionFilterChangedSignal.emit(
            ["virtual_helix"])

    def actionFilterEndpointSlot(self):
        """
        Disables handle filters when activated.
        Remains checked if no other item-type filter is active.
        """
        fH = self.win.action_filter_handle
        fE = self.win.action_filter_endpoint
        fS = self.win.action_filter_strand
        fX = self.win.action_filter_xover
        if fH.isChecked():
            fH.setChecked(False)
        if not fS.isChecked() and not fX.isChecked():
            fE.setChecked(True)
        self._strandFilterUpdate()

    # end def

    def actionFilterStrandSlot(self):
        """
        Disables handle filters when activated.
        Remains checked if no other item-type filter is active.
        """
        fH = self.win.action_filter_handle
        fE = self.win.action_filter_endpoint
        fS = self.win.action_filter_strand
        fX = self.win.action_filter_xover
        if fH.isChecked():
            fH.setChecked(False)
        if not fE.isChecked() and not fX.isChecked():
            fS.setChecked(True)
        self._strandFilterUpdate()

    # end def

    def actionFilterXoverSlot(self):
        """
        Disables handle filters when activated.
        Remains checked if no other item-type filter is active.
        """
        fH = self.win.action_filter_handle
        fE = self.win.action_filter_endpoint
        fS = self.win.action_filter_strand
        fX = self.win.action_filter_xover
        if fH.isChecked():
            fH.setChecked(False)
        if not fE.isChecked() and not fS.isChecked():
            fX.setChecked(True)
        self._strandFilterUpdate()

    # end def

    def actionFilterScafSlot(self):
        """Remains checked if no other strand-type filter is active."""
        fSc = self.win.action_filter_scaf
        fSt = self.win.action_filter_stap
        if not fSc.isChecked() and not fSt.isChecked():
            fSc.setChecked(True)
        self._strandFilterUpdate()

    def actionFilterStapSlot(self):
        """Remains checked if no other strand-type filter is active."""
        fSc = self.win.action_filter_scaf
        fSt = self.win.action_filter_stap
        if not fSc.isChecked() and not fSt.isChecked():
            fSt.setChecked(True)
        self._strandFilterUpdate()

    # end def

    def _strandFilterUpdate(self):
        win = self.win
        filter_list = []
        if win.action_filter_endpoint.isChecked():
            filter_list.append("endpoint")
        if win.action_filter_strand.isChecked():
            filter_list.append("strand")
        if win.action_filter_xover.isChecked():
            filter_list.append("xover")
        if win.action_filter_scaf.isChecked():
            filter_list.append("scaffold")
        if win.action_filter_stap.isChecked():
            filter_list.append("staple")
        self._document.documentSelectionFilterChangedSignal.emit(filter_list)

    # end def

    def actionNewSlot(self):
        """
        1. If document is has no parts, do nothing.
        2. If document is dirty, call maybeSave and continue if it succeeds.
        3. Create a new document and swap it into the existing ctrlr/window.
        """
        # clear/reset the view!

        if len(self._document.parts()) == 0:
            return  # no parts
        if self.maybeSave() == False:
            return  # user canceled in maybe save
        else:  # user did not cancel
            if self.filesavedialog != None:
                self.filesavedialog.finished.connect(self.newClickedCallback)
            else:  # user did not save
                self.newClickedCallback()  # finalize new

    def actionOpenSlot(self):
        """
        1. If document is untouched, proceed to open dialog.
        2. If document is dirty, call maybesave and continue if it succeeds.
        Downstream, the file is selected in openAfterMaybeSave,
        and the selected file is actually opened in openAfterMaybeSaveCallback.
        """
        if self.maybeSave() == False:
            return  # user canceled in maybe save
        else:  # user did not cancel
            if hasattr(self, "filesavedialog"):  # user did save
                if self.filesavedialog != None:
                    self.filesavedialog.finished.connect(
                        self.openAfterMaybeSave)
                else:
                    self.openAfterMaybeSave()  # windows
            else:  # user did not save
                self.openAfterMaybeSave()  # finalize new

    def actionCloseSlot(self):
        """This will trigger a Window closeEvent."""
        # if util.isWindows():
        self.win.close()
        app().qApp.exit(0)

    def actionSaveSlot(self):
        """SaveAs if necessary, otherwise overwrite existing file."""
        if self._has_no_associated_file:
            self.saveFileDialog()
            return
        self.writeDocumentToFile()

    def actionSaveAsSlot(self):
        """Open a save file dialog so user can choose a name."""
        self.saveFileDialog()

    def actionSVGSlot(self):
        """docstring for actionSVGSlot"""
        fname = os.path.basename(str(self.filename()))
        if fname == None:
            directory = "."
        else:
            directory = QFileInfo(fname).path()

        fdialog = QFileDialog(self.win,
                              "%s - Save As" % QApplication.applicationName(),
                              directory,
                              "%s (*.svg)" % QApplication.applicationName())
        fdialog.setAcceptMode(QFileDialog.AcceptSave)
        fdialog.setWindowFlags(Qt.Sheet)
        fdialog.setWindowModality(Qt.WindowModal)
        self.svgsavedialog = fdialog
        self.svgsavedialog.filesSelected.connect(self.saveSVGDialogCallback)
        fdialog.open()

    class DummyChild(QGraphicsItem):
        def boundingRect(self):
            return QRect(200, 200)  # self.parentObject().boundingRect()

        def paint(self, painter, option, widget=None):
            pass

    def saveSVGDialogCallback(self, selected):
        if isinstance(selected, (list, tuple)):
            fname = selected[0]
        else:
            fname = selected
        print("da fname", fname)
        if fname is None or os.path.isdir(fname):
            return False
        if not fname.lower().endswith(".svg"):
            fname += ".svg"
        if self.svgsavedialog != None:
            self.svgsavedialog.filesSelected.disconnect(
                self.saveSVGDialogCallback)
            del self.svgsavedialog  # prevents hang
            self.svgsavedialog = None

        generator = QSvgGenerator()
        generator.setFileName(fname)
        generator.setSize(QSize(200, 200))
        generator.setViewBox(QRect(0, 0, 2000, 2000))
        painter = QPainter()

        # Render through scene
        # painter.begin(generator)
        # self.win.pathscene.render(painter)
        # painter.end()

        # Render item-by-item
        painter = QPainter()
        style_option = QStyleOptionGraphicsItem()
        q = [self.win.pathroot]
        painter.begin(generator)
        while q:
            graphics_item = q.pop()
            transform = graphics_item.itemTransform(self.win.sliceroot)[0]
            painter.setTransform(transform)
            if graphics_item.isVisible():
                graphics_item.paint(painter, style_option, None)
                q.extend(graphics_item.childItems())
        painter.end()

    def actionExportSequencesSlot(self):
        """
        Triggered by clicking Export Staples button. Opens a file dialog to
        determine where the staples should be saved. The callback is
        exportStaplesCallback which collects the staple sequences and exports
        the file.
        """
        # Validate that no staple oligos are loops.
        part = self.activePart()
        if part is None:
            return
        stap_loop_olgs = part.getStapleLoopOligos()
        if stap_loop_olgs:
            from ui.dialogs.ui_warning import Ui_Warning
            dialog = QDialog()
            dialogWarning = Ui_Warning()  # reusing this dialog, should rename
            dialog.setStyleSheet(
                "QDialog { background-image: url(ui/dialogs/images/cadnano2-about.png); background-repeat: none; }"
            )
            dialogWarning.setupUi(dialog)

            locs = ", ".join([o.locString() for o in stap_loop_olgs])
            msg = "Part contains staple loop(s) at %s.\n\nUse the break tool to introduce 5' & 3' ends before exporting. Loops have been colored red; use undo to revert." % locs
            dialogWarning.title.setText("Staple validation failed")
            dialogWarning.message.setText(msg)
            for o in stap_loop_olgs:
                o.applyColor(styles.stapColors[0].name())
            dialog.exec_()
            return

        # Proceed with staple export.
        fname = self.filename()
        if fname == None:
            directory = "."
        else:
            directory = QFileInfo(fname).path()
        if util.isWindows():  # required for native looking file window
            fname = QFileDialog.getSaveFileName(
                self.win, "%s - Export As" % QApplication.applicationName(),
                directory, "(*.csv)")
            self.saveStaplesDialog = None
            self.exportStaplesCallback(fname)
        else:  # access through non-blocking callback
            fdialog = QFileDialog(
                self.win, "%s - Export As" % QApplication.applicationName(),
                directory, "(*.csv)")
            fdialog.setAcceptMode(QFileDialog.AcceptSave)
            fdialog.setWindowFlags(Qt.Sheet)
            fdialog.setWindowModality(Qt.WindowModal)
            self.saveStaplesDialog = fdialog
            self.saveStaplesDialog.filesSelected.connect(
                self.exportStaplesCallback)
            fdialog.open()

    # end def

    def actionPrefsSlot(self):
        app().prefsClicked()

    # end def

    def actionAutostapleSlot(self):
        part = self.activePart()
        if part:
            self.win.path_graphics_view.setViewportUpdateOn(False)
            part.autoStaple()
            self.win.path_graphics_view.setViewportUpdateOn(True)

    # end def

    def actionModifySlot(self):
        """
        Notifies that part root items that parts should respond to modifier
        selection signals. 
        """
        pass
        # uncomment for debugging
        # isChecked = self.win.actionModify.isChecked()
        # self.win.pathroot.setModifyState(isChecked)
        # self.win.sliceroot.setModifyState(isChecked)
        # if app().isInMaya():
        #     isChecked = self.win.actionModify.isChecked()
        #     self.win.pathroot.setModifyState(isChecked)
        #     self.win.sliceroot.setModifyState(isChecked)
        #     self.win.solidroot.setModifyState(isChecked)

    def actionAddHoneycombPartSlot(self):
        part = self._document.addHoneycombPart()
        self.setActivePart(part)

    # end def

    def actionAddSquarePartSlot(self):
        part = self._document.addSquarePart()
        self.setActivePart(part)

    # end def

    def actionAddHpxPartSlot(self):
        # part = self._document.addHpxPart()
        # self.setActivePart(part)
        pass

    # end def

    def actionAddSpxPartSlot(self):
        # part = self._document.addHpxPart()
        # self.setActivePart(part)
        pass

    # end def

    def actionRenumberSlot(self):
        part = self.activePart()
        if part:
            coordList = self.win.pathroot.getSelectedPartOrderedVHList()
            part.renumber(coordList)

    # end def

    ### ACCESSORS ###
    def document(self):
        return self._document

    # end def

    def window(self):
        return self.win

    # end def

    def setDocument(self, doc):
        """
        Sets the controller's document, and informs the document that
        this is its controller.
        """
        self._document = doc
        doc.setController(self)

    # end def

    def activePart(self):
        if self._active_part == None:
            self._active_part = self._document.selectedPart()
        return self._active_part

    # end def

    def setActivePart(self, part):
        self._active_part = part

    # end def

    def undoStack(self):
        return self._document.undoStack()

    # end def

    ### PRIVATE SUPPORT METHODS ###
    def newDocument(self, doc=None, fname=None):
        """Creates a new Document, reusing the DocumentController."""
        if fname is not None and self._filename == fname:
            setReopen(True)
        self._document.resetViews()
        setBatch(True)
        self._document.removeAllParts()  # clear out old parts
        setBatch(False)
        self._document.undoStack().clear()  # reset undostack
        self._filename = fname if fname else "untitled.json"
        self._has_no_associated_file = fname == None
        self._active_part = None
        self.win.setWindowTitle(self.documentTitle() + '[*]')

    # end def

    def saveFileDialog(self):
        fname = self.filename()
        if fname == None:
            directory = "."
        else:
            directory = QFileInfo(fname).path()
        if util.isWindows():  # required for native looking file window
            fname = QFileDialog.getSaveFileName(
                self.win, "%s - Save As" % QApplication.applicationName(),
                directory, "%s (*.json)" % QApplication.applicationName())
            if isinstance(fname, (list, tuple)):
                fname = fname[0]
            self.writeDocumentToFile(fname)
        else:  # access through non-blocking callback
            fdialog = QFileDialog(
                self.win, "%s - Save As" % QApplication.applicationName(),
                directory, "%s (*.json)" % QApplication.applicationName())
            fdialog.setAcceptMode(QFileDialog.AcceptSave)
            fdialog.setWindowFlags(Qt.Sheet)
            fdialog.setWindowModality(Qt.WindowModal)
            self.filesavedialog = fdialog
            self.filesavedialog.filesSelected.connect(
                self.saveFileDialogCallback)
            fdialog.open()

    # end def

    def _readSettings(self):
        self.settings.beginGroup("FileSystem")
        self._file_open_path = self.settings.value("openpath",
                                                   QDir().homePath())
        self.settings.endGroup()

    def _writeFileOpenPath(self, path):
        """docstring for _writePath"""
        self._file_open_path = path
        self.settings.beginGroup("FileSystem")
        self.settings.setValue("openpath", path)
        self.settings.endGroup()

    ### SLOT CALLBACKS ###
    def actionNewSlotCallback(self):
        """
        Gets called on completion of filesavedialog after newClicked's 
        maybeSave. Removes the dialog if necessary, but it was probably
        already removed by saveFileDialogCallback.
        """
        if self.filesavedialog != None:
            self.filesavedialog.finished.disconnect(self.actionNewSlotCallback)
            del self.filesavedialog  # prevents hang (?)
            self.filesavedialog = None
        self.newDocument()

    def exportStaplesCallback(self, selected):
        """Export all staple sequences to selected CSV file."""
        if isinstance(selected, (list, tuple)):
            fname = selected[0]
        else:
            fname = selected
        if fname is None or os.path.isdir(fname):
            return False
        if not fname.lower().endswith(".csv"):
            fname += ".csv"
        if self.saveStaplesDialog != None:
            self.saveStaplesDialog.filesSelected.disconnect(
                self.exportStaplesCallback)
            # manual garbage collection to prevent hang (in osx)
            del self.saveStaplesDialog
            self.saveStaplesDialog = None
        # write the file
        output = self.activePart().getStapleSequences()
        with open(fname, 'w') as f:
            f.write(output)

    # end def

    def newClickedCallback(self):
        """
        Gets called on completion of filesavedialog after newClicked's 
        maybeSave. Removes the dialog if necessary, but it was probably
        already removed by saveFileDialogCallback.
        """

        if self.filesavedialog != None:
            self.filesavedialog.finished.disconnect(self.newClickedCallback)
            del self.filesavedialog  # prevents hang (?)
            self.filesavedialog = None
        self.newDocument()

    def openAfterMaybeSaveCallback(self, selected):
        """
        Receives file selection info from the dialog created by
        openAfterMaybeSave, following user input.

        Extracts the file name and passes it to the decode method, which
        returns a new document doc, which is then set as the open document
        by newDocument. Calls finalizeImport and disconnects dialog signaling.
        """
        if isinstance(selected, (list, tuple)):
            fname = selected[0]
        else:
            fname = selected
        if fname is None or fname == '' or os.path.isdir(fname):
            return False
        if not os.path.exists(fname):
            return False
        self._writeFileOpenPath(os.path.dirname(fname))

        self.win.path_graphics_view.setViewportUpdateOn(False)
        self.win.slice_graphics_view.setViewportUpdateOn(False)

        self.newDocument(fname=fname)
        decodeFile(fname, document=self._document)

        self.win.path_graphics_view.setViewportUpdateOn(True)
        self.win.slice_graphics_view.setViewportUpdateOn(True)

        self.win.path_graphics_view.update()
        self.win.slice_graphics_view.update()

        if hasattr(self, "filesavedialog"):  # user did save
            if self.fileopendialog != None:
                self.fileopendialog.filesSelected.disconnect(\
                                              self.openAfterMaybeSaveCallback)
            # manual garbage collection to prevent hang (in osx)
            del self.fileopendialog
            self.fileopendialog = None

    def saveFileDialogCallback(self, selected):
        """If the user chose to save, write to that file."""
        if isinstance(selected, (list, tuple)):
            fname = selected[0]
        else:
            fname = selected
        if fname is None or os.path.isdir(fname):
            return False
        if not fname.lower().endswith(".json"):
            fname += ".json"
        if self.filesavedialog != None:
            self.filesavedialog.filesSelected.disconnect(
                self.saveFileDialogCallback)
            del self.filesavedialog  # prevents hang
            self.filesavedialog = None
        self.writeDocumentToFile(fname)
        self._writeFileOpenPath(os.path.dirname(fname))

    ### EVENT HANDLERS ###
    def windowCloseEventHandler(self, event):
        """Intercept close events when user attempts to close the window."""
        if self.maybeSave():
            event.accept()
            # if app().isInMaya():
            #     self.windock.setVisible(False)
            #     del self.windock
            #     self.windock = action_new_honeycomb_part
            dcs = app().document_controllers
            if self in dcs:
                dcs.remove(self)
        else:
            event.ignore()
        self.actionCloseSlot()

    ### FILE INPUT ##
    def documentTitle(self):
        fname = os.path.basename(str(self.filename()))
        if not self.undoStack().isClean():
            fname += '[*]'
        return fname

    def filename(self):
        return self._filename

    def setFilename(self, proposed_fname):
        if self._filename == proposed_fname:
            return True
        self._filename = proposed_fname
        self._has_no_associated_file = False
        self.win.setWindowTitle(self.documentTitle())
        return True

    def openAfterMaybeSave(self):
        """
        This is the method that initiates file opening. It is called by
        actionOpenSlot to spawn a QFileDialog and connect it to a callback
        method.
        """
        path = self._file_open_path
        if util.isWindows():  # required for native looking file window#"/",
            fname = QFileDialog.getOpenFileName(
                None, "Open Document", path,
                "cadnano1 / cadnano2 Files (*.nno *.json *.cadnano)")
            self.filesavedialog = None
            self.openAfterMaybeSaveCallback(fname)
        else:  # access through non-blocking callback
            fdialog = QFileDialog(
                self.win, "Open Document", path,
                "cadnano1 / cadnano2 Files (*.nno *.json *.cadnano)")
            fdialog.setAcceptMode(QFileDialog.AcceptOpen)
            fdialog.setWindowFlags(Qt.Sheet)
            fdialog.setWindowModality(Qt.WindowModal)
            self.fileopendialog = fdialog
            self.fileopendialog.filesSelected.connect(
                self.openAfterMaybeSaveCallback)
            fdialog.open()

    # end def

    ### FILE OUTPUT ###
    def maybeSave(self):
        """Save on quit, check if document changes have occured."""
        if app().dontAskAndJustDiscardUnsavedChanges:
            return True
        if not self.undoStack().isClean():  # document dirty?
            savebox = QMessageBox(
                QMessageBox.Warning, "Application",
                "The document has been modified.\nDo you want to save your changes?",
                QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel,
                self.win,
                Qt.Dialog | Qt.MSWindowsFixedSizeDialogHint | Qt.Sheet)
            savebox.setWindowModality(Qt.WindowModal)
            save = savebox.button(QMessageBox.Save)
            discard = savebox.button(QMessageBox.Discard)
            cancel = savebox.button(QMessageBox.Cancel)
            save.setShortcut("Ctrl+S")
            discard.setShortcut(QKeySequence("D,Ctrl+D"))
            cancel.setShortcut(QKeySequence("C,Ctrl+C,.,Ctrl+."))
            ret = savebox.exec_()
            del savebox  # manual garbage collection to prevent hang (in osx)
            if ret == QMessageBox.Save:
                return self.actionSaveAsSlot()
            elif ret == QMessageBox.Cancel:
                return False
        return True

    def writeDocumentToFile(self, filename=None):
        if filename == None or filename == '':
            if self._has_no_associated_file:
                return False
            filename = self.filename()
        try:
            with open(filename, 'w') as f:
                helix_order_list = self.win.pathroot.getSelectedPartOrderedVHList(
                )
                encode(self._document, helix_order_list, f)
        except IOError:
            flags = Qt.Dialog | Qt.MSWindowsFixedSizeDialogHint | Qt.Sheet
            errorbox = QMessageBox(QMessageBox.Critical, "cadnano",
                                   "Could not write to '%s'." % filename,
                                   QMessageBox.Ok, self.win, flags)
            errorbox.setWindowModality(Qt.WindowModal)
            errorbox.open()
            return False
        self.undoStack().setClean()
        self.setFilename(filename)
        return True

    def actionCadnanoWebsiteSlot(self):
        import webbrowser
        webbrowser.open("http://cadnano.org/")

    def actionFeedbackSlot(self):
        import webbrowser
        webbrowser.open("http://cadnano.org/feedback")
 def load_ui_settings(self, q_path: str) -> None:
     settings = QSettings()
     settings.beginGroup("%s/%s/%s" % (q_path, self.name, 'ui'))
     for ui_label in settings.childKeys():
         self.ui[ui_label] = settings.value(ui_label)
示例#38
0
 def saveSettings(self):
     s= QSettings()
     s.beginGroup("helper_applications")
     for name, title in self.items():
         s.setValue(name, self.entries[name].path())
示例#39
0
 def saveSettings(self):
     s= QSettings()
     s.beginGroup("helper_applications")
     s.setValue("printcommand", self.printCommand.path())
     s.setValue("printcommand/dialog", self.printDialogCheck.isChecked())
     s.setValue("printcommand/dpi", int(self.resolution.currentText()))