class MontageDialog(QDialog): def __init__(self, parent, montages, selected=None): super().__init__(parent) self.setWindowTitle("Set montage") vbox = QVBoxLayout(self) self.montages = QListWidget() self.montages.insertItems(0, montages) self.montages.setSelectionMode(QListWidget.SingleSelection) if selected is not None: for i in range(self.montages.count()): if self.montages.item(i).data(0) == selected: self.montages.item(i).setSelected(True) vbox.addWidget(self.montages) hbox = QHBoxLayout() self.view_button = QPushButton("View") self.view_button.clicked.connect(self.view_montage) hbox.addWidget(self.view_button) hbox.addStretch() self.buttonbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) hbox.addWidget(self.buttonbox) vbox.addLayout(hbox) self.buttonbox.accepted.connect(self.accept) self.buttonbox.rejected.connect(self.reject) self.montages.itemSelectionChanged.connect(self.toggle_buttons) self.toggle_buttons() # initialize OK and View buttons state @Slot() def toggle_buttons(self): """Toggle OK and View buttons. """ if self.montages.selectedItems(): self.buttonbox.button(QDialogButtonBox.Ok).setEnabled(True) self.view_button.setEnabled(True) else: self.buttonbox.button(QDialogButtonBox.Ok).setEnabled(False) self.view_button.setEnabled(False) def view_montage(self): name = self.montages.selectedItems()[0].data(0) montage = make_standard_montage(name) fig = montage.plot(show_names=True, show=False) win = fig.canvas.manager.window win.setWindowModality(Qt.WindowModal) win.setWindowTitle("Montage") win.findChild(QStatusBar).hide() win.findChild(QToolBar).hide() fig.show()
def __init__(self, parent=None): QDialog.__init__(self, parent) self.main = parent # Widgets self.pages_widget = QStackedWidget() self.pages_widget.setMinimumWidth(600) self.contents_widget = QListWidget() self.button_reset = QPushButton(_('Reset to defaults')) bbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Apply | QDialogButtonBox.Cancel) self.apply_btn = bbox.button(QDialogButtonBox.Apply) # Widgets setup # Destroying the C++ object right after closing the dialog box, # otherwise it may be garbage-collected in another QThread # (e.g. the editor's analysis thread in Spyder), thus leading to # a segmentation fault on UNIX or an application crash on Windows self.setAttribute(Qt.WA_DeleteOnClose) self.setWindowFlags(Qt.Dialog | Qt.WindowStaysOnTopHint) self.setWindowTitle(_('Preferences')) self.setWindowIcon(ima.icon('configure')) self.contents_widget.setMovement(QListView.Static) self.contents_widget.setSpacing(1) self.contents_widget.setCurrentRow(0) self.contents_widget.setMinimumWidth(220) self.contents_widget.setMinimumHeight(400) # Layout hsplitter = QSplitter() hsplitter.addWidget(self.contents_widget) hsplitter.addWidget(self.pages_widget) hsplitter.setStretchFactor(0, 1) hsplitter.setStretchFactor(1, 2) btnlayout = QHBoxLayout() btnlayout.addWidget(self.button_reset) btnlayout.addStretch(1) btnlayout.addWidget(bbox) vlayout = QVBoxLayout() vlayout.addWidget(hsplitter) vlayout.addLayout(btnlayout) self.setLayout(vlayout) # Signals and slots if self.main: self.button_reset.clicked.connect(self.main.reset_spyder) self.pages_widget.currentChanged.connect(self.current_page_changed) self.contents_widget.currentRowChanged.connect( self.pages_widget.setCurrentIndex) bbox.accepted.connect(self.accept) bbox.rejected.connect(self.reject) bbox.clicked.connect(self.button_clicked) # Ensures that the config is present on spyder first run CONF.set('main', 'interface_language', load_lang_conf())
class NewPBDialog(TimerDialog): """ Dialog showing a message congratulating the user on a new PB. The dialog has an 'Ok' button, but will also timeout after a few milliseconds. Parameters ---------- timeout : int Number of milliseconds for the dialog to be shown. Default is 3000. """ def __init__(self, timeout=3000): super().__init__(timeout=timeout) self.label = QLabel() font = self.label.font() font.setPointSize(14) self.label.setFont(font) self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok) self.okButton = self.buttonBox.button(QDialogButtonBox.Ok) self.okButton.clicked.connect(self.accept) self.layout = QVBoxLayout() self.layout.addWidget(self.label) self.layout.addWidget(self.buttonBox) self.setLayout(self.layout) self.setWindowTitle("New personal best") def setMessage(self, text): self.label.setText(text)
def __init__(self, parent=None): QDialog.__init__(self, parent) self.main = parent # Widgets self.pages_widget = QStackedWidget() self.pages_widget.setMinimumWidth(600) self.contents_widget = QListWidget() self.button_reset = QPushButton(_('Reset to defaults')) bbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Apply | QDialogButtonBox.Cancel) self.apply_btn = bbox.button(QDialogButtonBox.Apply) # Widgets setup # Destroying the C++ object right after closing the dialog box, # otherwise it may be garbage-collected in another QThread # (e.g. the editor's analysis thread in Spyder), thus leading to # a segmentation fault on UNIX or an application crash on Windows self.setAttribute(Qt.WA_DeleteOnClose) self.setWindowTitle(_('Preferences')) self.setWindowIcon(ima.icon('configure')) self.contents_widget.setMovement(QListView.Static) self.contents_widget.setSpacing(1) self.contents_widget.setCurrentRow(0) self.contents_widget.setMinimumWidth(220) self.contents_widget.setMinimumHeight(400) # Layout hsplitter = QSplitter() hsplitter.addWidget(self.contents_widget) hsplitter.addWidget(self.pages_widget) hsplitter.setStretchFactor(0, 1) hsplitter.setStretchFactor(1, 2) btnlayout = QHBoxLayout() btnlayout.addWidget(self.button_reset) btnlayout.addStretch(1) btnlayout.addWidget(bbox) vlayout = QVBoxLayout() vlayout.addWidget(hsplitter) vlayout.addLayout(btnlayout) self.setLayout(vlayout) # Signals and slots if self.main: self.button_reset.clicked.connect(self.main.reset_spyder) self.pages_widget.currentChanged.connect(self.current_page_changed) self.contents_widget.currentRowChanged.connect( self.pages_widget.setCurrentIndex) bbox.accepted.connect(self.accept) bbox.rejected.connect(self.reject) bbox.clicked.connect(self.button_clicked) # Ensures that the config is present on spyder first run CONF.set('main', 'interface_language', load_lang_conf())
def __init__(self, parent: MainWindowBase): super(_ScaleDialog, self).__init__(parent) self.setWindowTitle("Scale Mechanism") self.main_layout = QVBoxLayout(self) self.enlarge = QDoubleSpinBox(self) self.shrink = QDoubleSpinBox(self) self.__add_option("Enlarge", self.enlarge) self.__add_option("Shrink", self.shrink) button_box = QDialogButtonBox(self) button_box.setStandardButtons(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) button_box.accepted.connect(self.accept) button_box.rejected.connect(self.reject) button_box.button(QDialogButtonBox.Ok).setEnabled( bool(parent.vpoint_list)) self.main_layout.addWidget(button_box)
def __init__(self, parent=None, objname=None): QDialog.__init__(self, parent) # If used for data object in tree, the main is the tree widget. self.parent = parent self.objname = objname # Widgets self.pages_widget = QStackedWidget() self.contents_widget = QListWidget() self.button_reset = QPushButton(_('Reset to defaults')) bbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Apply | QDialogButtonBox.Cancel) self.apply_btn = bbox.button(QDialogButtonBox.Apply) # Widgets setup # Destroying the C++ object right after closing the dialog box, # otherwise it may be garbage-collected in another QThread # (e.g. the editor's analysis thread in Ezcad), thus leading to # a segmentation fault on UNIX or an application crash on Windows self.setAttribute(Qt.WA_DeleteOnClose) if self.objname is None: self.setWindowTitle(_('Preferences')) else: self.setWindowTitle(_('Preferences of ') + self.objname) self.setWindowIcon(ima.icon('configure')) self.contents_widget.setMovement(QListView.Static) self.contents_widget.setSpacing(1) self.contents_widget.setCurrentRow(0) # Layout hsplitter = QSplitter() hsplitter.addWidget(self.contents_widget) hsplitter.addWidget(self.pages_widget) hsplitter.setSizes([150,500]) btnlayout = QHBoxLayout() btnlayout.addWidget(self.button_reset) btnlayout.addStretch(1) btnlayout.addWidget(bbox) vlayout = QVBoxLayout() vlayout.addWidget(hsplitter) vlayout.addLayout(btnlayout) self.setLayout(vlayout) # Signals and slots self.pages_widget.currentChanged.connect(self.current_page_changed) self.contents_widget.currentRowChanged.connect( self.pages_widget.setCurrentIndex) bbox.accepted.connect(self.accept) bbox.rejected.connect(self.reject) bbox.clicked.connect(self.button_clicked) # Ensures that the config is present on ezcad first run CONF.set('main', 'interface_language', load_lang_conf())
def __init__( self, title="Title", description="Description", unique_names=None, choose_from_list=False, ): QDialog.__init__(self) self.setModal(True) self.setWindowTitle(title) # self.setMinimumWidth(250) # self.setMinimumHeight(150) if unique_names is None: unique_names = [] self.unique_names = unique_names self.choose_from_list = choose_from_list self.layout = QFormLayout() self.layout.setSizeConstraint(QLayout.SetFixedSize) label = QLabel(description) label.setAlignment(Qt.AlignHCenter) self.layout.addRow(self.createSpace(5)) self.layout.addRow(label) self.layout.addRow(self.createSpace(10)) buttons = QDialogButtonBox( QDialogButtonBox.Ok | QDialogButtonBox.Cancel, Qt.Horizontal, self) self.ok_button = buttons.button(QDialogButtonBox.Ok) self.ok_button.setEnabled(False) if choose_from_list: self.param_name_combo = QComboBox() self.param_name.currentIndexChanged.connect(self.validateChoice) for item in unique_names: self.param_name_combo.addItem(item) self.layout.addRow("Job:", self.param_name_combo) else: self.param_name = QLineEdit(self) self.param_name.setFocus() self.param_name.textChanged.connect(self.validateName) self.validColor = self.param_name.palette().color( self.param_name.backgroundRole()) self.layout.addRow("Name:", self.param_name) self.layout.addRow(self.createSpace(10)) self.layout.addRow(buttons) buttons.accepted.connect(self.accept) buttons.rejected.connect(self.reject) self.setLayout(self.layout)
class LayoutSaveDialog(QDialog): """ """ def __init__(self, parent, order): super(LayoutSaveDialog, self).__init__(parent) # variables self._parent = parent # widgets self.combo_box = QComboBox(self) self.combo_box.addItems(order) self.combo_box.setEditable(True) self.combo_box.clearEditText() self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel, Qt.Horizontal, self) self.button_ok = self.button_box.button(QDialogButtonBox.Ok) self.button_cancel = self.button_box.button(QDialogButtonBox.Cancel) # widget setup self.button_ok.setEnabled(False) self.dialog_size = QSize(300, 100) self.setWindowTitle('Save layout as') self.setModal(True) self.setMinimumSize(self.dialog_size) self.setFixedSize(self.dialog_size) # layouts self.layout = QVBoxLayout() self.layout.addWidget(self.combo_box) self.layout.addWidget(self.button_box) self.setLayout(self.layout) # signals and slots self.button_box.accepted.connect(self.accept) self.button_box.rejected.connect(self.close) self.combo_box.editTextChanged.connect(self.check_text) def check_text(self, text): """Disable empty layout name possibility""" if to_text_string(text) == u'': self.button_ok.setEnabled(False) else: self.button_ok.setEnabled(True)
def __init__(self, configuration_path, parent=None): QDialog.__init__(self, parent) self.setModal(True) self.setWindowTitle("New configuration file") self.setMinimumWidth(250) self.setMinimumHeight(150) layout = QFormLayout() directory, filename = os.path.split(configuration_path) if directory.strip() == "": directory = os.path.abspath(os.curdir) self.configuration_path = "%s/%s" % (directory, filename) else: self.configuration_path = configuration_path configuration_location = QLabel() configuration_location.setText(directory) configuration_name = QLabel() configuration_name.setText(filename) self.db_type = QComboBox() self.db_type.addItem("BLOCK_FS") self.db_type.addItem("PLAIN") self.num_realizations = QSpinBox() self.num_realizations.setMinimum(1) self.num_realizations.setMaximum(1000) self.num_realizations.setValue(10) self.storage_path = QLineEdit() self.storage_path.setText("Storage") self.storage_path.textChanged.connect(self._validateName) layout.addRow(createSpace(10)) layout.addRow("Configuration name:", configuration_name) layout.addRow("Configuration location:", configuration_location) layout.addRow("Path to store DBase:", self.storage_path) layout.addRow("DBase type:", self.db_type) layout.addRow("Number of realizations", self.num_realizations) layout.addRow(createSpace(10)) buttons = QDialogButtonBox( QDialogButtonBox.Ok | QDialogButtonBox.Cancel, Qt.Horizontal, self) self.ok_button = buttons.button(QDialogButtonBox.Ok) layout.addRow(buttons) buttons.accepted.connect(self.accept) buttons.rejected.connect(self.reject) self.setLayout(layout)
def __init__(self, editor): QDialog.__init__(self, editor, Qt.WindowTitleHint | Qt.WindowCloseButtonHint) # Destroying the C++ object right after closing the dialog box, # otherwise it may be garbage-collected in another QThread # (e.g. the editor's analysis thread in Spyder), thus leading to # a segmentation fault on UNIX or an application crash on Windows self.setAttribute(Qt.WA_DeleteOnClose) self.lineno = None self.editor = editor self.setWindowTitle(_("Editor")) self.setModal(True) label = QLabel(_("Go to line:")) self.lineedit = QLineEdit() validator = QIntValidator(self.lineedit) validator.setRange(1, editor.get_line_count()) self.lineedit.setValidator(validator) self.lineedit.textChanged.connect(self.text_has_changed) cl_label = QLabel(_("Current line:")) cl_label_v = QLabel("<b>%d</b>" % editor.get_cursor_line_number()) last_label = QLabel(_("Line count:")) last_label_v = QLabel("%d" % editor.get_line_count()) glayout = QGridLayout() glayout.addWidget(label, 0, 0, Qt.AlignVCenter | Qt.AlignRight) glayout.addWidget(self.lineedit, 0, 1, Qt.AlignVCenter) glayout.addWidget(cl_label, 1, 0, Qt.AlignVCenter | Qt.AlignRight) glayout.addWidget(cl_label_v, 1, 1, Qt.AlignVCenter) glayout.addWidget(last_label, 2, 0, Qt.AlignVCenter | Qt.AlignRight) glayout.addWidget(last_label_v, 2, 1, Qt.AlignVCenter) bbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel, Qt.Vertical, self) bbox.accepted.connect(self.accept) bbox.rejected.connect(self.reject) btnlayout = QVBoxLayout() btnlayout.addWidget(bbox) btnlayout.addStretch(1) ok_button = bbox.button(QDialogButtonBox.Ok) ok_button.setEnabled(False) self.lineedit.textChanged.connect( lambda text: ok_button.setEnabled(len(text) > 0)) layout = QHBoxLayout() layout.addLayout(glayout) layout.addLayout(btnlayout) self.setLayout(layout) self.lineedit.setFocus()
def addButtons(self): buttons = QDialogButtonBox( QDialogButtonBox.Ok | QDialogButtonBox.Cancel, Qt.Horizontal, self) self.ok_button = buttons.button(QDialogButtonBox.Ok) self.ok_button.setEnabled(False) self.layout.addRow(self.createSpace(10)) self.layout.addRow(buttons) buttons.accepted.connect(self.accept) buttons.rejected.connect(self.reject)
def install_button_layout(self): bbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) bbox.accepted.connect(self.accept) bbox.rejected.connect(self.reject) self.button_list += [bbox.button(QDialogButtonBox.Ok)] self.button_layout = QHBoxLayout() self.button_layout.addStretch() self.button_layout.addWidget(bbox) vlayout = self.layout() vlayout.addSpacing(10) vlayout.addLayout(self.button_layout)
def __init__( self, title="Title", description="Description", unique_names=None, ): QDialog.__init__(self) self.setModal(True) self.setWindowTitle(title) if unique_names is None: unique_names = [] self.unique_names = unique_names self.layout = QFormLayout() self.layout.setSizeConstraint(QLayout.SetFixedSize) label = QLabel(description) label.setAlignment(Qt.AlignHCenter) self.layout.addRow(self.createSpace(5)) self.layout.addRow(label) self.layout.addRow(self.createSpace(10)) buttons = QDialogButtonBox( QDialogButtonBox.Ok | QDialogButtonBox.Cancel, Qt.Horizontal, self ) self.ok_button = buttons.button(QDialogButtonBox.Ok) self.ok_button.setEnabled(False) self.param_name = QLineEdit(self) self.param_name.setFocus() self.param_name.textChanged.connect(self.validateName) self.validColor = self.param_name.palette().color( self.param_name.backgroundRole() ) self.layout.addRow("Name:", self.param_name) self.layout.addRow(self.createSpace(10)) self.layout.addRow(buttons) buttons.accepted.connect(self.accept) buttons.rejected.connect(self.reject) self.setLayout(self.layout)
class LSPServerEditor(QDialog): DEFAULT_HOST = '127.0.0.1' DEFAULT_PORT = 2084 DEFAULT_CMD = '' DEFAULT_ARGS = '' DEFAULT_CONFIGURATION = '{}' DEFAULT_EXTERNAL = False DEFAULT_STDIO = False HOST_REGEX = re.compile(r'^\w+([.]\w+)*$') NON_EMPTY_REGEX = re.compile(r'^\S+$') JSON_VALID = _('Valid JSON') JSON_INVALID = _('Invalid JSON') MIN_SIZE = QSize(850, 600) INVALID_CSS = "QLineEdit {border: 1px solid red;}" VALID_CSS = "QLineEdit {border: 1px solid green;}" def __init__(self, parent, language=None, cmd='', host='127.0.0.1', port=2084, args='', external=False, stdio=False, configurations={}, **kwargs): super(LSPServerEditor, self).__init__(parent) description = _( "To create a new server configuration, you need to select a " "programming language, set the command to start its associated " "server and enter any arguments that should be passed to it on " "startup. Additionally, you can set the server's hostname and " "port if connecting to an external server, " "or to a local one using TCP instead of stdio pipes." "<br><br>" "<i>Note</i>: You can use the placeholders <tt>{host}</tt> and " "<tt>{port}</tt> in the server arguments field to automatically " "fill in the respective values.<br>" ) self.parent = parent self.external = external # Widgets self.server_settings_description = QLabel(description) self.lang_cb = QComboBox(self) self.external_cb = QCheckBox(_('External server'), self) self.host_label = QLabel(_('Host:')) self.host_input = QLineEdit(self) self.port_label = QLabel(_('Port:')) self.port_spinner = QSpinBox(self) self.cmd_label = QLabel(_('Command:')) self.cmd_input = QLineEdit(self) self.args_label = QLabel(_('Arguments:')) self.args_input = QLineEdit(self) self.json_label = QLabel(self.JSON_VALID, self) self.conf_label = QLabel(_('<b>Server Configuration:</b>')) self.conf_input = CodeEditor(None) self.bbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.button_ok = self.bbox.button(QDialogButtonBox.Ok) self.button_cancel = self.bbox.button(QDialogButtonBox.Cancel) # Widget setup self.setMinimumSize(self.MIN_SIZE) self.setWindowTitle(_('LSP server editor')) self.server_settings_description.setWordWrap(True) self.lang_cb.setToolTip( _('Programming language provided by the LSP server')) self.lang_cb.addItem(_('Select a language')) self.lang_cb.addItems(LSP_LANGUAGES) self.button_ok.setEnabled(False) if language is not None: idx = LSP_LANGUAGES.index(language) self.lang_cb.setCurrentIndex(idx + 1) self.button_ok.setEnabled(True) self.host_input.setPlaceholderText('127.0.0.1') self.host_input.setText(host) self.host_input.textChanged.connect(lambda x: self.validate()) self.port_spinner.setToolTip(_('TCP port number of the server')) self.port_spinner.setMinimum(1) self.port_spinner.setMaximum(60000) self.port_spinner.setValue(port) self.cmd_input.setText(cmd) self.cmd_input.setPlaceholderText('/absolute/path/to/command') self.args_input.setToolTip( _('Additional arguments required to start the server')) self.args_input.setText(args) self.args_input.setPlaceholderText(r'--host {host} --port {port}') self.conf_input.setup_editor( language='json', color_scheme=CONF.get('appearance', 'selected'), wrap=False, edge_line=True, highlight_current_line=True, highlight_current_cell=True, occurrence_highlighting=True, auto_unindent=True, font=get_font(), filename='config.json', folding=False ) self.conf_input.set_language('json', 'config.json') self.conf_input.setToolTip(_('Additional LSP server configuration ' 'set at runtime. JSON required')) try: conf_text = json.dumps(configurations, indent=4, sort_keys=True) except Exception: conf_text = '{}' self.conf_input.set_text(conf_text) self.external_cb.setToolTip( _('Check if the server runs on a remote location')) self.external_cb.setChecked(external) self.stdio_cb = QCheckBox(_('Use stdio pipes for communication'), self) self.stdio_cb.setToolTip(_('Check if the server communicates ' 'using stdin/out pipes')) self.stdio_cb.setChecked(stdio) # Layout setup hlayout = QHBoxLayout() general_vlayout = QVBoxLayout() general_vlayout.addWidget(self.server_settings_description) vlayout = QVBoxLayout() lang_group = QGroupBox(_('Language')) lang_layout = QVBoxLayout() lang_layout.addWidget(self.lang_cb) lang_group.setLayout(lang_layout) vlayout.addWidget(lang_group) server_group = QGroupBox(_('Language server')) server_layout = QGridLayout() server_layout.addWidget(self.cmd_label, 0, 0) server_layout.addWidget(self.cmd_input, 0, 1) server_layout.addWidget(self.args_label, 1, 0) server_layout.addWidget(self.args_input, 1, 1) server_group.setLayout(server_layout) vlayout.addWidget(server_group) address_group = QGroupBox(_('Server address')) host_layout = QVBoxLayout() host_layout.addWidget(self.host_label) host_layout.addWidget(self.host_input) port_layout = QVBoxLayout() port_layout.addWidget(self.port_label) port_layout.addWidget(self.port_spinner) conn_info_layout = QHBoxLayout() conn_info_layout.addLayout(host_layout) conn_info_layout.addLayout(port_layout) address_group.setLayout(conn_info_layout) vlayout.addWidget(address_group) advanced_group = QGroupBox(_('Advanced')) advanced_layout = QVBoxLayout() advanced_layout.addWidget(self.external_cb) advanced_layout.addWidget(self.stdio_cb) advanced_group.setLayout(advanced_layout) vlayout.addWidget(advanced_group) conf_layout = QVBoxLayout() conf_layout.addWidget(self.conf_label) conf_layout.addWidget(self.conf_input) conf_layout.addWidget(self.json_label) vlayout.addStretch() hlayout.addLayout(vlayout, 2) hlayout.addLayout(conf_layout, 3) general_vlayout.addLayout(hlayout) general_vlayout.addWidget(self.bbox) self.setLayout(general_vlayout) self.form_status(False) # Signals if not external: self.cmd_input.textChanged.connect(lambda x: self.validate()) self.external_cb.stateChanged.connect(self.set_local_options) self.stdio_cb.stateChanged.connect(self.set_stdio_options) self.lang_cb.currentIndexChanged.connect(self.lang_selection_changed) self.conf_input.textChanged.connect(self.validate) self.bbox.accepted.connect(self.accept) self.bbox.rejected.connect(self.reject) # Final setup if language is not None: self.form_status(True) self.validate() if stdio: self.set_stdio_options(True) if external: self.set_local_options(True) @Slot() def validate(self): host_text = self.host_input.text() cmd_text = self.cmd_input.text() if not self.HOST_REGEX.match(host_text): self.button_ok.setEnabled(False) self.host_input.setStyleSheet(self.INVALID_CSS) if bool(host_text): self.host_input.setToolTip(_('Hostname must be valid')) else: self.host_input.setToolTip( _('Hostname or IP address of the host on which the server ' 'is running. Must be non empty.')) else: self.host_input.setStyleSheet(self.VALID_CSS) self.host_input.setToolTip(_('Hostname is valid')) self.button_ok.setEnabled(True) if not self.external: if not self.NON_EMPTY_REGEX.match(cmd_text): self.button_ok.setEnabled(False) self.cmd_input.setStyleSheet(self.INVALID_CSS) self.cmd_input.setToolTip( _('Command used to start the LSP server locally. Must be ' 'non empty')) return if find_program(cmd_text) is None: self.button_ok.setEnabled(False) self.cmd_input.setStyleSheet(self.INVALID_CSS) self.cmd_input.setToolTip(_('Program was not found ' 'on your system')) else: self.cmd_input.setStyleSheet(self.VALID_CSS) self.cmd_input.setToolTip(_('Program was found on your ' 'system')) self.button_ok.setEnabled(True) try: json.loads(self.conf_input.toPlainText()) try: self.json_label.setText(self.JSON_VALID) except Exception: pass except ValueError: try: self.json_label.setText(self.JSON_INVALID) self.button_ok.setEnabled(False) except Exception: pass def form_status(self, status): self.host_input.setEnabled(status) self.port_spinner.setEnabled(status) self.external_cb.setEnabled(status) self.stdio_cb.setEnabled(status) self.cmd_input.setEnabled(status) self.args_input.setEnabled(status) self.conf_input.setEnabled(status) self.json_label.setVisible(status) @Slot() def lang_selection_changed(self): idx = self.lang_cb.currentIndex() if idx == 0: self.set_defaults() self.form_status(False) self.button_ok.setEnabled(False) else: server = self.parent.get_server_by_lang(LSP_LANGUAGES[idx - 1]) self.form_status(True) if server is not None: self.host_input.setText(server.host) self.port_spinner.setValue(server.port) self.external_cb.setChecked(server.external) self.stdio_cb.setChecked(server.stdio) self.cmd_input.setText(server.cmd) self.args_input.setText(server.args) self.conf_input.set_text(json.dumps(server.configurations)) self.json_label.setText(self.JSON_VALID) self.button_ok.setEnabled(True) else: self.set_defaults() def set_defaults(self): self.cmd_input.setStyleSheet('') self.host_input.setStyleSheet('') self.host_input.setText(self.DEFAULT_HOST) self.port_spinner.setValue(self.DEFAULT_PORT) self.external_cb.setChecked(self.DEFAULT_EXTERNAL) self.stdio_cb.setChecked(self.DEFAULT_STDIO) self.cmd_input.setText(self.DEFAULT_CMD) self.args_input.setText(self.DEFAULT_ARGS) self.conf_input.set_text(self.DEFAULT_CONFIGURATION) self.json_label.setText(self.JSON_VALID) @Slot(bool) @Slot(int) def set_local_options(self, enabled): self.external = enabled self.cmd_input.setEnabled(True) self.args_input.setEnabled(True) if enabled: self.cmd_input.setEnabled(False) self.cmd_input.setStyleSheet('') self.args_input.setEnabled(False) self.stdio_cb.stateChanged.disconnect() self.stdio_cb.setChecked(False) self.stdio_cb.setEnabled(False) else: self.cmd_input.setEnabled(True) self.args_input.setEnabled(True) self.stdio_cb.setEnabled(True) self.stdio_cb.setChecked(False) self.stdio_cb.stateChanged.connect(self.set_stdio_options) try: self.validate() except Exception: pass @Slot(bool) @Slot(int) def set_stdio_options(self, enabled): self.stdio = enabled if enabled: self.cmd_input.setEnabled(True) self.args_input.setEnabled(True) self.external_cb.stateChanged.disconnect() self.external_cb.setChecked(False) self.external_cb.setEnabled(False) self.host_input.setStyleSheet('') self.host_input.setEnabled(False) self.port_spinner.setEnabled(False) else: self.cmd_input.setEnabled(True) self.args_input.setEnabled(True) self.external_cb.setChecked(False) self.external_cb.setEnabled(True) self.external_cb.stateChanged.connect(self.set_local_options) self.host_input.setEnabled(True) self.port_spinner.setEnabled(True) try: self.validate() except Exception: pass def get_options(self): language_idx = self.lang_cb.currentIndex() language = LSP_LANGUAGES[language_idx - 1] host = self.host_input.text() port = int(self.port_spinner.value()) external = self.external_cb.isChecked() stdio = self.stdio_cb.isChecked() args = self.args_input.text() cmd = self.cmd_input.text() configurations = json.loads(self.conf_input.toPlainText()) server = LSPServer(language=language.lower(), cmd=cmd, args=args, host=host, port=port, external=external, stdio=stdio, configurations=configurations) return server
def __init__(self, parent, context, name, sequence, shortcuts): super(ShortcutEditor, self).__init__(parent) self._parent = parent self.context = context self.npressed = 0 self.keys = set() self.key_modifiers = set() self.key_non_modifiers = list() self.key_text = list() self.sequence = sequence self.new_sequence = None self.edit_state = True self.shortcuts = shortcuts # Widgets self.label_info = QLabel() self.label_info.setText(_("Press the new shortcut and select 'Ok': \n" "(Press 'Tab' once to switch focus between the shortcut entry \n" "and the buttons below it)")) self.label_current_sequence = QLabel(_("Current shortcut:")) self.text_current_sequence = QLabel(sequence) self.label_new_sequence = QLabel(_("New shortcut:")) self.text_new_sequence = CustomLineEdit(self) self.text_new_sequence.setPlaceholderText(sequence) self.helper_button = HelperToolButton() self.helper_button.hide() self.label_warning = QLabel() self.label_warning.hide() bbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.button_ok = bbox.button(QDialogButtonBox.Ok) self.button_cancel = bbox.button(QDialogButtonBox.Cancel) # Setup widgets self.setWindowTitle(_('Shortcut: {0}').format(name)) self.button_ok.setFocusPolicy(Qt.NoFocus) self.button_ok.setEnabled(False) self.button_cancel.setFocusPolicy(Qt.NoFocus) self.helper_button.setToolTip('') self.helper_button.setFocusPolicy(Qt.NoFocus) style = """ QToolButton { margin:1px; border: 0px solid grey; padding:0px; border-radius: 0px; }""" self.helper_button.setStyleSheet(style) self.text_new_sequence.setFocusPolicy(Qt.NoFocus) self.label_warning.setFocusPolicy(Qt.NoFocus) # Layout spacing = 5 layout_sequence = QGridLayout() layout_sequence.addWidget(self.label_info, 0, 0, 1, 3) layout_sequence.addItem(QSpacerItem(spacing, spacing), 1, 0, 1, 2) layout_sequence.addWidget(self.label_current_sequence, 2, 0) layout_sequence.addWidget(self.text_current_sequence, 2, 2) layout_sequence.addWidget(self.label_new_sequence, 3, 0) layout_sequence.addWidget(self.helper_button, 3, 1) layout_sequence.addWidget(self.text_new_sequence, 3, 2) layout_sequence.addWidget(self.label_warning, 4, 2, 1, 2) layout = QVBoxLayout() layout.addLayout(layout_sequence) layout.addSpacing(spacing) layout.addWidget(bbox) self.setLayout(layout) # Signals bbox.accepted.connect(self.accept) bbox.rejected.connect(self.reject)
class InputTextDialog(QDialog): """Input text dialog with regex validation.""" def __init__(self, parent=None, title='', label=''): """Input text dialog with regex validation.""" super(InputTextDialog, self).__init__(parent=parent) self._reg = None self._regex = None # Widgets self.label = QLabel() self.lineedit = QLineEdit() self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.button_ok = self.button_box.button(QDialogButtonBox.Ok) self.button_cancel = self.button_box.button(QDialogButtonBox.Cancel) # Widget setup self.setWindowTitle(title) self.setMinimumWidth(500) # FIXME: use metrics self.label.setText(label) # Layout layout = QVBoxLayout() layout.addWidget(self.label) layout.addWidget(self.lineedit) layout.addSpacing(24) # FIXME: use metrics layout.addWidget(self.button_box) self.setLayout(layout) # Signals self.button_ok.clicked.connect(self.accept) self.button_cancel.clicked.connect(self.reject) self.lineedit.textChanged.connect(self.validate) self.validate() def validate(self): """Validate content.""" text = self.text().strip() is_valid = bool(text) if self._reg: res = self._reg.match(text) if res: text_matched = res.group(0) is_valid = is_valid and text_matched == text else: is_valid = False self.button_ok.setEnabled(is_valid) def set_regex_validation(self, regex): """Set the regular expression to validate content.""" self._regex = regex self._reg = re.compile(regex, re.IGNORECASE) validator = QRegExpValidator(QRegExp(regex)) self.lineedit.setValidator(validator) def text(self): """Return the text of the lineedit.""" return self.lineedit.text() def set_text(self, text): """Set the text of the lineedit.""" self.lineedit.setText(text) self.validate()
def __init__(self, parent, language=None, cmd='', host='127.0.0.1', port=2084, args='', external=False, configurations={}, **kwargs): super(LSPServerEditor, self).__init__(parent) self.parent = parent self.external = external bbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.button_ok = bbox.button(QDialogButtonBox.Ok) self.button_cancel = bbox.button(QDialogButtonBox.Cancel) self.button_ok.setEnabled(False) description = _('To create a new configuration, ' 'you need to select a programming ' 'language, along with a executable ' 'name for the server to execute ' '(If the instance is local), ' 'and the host and port. Finally, ' 'you need to provide the ' 'arguments that the server accepts. ' 'The placeholders <tt>%(host)s</tt> and ' '<tt>%(port)s</tt> refer to the host ' 'and the port, respectively.') server_settings_description = QLabel(description) server_settings_description.setWordWrap(True) lang_label = QLabel(_('Language:')) self.lang_cb = QComboBox(self) self.lang_cb.setToolTip(_('Programming language provided ' 'by the LSP server')) self.lang_cb.addItem(_('Select a language')) self.lang_cb.addItems(LSP_LANGUAGES) if language is not None: idx = LSP_LANGUAGES.index(language) self.lang_cb.setCurrentIndex(idx + 1) self.button_ok.setEnabled(True) host_label = QLabel(_('Host:')) self.host_input = QLineEdit(self) self.host_input.setToolTip(_('Name of the host that will provide ' 'access to the server')) self.host_input.setText(host) self.host_input.textChanged.connect(lambda x: self.validate()) port_label = QLabel(_('Port:')) self.port_spinner = QSpinBox(self) self.port_spinner.setToolTip(_('TCP port number of the server')) self.port_spinner.setMinimum(1) self.port_spinner.setMaximum(60000) self.port_spinner.setValue(port) cmd_label = QLabel(_('Command to execute:')) self.cmd_input = QLineEdit(self) self.cmd_input.setToolTip(_('Command used to start the ' 'LSP server locally')) self.cmd_input.setText(cmd) if not external: self.cmd_input.textChanged.connect(lambda x: self.validate()) args_label = QLabel(_('Server arguments:')) self.args_input = QLineEdit(self) self.args_input.setToolTip(_('Additional arguments required to ' 'start the server')) self.args_input.setText(args) conf_label = QLabel(_('LSP Server Configurations:')) self.conf_input = CodeEditor(None) self.conf_input.textChanged.connect(self.validate) color_scheme = CONF.get('color_schemes', 'selected') self.conf_input.setup_editor( language='JSON', color_scheme=color_scheme, wrap=False, edge_line=True, highlight_current_line=True, highlight_current_cell=True, occurrence_highlighting=True, auto_unindent=True, font=get_font(), filename='config.json') self.conf_input.setToolTip(_('Additional LSP server configurations ' 'set at runtime. JSON required')) conf_text = '{}' try: conf_text = json.dumps(configurations, indent=4, sort_keys=True) except Exception: pass self.conf_input.set_text(conf_text) self.json_label = QLabel(self.JSON_VALID, self) self.external_cb = QCheckBox(_('External server'), self) self.external_cb.setToolTip(_('Check if the server runs ' 'on a remote location')) self.external_cb.setChecked(external) self.external_cb.stateChanged.connect(self.set_local_options) hlayout = QHBoxLayout() general_vlayout = QVBoxLayout() general_vlayout.addWidget(server_settings_description) vlayout = QVBoxLayout() lang_layout = QVBoxLayout() lang_layout.addWidget(lang_label) lang_layout.addWidget(self.lang_cb) # layout2 = QHBoxLayout() # layout2.addLayout(lang_layout) lang_layout.addWidget(self.external_cb) vlayout.addLayout(lang_layout) host_layout = QVBoxLayout() host_layout.addWidget(host_label) host_layout.addWidget(self.host_input) port_layout = QVBoxLayout() port_layout.addWidget(port_label) port_layout.addWidget(self.port_spinner) conn_info_layout = QHBoxLayout() conn_info_layout.addLayout(host_layout) conn_info_layout.addLayout(port_layout) vlayout.addLayout(conn_info_layout) cmd_layout = QVBoxLayout() cmd_layout.addWidget(cmd_label) cmd_layout.addWidget(self.cmd_input) vlayout.addLayout(cmd_layout) args_layout = QVBoxLayout() args_layout.addWidget(args_label) args_layout.addWidget(self.args_input) vlayout.addLayout(args_layout) conf_layout = QVBoxLayout() conf_layout.addWidget(conf_label) conf_layout.addWidget(self.conf_input) conf_layout.addWidget(self.json_label) hlayout.addLayout(vlayout) hlayout.addLayout(conf_layout) general_vlayout.addLayout(hlayout) general_vlayout.addWidget(bbox) self.setLayout(general_vlayout) bbox.accepted.connect(self.accept) bbox.rejected.connect(self.reject) self.lang_cb.currentIndexChanged.connect( self.lang_selection_changed) self.form_status(False) if language is not None: self.form_status(True) self.validate()
class MergeDialog(QDialog): # name of src tab, names of dst tabs, whether to keep connections alive or not merge_tabs_signal = Signal(str, list, bool) def __init__(self, parent, loggers): super().__init__(parent) self.loggers = loggers self.merge_list = [] # all tabs to be merged self.merge_dst = None # tab to merge the rest of merge_list into self.setupUi() def setupUi(self): self.resize(340, 320) self.gridLayout = QGridLayout(self) self.dstComboBox = QComboBox(self) self.gridLayout.addWidget(self.dstComboBox, 1, 2, 1, 2) self.buttonBox = QDialogButtonBox(QDialogButtonBox.Cancel | QDialogButtonBox.Ok, self) self.gridLayout.addWidget(self.buttonBox, 5, 0, 1, 4) self.loggerList = QListWidget(self) self.loggerList.setDefaultDropAction(Qt.IgnoreAction) self.loggerList.setSelectionMode(QAbstractItemView.MultiSelection) self.gridLayout.addWidget(self.loggerList, 1, 0, 4, 2) self.keepAliveCheckBox = QCheckBox("Keep connections alive", self) self.keepAliveCheckBox.setChecked(True) self.gridLayout.addWidget(self.keepAliveCheckBox, 2, 2, 1, 2) self.srcsLabel = QLabel("All loggers:", self) self.gridLayout.addWidget(self.srcsLabel, 0, 0, 1, 2) self.dstLabel = QLabel("Merge all into:", self) self.gridLayout.addWidget(self.dstLabel, 0, 2, 1, 2) spacerItem = QSpacerItem(20, 169, QSizePolicy.Minimum, QSizePolicy.Expanding) self.gridLayout.addItem(spacerItem, 4, 2, 1, 2) self.buttonBox.accepted.connect(self.accept) self.buttonBox.rejected.connect(self.reject) self.loggerList.selectionModel().selectionChanged.connect(self.merge_list_changed) self.dstComboBox.currentTextChanged.connect(self.merge_dst_changed) self.ok_button = self.buttonBox.button(QDialogButtonBox.Ok) self.ok_button.setEnabled(False) self.keepAliveCheckBox.setToolTip("If disabled then only the destination connection " "will still be alive after merging.") self.fill_logger_list() def fill_logger_list(self): for logger_name in self.loggers.keys(): LoggerListItem(self.loggerList, logger_name) def merge_list_changed(self, sel, desel): sel = sel.indexes() desel = desel.indexes() for index in sel: sel_item = self.loggerList.itemFromIndex(index) self.merge_list.append(sel_item) self.dstComboBox.addItem(sel_item.name) self.ok_button.setEnabled(True) for index in desel: desel_item = self.loggerList.itemFromIndex(index) self.merge_list.remove(desel_item) row = self.dstComboBox.findText(desel_item.name) self.dstComboBox.removeItem(row) if self.dstComboBox.count() == 0: self.ok_button.setEnabled(False) def merge_dst_changed(self, text): self.merge_dst = text def accept(self): name_list = [item.name for item in self.merge_list] name_list.remove(self.merge_dst) self.merge_tabs_signal.emit(self.merge_dst, name_list, self.keepAliveCheckBox.isChecked()) self.done(0) def reject(self): self.done(0)
class ConfigDialog(QDialog): def __init__(self): super(ConfigDialog, self).__init__() # Set size and position self.setGeometry(0, 0, 900, 550) frameGm = self.frameGeometry() screen = QApplication.desktop().screenNumber(QApplication.desktop().cursor().pos()) centerPoint = QApplication.desktop().screenGeometry(screen).center() frameGm.moveCenter(centerPoint) self.move(frameGm.topLeft()) self.contentsWidget = QListView() self.contentsWidget.setViewMode(QListView.IconMode) # self.contentsWidget.setIconSize(QSize(96, 84)) self.contentsWidget.setMovement(QListView.Static) self.contentsWidget.setMaximumWidth(174) self.contentsWidget.setSpacing(12) self.contentsWidget.setSelectionMode(QAbstractItemView.SingleSelection) self.contentsModel = QStandardItemModel() self.contentsWidget.setModel(self.contentsModel) self.contentsWidget.selectionModel().currentChanged.connect(self.changePage) self.buttonboxWidget = QDialogButtonBox( QDialogButtonBox.Ok | QDialogButtonBox.Cancel | QDialogButtonBox.Apply# | QDialogButtonBox.Help ) self.buttonboxWidget.button(QDialogButtonBox.Ok).clicked.connect(self.ok) self.buttonboxWidget.button(QDialogButtonBox.Apply).clicked.connect(self.apply) self.buttonboxWidget.button(QDialogButtonBox.Cancel).clicked.connect(self.close) self.pagesWidget = QStackedWidget() horizontalLayout = QHBoxLayout() horizontalLayout.addWidget(self.contentsWidget) horizontalLayout.addWidget(self.pagesWidget, 1) mainLayout = QVBoxLayout() mainLayout.addLayout(horizontalLayout) # mainLayout.addStretch(1) mainLayout.addSpacing(12) mainLayout.addWidget(self.buttonboxWidget) self.setLayout(mainLayout) self.setWindowTitle("Config Dialog") # Set modality self.setModal(True) self.lastwidget = None self.createIcons() self.restore() pluginmanager.attach(self.pluginsChanged) def createIcons(self): self.contentsModel.clear() for pluginInfo in pluginmanager.getPluginsOfCategory("SettingsPlugin"): item = QStandardItem(pluginInfo.plugin_object.icon, pluginInfo.plugin_object.name()) item.widget = pluginInfo.plugin_object.widget item.setTextAlignment(Qt.AlignHCenter) item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) item.setSizeHint(QSize(136, 80)) self.contentsModel.appendRow(item) def show(self): if self.lastwidget: self.pagesWidget.addWidget(self.lastwidget) self.pagesWidget.setCurrentWidget(self.lastwidget) self.restore() super(ConfigDialog, self).show() def changePage(self, current, previous): if not current: current = previous current = self.contentsModel.itemFromIndex(current) self.pagesWidget.addWidget(current.widget) self.pagesWidget.setCurrentWidget(current.widget) self.lastwidget = current.widget def pluginsChanged(self): self.createIcons() def restore(self): for pluginInfo in pluginmanager.getPluginsOfCategory("SettingsPlugin"): pluginInfo.plugin_object.restore() self.apply() def ok(self): self._empty() self.apply() self.accept() def apply(self): for pluginInfo in pluginmanager.getPluginsOfCategory("SettingsPlugin"): pluginInfo.plugin_object.save() def close(self): self._empty() self.restore() self.reject() def _empty(self): """ Disown all widget children (otherwise their c++ objects are force deleted when the dialog closes). Must be run in reverse to avoid index update errors """ for i in reversed(range(self.pagesWidget.count())): self.pagesWidget.widget(i).setParent(None) def closeEvent(self, event): self.close() event.accept() def keyPressEvent(self, e: QKeyEvent): if e.key() != Qt.Key_Escape: super(ConfigDialog, self).keyPressEvent(e) else: self.close()
def __init__(self, parent, language=None, cmd='', host='127.0.0.1', port=2084, args='', external=False, configurations={}, **kwargs): super(LSPServerEditor, self).__init__(parent) self.parent = parent self.external = external bbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.button_ok = bbox.button(QDialogButtonBox.Ok) self.button_cancel = bbox.button(QDialogButtonBox.Cancel) self.button_ok.setEnabled(False) description = _('To create a new configuration, ' 'you need to select a programming ' 'language, along with a executable ' 'name for the server to execute ' '(If the instance is local), ' 'and the host and port. Finally, ' 'you need to provide the ' 'arguments that the server accepts. ' 'The placeholders <tt>%(host)s</tt> and ' '<tt>%(port)s</tt> refer to the host ' 'and the port, respectively.') server_settings_description = QLabel(description) server_settings_description.setWordWrap(True) lang_label = QLabel(_('Language:')) self.lang_cb = QComboBox(self) self.lang_cb.setToolTip(_('Programming language provided ' 'by the LSP server')) self.lang_cb.addItem(_('Select a language')) self.lang_cb.addItems(LSP_LANGUAGES) if language is not None: idx = LSP_LANGUAGES.index(language) self.lang_cb.setCurrentIndex(idx + 1) self.button_ok.setEnabled(True) host_label = QLabel(_('Host:')) self.host_input = QLineEdit(self) self.host_input.setToolTip(_('Name of the host that will provide ' 'access to the server')) self.host_input.setText(host) self.host_input.textChanged.connect(lambda x: self.validate()) port_label = QLabel(_('Port:')) self.port_spinner = QSpinBox(self) self.port_spinner.setToolTip(_('TCP port number of the server')) self.port_spinner.setMinimum(1) self.port_spinner.setMaximum(60000) self.port_spinner.setValue(port) cmd_label = QLabel(_('Command to execute:')) self.cmd_input = QLineEdit(self) self.cmd_input.setToolTip(_('Command used to start the ' 'LSP server locally')) self.cmd_input.setText(cmd) if not external: self.cmd_input.textChanged.connect(lambda x: self.validate()) args_label = QLabel(_('Server arguments:')) self.args_input = QLineEdit(self) self.args_input.setToolTip(_('Additional arguments required to ' 'start the server')) self.args_input.setText(args) conf_label = QLabel(_('LSP Server Configurations:')) self.conf_input = CodeEditor(None) self.conf_input.textChanged.connect(self.validate) color_scheme = CONF.get('appearance', 'selected') self.conf_input.setup_editor( language='JSON', color_scheme=color_scheme, wrap=False, edge_line=True, highlight_current_line=True, highlight_current_cell=True, occurrence_highlighting=True, auto_unindent=True, font=get_font(), filename='config.json') self.conf_input.setToolTip(_('Additional LSP server configurations ' 'set at runtime. JSON required')) conf_text = '{}' try: conf_text = json.dumps(configurations, indent=4, sort_keys=True) except Exception: pass self.conf_input.set_text(conf_text) self.json_label = QLabel(self.JSON_VALID, self) self.external_cb = QCheckBox(_('External server'), self) self.external_cb.setToolTip(_('Check if the server runs ' 'on a remote location')) self.external_cb.setChecked(external) self.external_cb.stateChanged.connect(self.set_local_options) hlayout = QHBoxLayout() general_vlayout = QVBoxLayout() general_vlayout.addWidget(server_settings_description) vlayout = QVBoxLayout() lang_layout = QVBoxLayout() lang_layout.addWidget(lang_label) lang_layout.addWidget(self.lang_cb) # layout2 = QHBoxLayout() # layout2.addLayout(lang_layout) lang_layout.addWidget(self.external_cb) vlayout.addLayout(lang_layout) host_layout = QVBoxLayout() host_layout.addWidget(host_label) host_layout.addWidget(self.host_input) port_layout = QVBoxLayout() port_layout.addWidget(port_label) port_layout.addWidget(self.port_spinner) conn_info_layout = QHBoxLayout() conn_info_layout.addLayout(host_layout) conn_info_layout.addLayout(port_layout) vlayout.addLayout(conn_info_layout) cmd_layout = QVBoxLayout() cmd_layout.addWidget(cmd_label) cmd_layout.addWidget(self.cmd_input) vlayout.addLayout(cmd_layout) args_layout = QVBoxLayout() args_layout.addWidget(args_label) args_layout.addWidget(self.args_input) vlayout.addLayout(args_layout) conf_layout = QVBoxLayout() conf_layout.addWidget(conf_label) conf_layout.addWidget(self.conf_input) conf_layout.addWidget(self.json_label) hlayout.addLayout(vlayout) hlayout.addLayout(conf_layout) general_vlayout.addLayout(hlayout) general_vlayout.addWidget(bbox) self.setLayout(general_vlayout) bbox.accepted.connect(self.accept) bbox.rejected.connect(self.reject) self.lang_cb.currentIndexChanged.connect( self.lang_selection_changed) self.form_status(False) if language is not None: self.form_status(True) self.validate()
class ConfigDialog(QDialog): """ Dialog window for specifying test configuration. The window contains two radio buttons (for 'py,test' and 'nose'), a line edit box for specifying the working directory, a button to use a file browser for selecting the directory, and OK and Cancel buttons. Initially, neither radio button is selected and the OK button is disabled. Selecting a radio button enabled the OK button. """ def __init__(self, config, parent=None): """ Construct a dialog window. Parameters ---------- config : Config Initial configuration parent : QWidget """ super(ConfigDialog, self).__init__(parent) self.setWindowTitle(_('Configure tests')) layout = QVBoxLayout(self) framework_groupbox = QGroupBox(_('Test framework'), self) framework_layout = QVBoxLayout(framework_groupbox) self.pytest_button = QRadioButton('py.test', framework_groupbox) framework_layout.addWidget(self.pytest_button) self.nose_button = QRadioButton('nose', framework_groupbox) framework_layout.addWidget(self.nose_button) layout.addWidget(framework_groupbox) layout.addSpacing(10) wdir_label = QLabel(_('Directory from which to run tests')) layout.addWidget(wdir_label) wdir_layout = QHBoxLayout() self.wdir_lineedit = QLineEdit(self) wdir_layout.addWidget(self.wdir_lineedit) self.wdir_button = QPushButton(ima.icon('DirOpenIcon'), '', self) self.wdir_button.setToolTip(_("Select directory")) self.wdir_button.clicked.connect(lambda: self.select_directory()) wdir_layout.addWidget(self.wdir_button) layout.addLayout(wdir_layout) layout.addSpacing(20) self.buttons = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) layout.addWidget(self.buttons) self.buttons.accepted.connect(self.accept) self.buttons.rejected.connect(self.reject) ok_button = self.buttons.button(QDialogButtonBox.Ok) ok_button.setEnabled(False) self.pytest_button.toggled.connect(lambda: ok_button.setEnabled(True)) self.nose_button.toggled.connect(lambda: ok_button.setEnabled(True)) if config.framework == 'py.test': self.pytest_button.setChecked(True) elif config.framework == 'nose': self.nose_button.setChecked(True) self.wdir_lineedit.setText(config.wdir) def select_directory(self): """Display dialog for user to select working directory.""" basedir = to_text_string(self.wdir_lineedit.text()) if not osp.isdir(basedir): basedir = getcwd() title = _("Select directory") directory = getexistingdirectory(self, title, basedir) if directory: self.wdir_lineedit.setText(directory) def get_config(self): """ Return the test configuration specified by the user. Returns ------- Config Test configuration """ if self.pytest_button.isChecked(): framework = 'py.test' elif self.nose_button.isChecked(): framework = 'nose' else: framework = None return Config(framework=framework, wdir=self.wdir_lineedit.text())
class CondaPackageActionDialog(QDialog): """ """ def __init__(self, parent, env, name, action, version, versions): super(CondaPackageActionDialog, self).__init__(parent) self._parent = parent self._env = env self._version_text = None self._name = name self._dependencies_dic = {} self._conda_process = \ conda_api_q.CondaProcess(self, self._on_process_finished) # widgets self.label = QLabel(self) self.combobox_version = QComboBox() self.label_version = QLabel(self) self.widget_version = None self.table_dependencies = None self.checkbox = QCheckBox(_('Install dependencies (recommended)')) self.bbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel, Qt.Horizontal, self) self.button_ok = self.bbox.button(QDialogButtonBox.Ok) self.button_cancel = self.bbox.button(QDialogButtonBox.Cancel) self.button_cancel.setDefault(True) self.button_cancel.setAutoDefault(True) dialog_size = QSize(300, 90) # helper variable values action_title = {const.UPGRADE: _("Upgrade package"), const.DOWNGRADE: _("Downgrade package"), const.REMOVE: _("Remove package"), const.INSTALL: _("Install package")} # Versions might have duplicates from different builds versions = sort_versions(list(set(versions)), reverse=True) # FIXME: There is a bug, a package installed by anaconda has version # astropy 0.4 and the linked list 0.4 but the available versions # in the json file do not include 0.4 but 0.4rc1... so... # temporal fix is to check if inside list otherwise show full list if action == const.UPGRADE: if version in versions: index = versions.index(version) versions = versions[:index] else: versions = versions elif action == const.DOWNGRADE: if version in versions: index = versions.index(version) versions = versions[index+1:] else: versions = versions elif action == const.REMOVE: versions = [version] self.combobox_version.setEnabled(False) if len(versions) == 1: if action == const.REMOVE: labeltext = _('Package version to remove:') else: labeltext = _('Package version available:') self.label_version.setText(versions[0]) self.widget_version = self.label_version else: labeltext = _("Select package version:") self.combobox_version.addItems(versions) self.widget_version = self.combobox_version self.label.setText(labeltext) self.label_version.setAlignment(Qt.AlignLeft) self.table_dependencies = QWidget(self) self._layout = QGridLayout() self._layout.addWidget(self.label, 0, 0, Qt.AlignVCenter | Qt.AlignLeft) self._layout.addWidget(self.widget_version, 0, 1, Qt.AlignVCenter | Qt.AlignRight) self.widgets = [self.checkbox, self.button_ok, self.widget_version, self.table_dependencies] row_index = 1 # Create a Table if action in [const.INSTALL, const.UPGRADE, const.DOWNGRADE]: table = QTableView(self) dialog_size = QSize(dialog_size.width() + 40, 300) self.table_dependencies = table row_index = 1 self._layout.addItem(QSpacerItem(10, 5), row_index, 0) self._layout.addWidget(self.checkbox, row_index + 1, 0, 1, 2) self.checkbox.setChecked(True) self._changed_version(versions[0]) table.setSelectionBehavior(QAbstractItemView.SelectRows) table.verticalHeader().hide() table.horizontalHeader().hide() table.setAlternatingRowColors(True) table.setShowGrid(False) table.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) table.horizontalHeader().setStretchLastSection(True) self._layout.addWidget(self.table_dependencies, row_index + 2, 0, 1, 2, Qt.AlignHCenter) self._layout.addItem(QSpacerItem(10, 5), row_index + 3, 0) self._layout.addWidget(self.bbox, row_index + 6, 0, 1, 2, Qt.AlignHCenter) title = "{0}: {1}".format(action_title[action], name) self.setLayout(self._layout) self.setMinimumSize(dialog_size) self.setFixedSize(dialog_size) self.setWindowTitle(title) self.setModal(True) # signals and slots self.bbox.accepted.connect(self.accept) self.bbox.rejected.connect(self.close) self.combobox_version.currentIndexChanged.connect( self._changed_version) self.checkbox.stateChanged.connect(self._changed_checkbox) def _changed_version(self, version, dependencies=True): """ """ self._set_gui_disabled(True) install_dependencies = (self.checkbox.checkState() == 2) self._version_text = to_text_string(version) self._get_dependencies(install_dependencies) def _get_dependencies(self, dependencies=True): """ """ name = [self._name + '=' + self._version_text] # Temporal fix env_name = self._env if env_name != 'root': env_name = osp.basename(env_name) self._conda_process.dependencies(name=env_name, pkgs=name, dep=dependencies) def _changed_checkbox(self, state): """ """ if state: self._changed_version(self._version_text) else: self._changed_version(self._version_text, dependencies=False) def _on_process_finished(self, boo): """ """ if self.isVisible(): dic = self._conda_process.output self.dependencies_dic = dic self._set_dependencies_table() self._set_gui_disabled(False) def _set_dependencies_table(self): """ """ table = self.table_dependencies dic = self.dependencies_dic table.setModel(CondaDependenciesModel(self, dic)) table.resizeColumnsToContents() table.resizeColumnToContents(1) def _set_gui_disabled(self, value): """ """ if value: table = self.table_dependencies table.setModel(CondaDependenciesModel(self, {})) table.resizeColumnsToContents() table.setDisabled(True) else: table = self.table_dependencies table.setDisabled(False) for widget in self.widgets: widget.setDisabled(value)
class FramesWidget(BaseFrame): rejected = Signal() accepted = Signal() back = Signal() def __init__(self, parent=None): super(FramesWidget, self).__init__(parent) self.config_frames = [] self.frames_instances = [] self.current_frame = 0 self.widget_layout = QVBoxLayout(self) self.setLayout(self.widget_layout) self.frames_widget = QWidget(self) self.frames_widget.setContentsMargins(0, 0, 0, 0) self.frames_widget_layout = QGridLayout(self.frames_widget) self.frames_widget_layout.setContentsMargins(0, 0, 0, 0) self.widget_layout.addWidget(self.frames_widget) self.btn_box = QDialogButtonBox(self) self.btn_box.setObjectName("btn_box") self.btn_box.setStandardButtons(QDialogButtonBox.Reset | QDialogButtonBox.Cancel | QDialogButtonBox.Ok) self.btn_box.button(QDialogButtonBox.Reset).setText("Back") self.btn_box.button(QDialogButtonBox.Ok).setText("Next") self.widget_layout.addWidget(self.btn_box) self.btn_box.button(QDialogButtonBox.Reset).clicked.connect( self.on_btn_box_resetted) QMetaObject.connectSlotsByName(self) def add_frames(self, frames): for frame in frames: self.add_frame(frame) def add_frame(self, frame): f = frame(self.frames_widget) f.setVisible(False) f.set_next_enabled.connect( self.btn_box.button(QDialogButtonBox.Ok).setEnabled) self.frames_widget_layout.addWidget(f, 0, 0, 1, 1) self.frames_instances.append(f) if len(self.frames_instances) > 0: self.frames_instances[0].setVisible(True) if self.frames_instances[0].disable_next_on_enter: self.btn_box.button(QDialogButtonBox.Ok).setEnabled(False) @Slot(dict) def set_options(self, options): for frame in self.frames_instances: frame.set_options(options) @Slot() def collect_info(self) -> dict: """Get info from every page""" settings = {} for frame in self.frames_instances: settings = settings | frame.collect_info() return settings @Slot() def on_btn_box_rejected(self): """When user clicks cancel button""" self.rejected.emit() @Slot() def on_btn_box_accepted(self): """On Next button clicked""" if self.current_frame + 1 < len(self.frames_instances): # if not last self.frames_instances[self.current_frame].setVisible(False) self.current_frame = self.current_frame + 1 self.frames_instances[self.current_frame].setVisible(True) # if disable on enter page self.btn_box.button( QDialogButtonBox.Ok).setEnabled(not self.frames_instances[ self.current_frame].disable_next_on_enter) # if next page is last self._change_next_finish() # if last page elif self.current_frame + 1 == len(self.frames_instances): self.accepted.emit() @Slot() def on_btn_box_resetted(self): """On Back button clicked.""" if self.current_frame > 0: self.frames_instances[self.current_frame].setVisible(False) self.current_frame = self.current_frame - 1 self.frames_instances[self.current_frame].setVisible(True) self._change_next_finish() self.btn_box.button( QDialogButtonBox.Ok).setEnabled(not self.frames_instances[ self.current_frame].disable_next_on_enter) else: self.back.emit() def _change_next_finish(self): if self.current_frame + 1 == len(self.frames_instances): self.btn_box.button(QDialogButtonBox.Ok).setText("Finish") else: self.btn_box.button(QDialogButtonBox.Ok).setText("Next")
class LevelEditDialog(QDialog): level_changed = Signal(LogLevel) def __init__(self, parent, level=None, creating_new_level=False, level_names=set()): super().__init__(parent) if level: self.level = level else: self.level = deepcopy(NO_LEVEL) self.creating_new_level = creating_new_level self.level_names = level_names self.setupUi() self.load_level(self.level) self.update_output() def setupUi(self): self.resize(350, 280) self.gridLayout = QGridLayout(self) self.levelNameLabel = QLabel("Level name", self) self.gridLayout.addWidget(self.levelNameLabel, 0, 0) self.levelNameLine = QLineEdit(self) self.gridLayout.addWidget(self.levelNameLine, 1, 0, 1, 0) self.groupBox = QGroupBox("Light mode", self) self.gridLayout.addWidget(self.groupBox, 2, 0) self.groupBoxDark = QGroupBox("Dark mode", self) self.gridLayout.addWidget(self.groupBoxDark, 2, 1) self.formLayout = QFormLayout(self.groupBox) self.groupBox.setLayout(self.formLayout) self.fgColorPreview = QLineEdit(self) self.formLayout.addRow("Foreground", self.fgColorPreview) self.bgColorPreview = QLineEdit(self) self.formLayout.addRow("Background", self.bgColorPreview) self.boldCheckBox = QCheckBox(self.groupBox) self.formLayout.addRow("Bold", self.boldCheckBox) self.italicCheckBox = QCheckBox(self.groupBox) self.formLayout.addRow("Italic", self.italicCheckBox) self.underlineCheckBox = QCheckBox(self.groupBox) self.formLayout.addRow("Underline", self.underlineCheckBox) self.formLayoutDark = QFormLayout(self.groupBoxDark) self.groupBoxDark.setLayout(self.formLayoutDark) self.fgColorPreviewDark = QLineEdit(self) self.formLayoutDark.addRow("Foreground", self.fgColorPreviewDark) self.bgColorPreviewDark = QLineEdit(self) self.formLayoutDark.addRow("Background", self.bgColorPreviewDark) self.boldCheckBoxDark = QCheckBox(self.groupBoxDark) self.formLayoutDark.addRow("Bold", self.boldCheckBoxDark) self.italicCheckBoxDark = QCheckBox(self.groupBox) self.formLayoutDark.addRow("Italic", self.italicCheckBoxDark) self.underlineCheckBoxDark = QCheckBox(self.groupBox) self.formLayoutDark.addRow("Underline", self.underlineCheckBoxDark) self.spacer = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) self.gridLayout.addItem(self.spacer, 3, 0, 1, 2) self.previewLabel = QLabel("Preview", self) self.gridLayout.addWidget(self.previewLabel, 4, 0, 1, 2) self.previewLine = QLineEdit(self) self.gridLayout.addWidget(self.previewLine, 5, 0) self.previewLineDark = QLineEdit(self) self.gridLayout.addWidget(self.previewLineDark, 5, 1) buttons = QDialogButtonBox.Reset | QDialogButtonBox.Save | QDialogButtonBox.Cancel self.buttonBox = QDialogButtonBox(buttons, self) self.resetButton = self.buttonBox.button(QDialogButtonBox.Reset) self.gridLayout.addWidget(self.buttonBox, 6, 0, 1, 2) self.setup_widget_attributes() self.setup_widget_connections() def setup_widget_attributes(self): self.fgColorPreview.setReadOnly(True) self.bgColorPreview.setReadOnly(True) self.fgColorPreviewDark.setReadOnly(True) self.bgColorPreviewDark.setReadOnly(True) self.previewLine.setText("Log message") self.previewLineDark.setText("Log message") self.resetButton.setMaximumWidth(60) self.resetButton.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) self.levelNameLine.setText(self.level.levelname) if self.creating_new_level: self.name_validator = LevelNameValidator(self, self.level_names) self.levelNameLine.setValidator(self.name_validator) self.levelNameLine.textChanged.connect(self.level_name_valid) else: self.levelNameLine.setReadOnly(True) def setup_widget_connections(self): self.boldCheckBox.toggled.connect(self.toggle_bold) self.italicCheckBox.toggled.connect(self.toggle_italic) self.underlineCheckBox.toggled.connect(self.toggle_underline) self.boldCheckBoxDark.toggled.connect( partial(self.toggle_bold, dark=True)) self.italicCheckBoxDark.toggled.connect( partial(self.toggle_italic, dark=True)) self.underlineCheckBoxDark.toggled.connect( partial(self.toggle_underline, dark=True)) # couldn't find a way to make this any better self.fgColorPreview.mouseReleaseEvent = partial( self.open_color_dialog, 'fg') self.bgColorPreview.mouseReleaseEvent = partial( self.open_color_dialog, 'bg') self.fgColorPreviewDark.mouseReleaseEvent = partial( self.open_color_dialog, 'fgDark') self.bgColorPreviewDark.mouseReleaseEvent = partial( self.open_color_dialog, 'bgDark') self.buttonBox.accepted.connect(self.accept) self.buttonBox.rejected.connect(self.reject) self.resetButton.clicked.connect(self.reset_level) def set_checkboxes_state(self): self.boldCheckBox.setChecked(self.bold) self.italicCheckBox.setChecked(self.italic) self.underlineCheckBox.setChecked(self.underline) self.boldCheckBoxDark.setChecked(self.boldDark) self.italicCheckBoxDark.setChecked(self.italicDark) self.underlineCheckBoxDark.setChecked(self.underlineDark) def load_level(self, level): self.bg = level.bg self.fg = level.fg self.bgDark = level.bgDark self.fgDark = level.fgDark self.bold = 'bold' in level.styles self.italic = 'italic' in level.styles self.underline = 'underline' in level.styles self.boldDark = 'bold' in level.stylesDark self.italicDark = 'italic' in level.stylesDark self.underlineDark = 'underline' in level.stylesDark def reset_level(self): replacement = DEFAULT_LEVELS.get(self.level.levelname) if not replacement: replacement = NO_LEVEL self.load_level(replacement) self.update_output() def toggle_bold(self, enabled, dark=False): if not dark: self.bold = enabled else: self.boldDark = enabled self.update_output() def toggle_italic(self, enabled, dark=False): if not dark: self.italic = enabled else: self.italicDark = enabled self.update_output() def toggle_underline(self, enabled, dark=False): if not dark: self.underline = enabled else: self.underlineDark = enabled self.update_output() def open_color_dialog(self, attr_name, mouse_event): d = QColorDialog(self) d.setCurrentColor(getattr(self, attr_name)) f = partial(self.set_color, attr_name) d.colorSelected.connect( f) # d.open(f) doesn't pass color for some reason d.open() def set_color(self, attr_name, color): setattr(self, attr_name, color) self.update_output() def accept(self): self.level.styles = set() if self.bold: self.level.styles.add('bold') if self.italic: self.level.styles.add('italic') if self.underline: self.level.styles.add('underline') self.level.stylesDark = set() if self.boldDark: self.level.stylesDark.add('bold') if self.italicDark: self.level.stylesDark.add('italic') if self.underlineDark: self.level.stylesDark.add('underline') self.level.bg = self.bg self.level.fg = self.fg self.level.bgDark = self.bgDark self.level.fgDark = self.fgDark self.level.levelname = self.levelNameLine.text().upper() self.level_changed.emit(self.level) self.done(0) def reject(self): self.done(0) def update_output(self): # Setting the pallette doesn't override the global stylesheet, # which is why I can't just set pallete with needed colors here. self.previewLine.setStyleSheet("""QLineEdit {{ color: {}; background: {} }}""".format(self.fg.name(), self.bg.name())) self.previewLineDark.setStyleSheet("""QLineEdit {{ color: {}; background: {} }}""".format( self.fgDark.name(), self.bgDark.name())) self.bgColorPreview.setStyleSheet( 'QLineEdit {{background: {} }}'.format(self.bg.name())) self.fgColorPreview.setStyleSheet( 'QLineEdit {{background: {} }}'.format(self.fg.name())) self.bgColorPreviewDark.setStyleSheet( 'QLineEdit {{ background: {} }}'.format(self.bgDark.name())) self.fgColorPreviewDark.setStyleSheet( 'QLineEdit {{ background: {} }}'.format(self.fgDark.name())) font = self.previewLine.font() font.setBold(self.bold) font.setItalic(self.italic) font.setUnderline(self.underline) self.previewLine.setFont(font) fontDark = self.previewLineDark.font() fontDark.setBold(self.boldDark) fontDark.setItalic(self.italicDark) fontDark.setUnderline(self.underlineDark) self.previewLineDark.setFont(fontDark) self.set_checkboxes_state() def level_name_valid(self): if self.levelNameLine.hasAcceptableInput(): self.buttonBox.button(QDialogButtonBox.Save).setEnabled(True) else: self.buttonBox.button(QDialogButtonBox.Save).setEnabled(False)
class SnippetEditor(QDialog): SNIPPET_VALID = _('Valid snippet') SNIPPET_INVALID = _('Invalid snippet') INVALID_CB_CSS = "QComboBox {border: 1px solid red;}" VALID_CB_CSS = "QComboBox {border: 1px solid green;}" INVALID_LINE_CSS = "QLineEdit {border: 1px solid red;}" VALID_LINE_CSS = "QLineEdit {border: 1px solid green;}" MIN_SIZE = QSize(850, 600) def __init__(self, parent, language=None, trigger_text='', description='', snippet_text='', remove_trigger=False, trigger_texts=[], descriptions=[], get_option=None, set_option=None): super(SnippetEditor, self).__init__(parent) snippet_description = _( "To add a new text snippet, you need to define the text " "that triggers it, a short description (two words maximum) " "of the snippet and if it should delete the trigger text when " "inserted. Finally, you need to define the snippet body to insert." ) self.parent = parent self.trigger_text = trigger_text self.description = description self.remove_trigger = remove_trigger self.snippet_text = snippet_text self.descriptions = descriptions self.base_snippet = Snippet(language=language, trigger_text=trigger_text, snippet_text=snippet_text, description=description, remove_trigger=remove_trigger, get_option=get_option, set_option=set_option) # Widgets self.snippet_settings_description = QLabel(snippet_description) self.snippet_settings_description.setFixedWidth(450) # Trigger text self.trigger_text_label = QLabel(_('Trigger text:')) self.trigger_text_cb = QComboBox(self) self.trigger_text_cb.setEditable(True) # Description self.description_label = QLabel(_('Description:')) self.description_input = QLineEdit(self) # Remove trigger self.remove_trigger_cb = QCheckBox( _('Remove trigger text on insertion'), self) self.remove_trigger_cb.setToolTip( _('Check if the text that triggers ' 'this snippet should be removed ' 'when inserting it')) self.remove_trigger_cb.setChecked(self.remove_trigger) # Snippet body input self.snippet_label = QLabel(_('<b>Snippet text:</b>')) self.snippet_valid_label = QLabel(self.SNIPPET_INVALID, self) self.snippet_input = SimpleCodeEditor(None) # Dialog buttons self.bbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.button_ok = self.bbox.button(QDialogButtonBox.Ok) self.button_cancel = self.bbox.button(QDialogButtonBox.Cancel) # Widget setup self.setWindowTitle(_('Snippet editor')) self.snippet_settings_description.setWordWrap(True) self.trigger_text_cb.setToolTip( _('Trigger text for the current snippet')) self.trigger_text_cb.addItems(trigger_texts) if self.trigger_text != '': idx = trigger_texts.index(self.trigger_text) self.trigger_text_cb.setCurrentIndex(idx) self.description_input.setText(self.description) self.description_input.textChanged.connect(lambda _x: self.validate()) text_inputs = (self.trigger_text, self.description, self.snippet_text) non_empty_text = all([x != '' for x in text_inputs]) if non_empty_text: self.button_ok.setEnabled(True) self.snippet_input.setup_editor(language=language, color_scheme=get_option( 'selected', section='appearance'), wrap=False, highlight_current_line=True, font=get_font()) self.snippet_input.set_language(language) self.snippet_input.setToolTip(_('Snippet text completion to insert')) self.snippet_input.set_text(snippet_text) # Layout setup general_layout = QVBoxLayout() general_layout.addWidget(self.snippet_settings_description) snippet_settings_group = QGroupBox(_('Trigger information')) settings_layout = QGridLayout() settings_layout.addWidget(self.trigger_text_label, 0, 0) settings_layout.addWidget(self.trigger_text_cb, 0, 1) settings_layout.addWidget(self.description_label, 1, 0) settings_layout.addWidget(self.description_input, 1, 1) all_settings_layout = QVBoxLayout() all_settings_layout.addLayout(settings_layout) all_settings_layout.addWidget(self.remove_trigger_cb) snippet_settings_group.setLayout(all_settings_layout) general_layout.addWidget(snippet_settings_group) text_layout = QVBoxLayout() text_layout.addWidget(self.snippet_label) text_layout.addWidget(self.snippet_input) text_layout.addWidget(self.snippet_valid_label) general_layout.addLayout(text_layout) general_layout.addWidget(self.bbox) self.setLayout(general_layout) # Signals self.trigger_text_cb.editTextChanged.connect(self.validate) self.description_input.textChanged.connect(self.validate) self.snippet_input.textChanged.connect(self.validate) self.bbox.accepted.connect(self.accept) self.bbox.rejected.connect(self.reject) # Final setup if trigger_text != '' or snippet_text != '': self.validate() @Slot() def validate(self): trigger_text = self.trigger_text_cb.currentText() description_text = self.description_input.text() snippet_text = self.snippet_input.toPlainText() invalid = False try: build_snippet_ast(snippet_text) self.snippet_valid_label.setText(self.SNIPPET_VALID) except SyntaxError: invalid = True self.snippet_valid_label.setText(self.SNIPPET_INVALID) if trigger_text == '': invalid = True self.trigger_text_cb.setStyleSheet(self.INVALID_CB_CSS) else: self.trigger_text_cb.setStyleSheet(self.VALID_CB_CSS) if trigger_text in self.descriptions: if self.trigger_text != trigger_text: if description_text in self.descriptions[trigger_text]: invalid = True self.description_input.setStyleSheet(self.INVALID_LINE_CSS) else: self.description_input.setStyleSheet(self.VALID_LINE_CSS) else: if description_text != self.description: if description_text in self.descriptions[trigger_text]: invalid = True self.description_input.setStyleSheet( self.INVALID_LINE_CSS) else: self.description_input.setStyleSheet( self.VALID_LINE_CSS) else: self.description_input.setStyleSheet(self.VALID_LINE_CSS) self.button_ok.setEnabled(not invalid) def get_options(self): trigger_text = self.trigger_text_cb.currentText() description_text = self.description_input.text() snippet_text = self.snippet_input.toPlainText() remove_trigger = self.remove_trigger_cb.isChecked() self.base_snippet.update(trigger_text, description_text, snippet_text, remove_trigger) return self.base_snippet
class ConfigDialog(QDialog): """ Dialog window for specifying test configuration. The window contains a combobox with all the frameworks, a line edit box for specifying the working directory, a button to use a file browser for selecting the directory, and OK and Cancel buttons. Initially, no framework is selected and the OK button is disabled. Selecting a framework enables the OK button. """ def __init__(self, frameworks, config, parent=None): """ Construct a dialog window. Parameters ---------- frameworks : dict of (str, type) Names of all supported frameworks with their associated class (assumed to be a subclass of RunnerBase) config : Config Initial configuration parent : QWidget """ super(ConfigDialog, self).__init__(parent) self.setWindowTitle(_('Configure tests')) layout = QVBoxLayout(self) framework_layout = QHBoxLayout() framework_label = QLabel(_('Test framework')) framework_layout.addWidget(framework_label) self.framework_combobox = QComboBox(self) for ix, (name, runner) in enumerate(sorted(frameworks.items())): installed = runner.is_installed() if installed: label = name else: label = '{} ({})'.format(name, _('not available')) self.framework_combobox.addItem(label) self.framework_combobox.model().item(ix).setEnabled(installed) framework_layout.addWidget(self.framework_combobox) layout.addLayout(framework_layout) layout.addSpacing(10) wdir_label = QLabel(_('Directory from which to run tests')) layout.addWidget(wdir_label) wdir_layout = QHBoxLayout() self.wdir_lineedit = QLineEdit(self) wdir_layout.addWidget(self.wdir_lineedit) self.wdir_button = QPushButton(ima.icon('DirOpenIcon'), '', self) self.wdir_button.setToolTip(_("Select directory")) self.wdir_button.clicked.connect(lambda: self.select_directory()) wdir_layout.addWidget(self.wdir_button) layout.addLayout(wdir_layout) layout.addSpacing(20) self.buttons = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) layout.addWidget(self.buttons) self.buttons.accepted.connect(self.accept) self.buttons.rejected.connect(self.reject) self.ok_button = self.buttons.button(QDialogButtonBox.Ok) self.ok_button.setEnabled(False) self.framework_combobox.currentIndexChanged.connect( self.framework_changed) self.framework_combobox.setCurrentIndex(-1) if config.framework: index = self.framework_combobox.findText(config.framework) if index != -1: self.framework_combobox.setCurrentIndex(index) self.wdir_lineedit.setText(config.wdir) @Slot(int) def framework_changed(self, index): """Called when selected framework changes.""" if index != -1: self.ok_button.setEnabled(True) def select_directory(self): """Display dialog for user to select working directory.""" basedir = to_text_string(self.wdir_lineedit.text()) if not osp.isdir(basedir): basedir = getcwd() title = _("Select directory") directory = getexistingdirectory(self, title, basedir) if directory: self.wdir_lineedit.setText(directory) def get_config(self): """ Return the test configuration specified by the user. Returns ------- Config Test configuration """ framework = self.framework_combobox.currentText() if framework == '': framework = None return Config(framework=framework, wdir=self.wdir_lineedit.text())
def __init__(self, parent, context, name, sequence, shortcuts): super(ShortcutEditor, self).__init__(parent) self._parent = parent self.context = context self.npressed = 0 self.keys = set() self.key_modifiers = set() self.key_non_modifiers = list() self.key_text = list() self.sequence = sequence self.new_sequence = None self.edit_state = True self.shortcuts = shortcuts # Widgets self.label_info = QLabel() self.label_info.setText( _("Press the new shortcut and select 'Ok': \n" "(Press 'Tab' once to switch focus between the shortcut entry \n" "and the buttons below it)")) self.label_current_sequence = QLabel(_("Current shortcut:")) self.text_current_sequence = QLabel(sequence) self.label_new_sequence = QLabel(_("New shortcut:")) self.text_new_sequence = CustomLineEdit(self) self.text_new_sequence.setPlaceholderText(sequence) self.helper_button = HelperToolButton() self.helper_button.hide() self.label_warning = QLabel() self.label_warning.hide() bbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.button_ok = bbox.button(QDialogButtonBox.Ok) self.button_cancel = bbox.button(QDialogButtonBox.Cancel) # Setup widgets self.setWindowTitle(_('Shortcut: {0}').format(name)) self.button_ok.setFocusPolicy(Qt.NoFocus) self.button_ok.setEnabled(False) self.button_cancel.setFocusPolicy(Qt.NoFocus) self.helper_button.setToolTip('') self.helper_button.setFocusPolicy(Qt.NoFocus) style = """ QToolButton { margin:1px; border: 0px solid grey; padding:0px; border-radius: 0px; }""" self.helper_button.setStyleSheet(style) self.text_new_sequence.setFocusPolicy(Qt.NoFocus) self.label_warning.setFocusPolicy(Qt.NoFocus) # Layout spacing = 5 layout_sequence = QGridLayout() layout_sequence.addWidget(self.label_info, 0, 0, 1, 3) layout_sequence.addItem(QSpacerItem(spacing, spacing), 1, 0, 1, 2) layout_sequence.addWidget(self.label_current_sequence, 2, 0) layout_sequence.addWidget(self.text_current_sequence, 2, 2) layout_sequence.addWidget(self.label_new_sequence, 3, 0) layout_sequence.addWidget(self.helper_button, 3, 1) layout_sequence.addWidget(self.text_new_sequence, 3, 2) layout_sequence.addWidget(self.label_warning, 4, 2, 1, 2) layout = QVBoxLayout() layout.addLayout(layout_sequence) layout.addSpacing(spacing) layout.addWidget(bbox) self.setLayout(layout) # Signals bbox.accepted.connect(self.accept) bbox.rejected.connect(self.reject)
class LayoutSettingsDialog(QDialog): """Layout settings dialog""" def __init__(self, parent, names, order, active): super(LayoutSettingsDialog, self).__init__(parent) # variables self._parent = parent self._selection_model = None self.names = names self.order = order self.active = active # widgets self.button_move_up = QPushButton(_('Move Up')) self.button_move_down = QPushButton(_('Move Down')) self.button_delete = QPushButton(_('Delete Layout')) self.button_box = QDialogButtonBox( QDialogButtonBox.Ok | QDialogButtonBox.Cancel, Qt.Horizontal, self) self.group_box = QGroupBox(_("Layout Display and Order")) self.table = QTableView(self) self.ok_button = self.button_box.button(QDialogButtonBox.Ok) self.cancel_button = self.button_box.button(QDialogButtonBox.Cancel) self.cancel_button.setDefault(True) self.cancel_button.setAutoDefault(True) # widget setup self.dialog_size = QSize(300, 200) self.setMinimumSize(self.dialog_size) self.setFixedSize(self.dialog_size) self.setWindowTitle('Layout Settings') self.table.setModel(LayoutModel(self.table, order, active)) self.table.setSelectionBehavior(QAbstractItemView.SelectRows) self.table.setSelectionMode(QAbstractItemView.SingleSelection) self.table.verticalHeader().hide() self.table.horizontalHeader().hide() self.table.setAlternatingRowColors(True) self.table.setShowGrid(False) self.table.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.table.horizontalHeader().setStretchLastSection(True) self.table.setColumnHidden(1, True) # need to keep a reference for pyside not to segfault! self._selection_model = self.table.selectionModel() # layout buttons_layout = QVBoxLayout() buttons_layout.addWidget(self.button_move_up) buttons_layout.addWidget(self.button_move_down) buttons_layout.addStretch() buttons_layout.addWidget(self.button_delete) group_layout = QHBoxLayout() group_layout.addWidget(self.table) group_layout.addLayout(buttons_layout) self.group_box.setLayout(group_layout) layout = QVBoxLayout() layout.addWidget(self.group_box) layout.addWidget(self.button_box) self.setLayout(layout) # signals and slots self.button_box.accepted.connect(self.accept) self.button_box.rejected.connect(self.close) self.button_delete.clicked.connect(self.delete_layout) self.button_move_up.clicked.connect(lambda: self.move_layout(True)) self.button_move_down.clicked.connect(lambda: self.move_layout(False)) self.table.model().dataChanged.connect( lambda: self.selection_changed(None, None)) self._selection_model.selectionChanged.connect( lambda: self.selection_changed(None, None)) # focus table index = self.table.model().index(0, 0) self.table.setCurrentIndex(index) self.table.setFocus() def delete_layout(self): """ """ names, order, active = self.names, self.order, self.order name = from_qvariant(self.table.selectionModel().currentIndex().data(), to_text_string) if name in names: index = names.index(name) # In case nothing has focus in the table if index != -1: order.remove(name) names[index] = None if name in active: active.remove(name) self.names, self.order, self.active = names, order, active self.table.model().set_data(order, active) index = self.table.model().index(0, 0) self.table.setCurrentIndex(index) self.table.setFocus() self.selection_changed(None, None) if len(order) == 0: self.button_move_up.setDisabled(True) self.button_move_down.setDisabled(True) self.button_delete.setDisabled(True) def move_layout(self, up=True): """ """ names, order, active = self.names, self.order, self.active row = self.table.selectionModel().currentIndex().row() row_new = row if up: row_new -= 1 else: row_new += 1 order[row], order[row_new] = order[row_new], order[row] self.order = order self.table.model().set_data(order, active) index = self.table.model().index(row_new, 0) self.table.setCurrentIndex(index) self.table.setFocus() self.selection_changed(None, None) def selection_changed(self, selection, deselection): """ """ model = self.table.model() index = self.table.currentIndex() row = index.row() order, names, active = self.order, self.names, self.active state = model.row(row)[1] name = model.row(row)[0] # Check if name changed if name not in names: # Did changed if row != -1: # row == -1, means no items left to delete old_name = order[row] order[row] = name names[names.index(old_name)] = name if old_name in active: active[active.index(old_name)] = name # Check if checbox clicked if state: if name not in active: active.append(name) else: if name in active: active.remove(name) self.active = active self.button_move_up.setDisabled(False) self.button_move_down.setDisabled(False) if row == 0: self.button_move_up.setDisabled(True) if row == len(names) - 1: self.button_move_down.setDisabled(True) if len(names) == 0: self.button_move_up.setDisabled(True) self.button_move_down.setDisabled(True)
class CondaPackageActionDialog(QDialog): """ """ def __init__(self, parent, prefix, name, action, version, versions, packages_sizes, active_channels): super(CondaPackageActionDialog, self).__init__(parent) self._parent = parent self._prefix = prefix self._version_text = None self._name = name self._dependencies_dic = {} self._active_channels = active_channels self._packages_sizes = packages_sizes self.api = ManagerAPI() # Widgets self.label = QLabel(self) self.combobox_version = QComboBox() self.label_version = QLabel(self) self.widget_version = None self.table_dependencies = None self.checkbox = QCheckBox(_("Install dependencies (recommended)")) self.bbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel, Qt.Horizontal, self) self.button_ok = self.bbox.button(QDialogButtonBox.Ok) self.button_cancel = self.bbox.button(QDialogButtonBox.Cancel) self.button_cancel.setDefault(True) self.button_cancel.setAutoDefault(True) dialog_size = QSize(300, 90) # Helper variable values action_title = { C.ACTION_UPGRADE: _("Upgrade package"), C.ACTION_DOWNGRADE: _("Downgrade package"), C.ACTION_REMOVE: _("Remove package"), C.ACTION_INSTALL: _("Install package"), } # FIXME: There is a bug, a package installed by anaconda has version # astropy 0.4 and the linked list 0.4 but the available versions # in the json file do not include 0.4 but 0.4rc1... so... # temporal fix is to check if inside list otherwise show full list if action == C.ACTION_UPGRADE: if version in versions: index = versions.index(version) combo_versions = versions[index + 1 :] else: versions = versions elif action == C.ACTION_DOWNGRADE: if version in versions: index = versions.index(version) combo_versions = versions[:index] else: versions = versions elif action == C.ACTION_REMOVE: combo_versions = [version] self.combobox_version.setEnabled(False) elif action == C.ACTION_INSTALL: combo_versions = versions # Reverse order for combobox combo_versions = list(reversed(combo_versions)) if len(versions) == 1: if action == C.ACTION_REMOVE: labeltext = _("Package version to remove:") else: labeltext = _("Package version available:") self.label_version.setText(combo_versions[0]) self.widget_version = self.label_version else: labeltext = _("Select package version:") self.combobox_version.addItems(combo_versions) self.widget_version = self.combobox_version self.label.setText(labeltext) self.label_version.setAlignment(Qt.AlignLeft) self.table_dependencies = QWidget(self) layout = QVBoxLayout() version_layout = QHBoxLayout() version_layout.addWidget(self.label) version_layout.addStretch() version_layout.addWidget(self.widget_version) layout.addLayout(version_layout) self.widgets = [self.checkbox, self.button_ok, self.widget_version, self.table_dependencies] # Create a Table if action in [C.ACTION_INSTALL, C.ACTION_UPGRADE, C.ACTION_DOWNGRADE]: table = QTableView(self) dialog_size = QSize(dialog_size.width() + 40, 300) self.table_dependencies = table layout.addWidget(self.checkbox) self.checkbox.setChecked(True) self._changed_version(versions[0]) table.setSelectionBehavior(QAbstractItemView.SelectRows) table.verticalHeader().hide() table.horizontalHeader().hide() table.setAlternatingRowColors(True) table.setShowGrid(False) table.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) table.horizontalHeader().setStretchLastSection(True) layout.addWidget(self.table_dependencies) layout.addWidget(self.bbox) title = "{0}: {1}".format(action_title[action], name) self.setLayout(layout) self.setMinimumSize(dialog_size) self.setFixedSize(dialog_size) self.setWindowTitle(title) self.setModal(True) # Signals and slots self.bbox.accepted.connect(self.accept) self.bbox.rejected.connect(self.close) self.combobox_version.currentIndexChanged.connect(self._changed_version) self.checkbox.stateChanged.connect(self._changed_checkbox) def _changed_version(self, version, dependencies=True): """ """ self._set_gui_disabled(True) version = self.combobox_version.currentText() install_dependencies = self.checkbox.checkState() == Qt.Checked self._version_text = to_text_string(version) self._get_dependencies(install_dependencies) def _get_dependencies(self, dependencies=True): """ """ package_name = [self._name + "=" + self._version_text] worker = self.api.conda_dependencies( prefix=self._prefix, pkgs=package_name, dep=dependencies, channels=self._active_channels ) worker.sig_finished.connect(self._on_process_finished) def _changed_checkbox(self, state): """ """ if state: self._changed_version(self._version_text) else: self._changed_version(self._version_text, dependencies=False) def _on_process_finished(self, worker, output, error): """ """ if self.isVisible(): dic = output self.dependencies_dic = dic self._set_dependencies_table() self._set_gui_disabled(False) def _set_dependencies_table(self): """ """ table = self.table_dependencies dic = self.dependencies_dic table.setModel(CondaDependenciesModel(self, dic, self._packages_sizes)) table.resizeColumnsToContents() table.resizeRowsToContents() def _set_gui_disabled(self, value): """ """ if value: table = self.table_dependencies table.setModel(CondaDependenciesModel(self, {})) table.resizeColumnsToContents() table.setDisabled(True) else: table = self.table_dependencies table.setDisabled(False) for widget in self.widgets: widget.setDisabled(value) def reject(self): self.api.conda_terminate() super(CondaPackageActionDialog, self).reject()
class LayoutSettingsDialog(QDialog): """Layout settings dialog""" def __init__(self, parent, names, order, active): super(LayoutSettingsDialog, self).__init__(parent) # variables self._parent = parent self._selection_model = None self.names = names self.order = order self.active = active # widgets self.button_move_up = QPushButton(_('Move Up')) self.button_move_down = QPushButton(_('Move Down')) self.button_delete = QPushButton(_('Delete Layout')) self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel, Qt.Horizontal, self) self.group_box = QGroupBox(_("Layout Display and Order")) self.table = QTableView(self) self.ok_button = self.button_box.button(QDialogButtonBox.Ok) self.cancel_button = self.button_box.button(QDialogButtonBox.Cancel) self.cancel_button.setDefault(True) self.cancel_button.setAutoDefault(True) # widget setup self.dialog_size = QSize(300, 200) self.setMinimumSize(self.dialog_size) self.setFixedSize(self.dialog_size) self.setWindowTitle('Layout Settings') self.table.setModel(LayoutModel(self.table, order, active)) self.table.setSelectionBehavior(QAbstractItemView.SelectRows) self.table.setSelectionMode(QAbstractItemView.SingleSelection) self.table.verticalHeader().hide() self.table.horizontalHeader().hide() self.table.setAlternatingRowColors(True) self.table.setShowGrid(False) self.table.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.table.horizontalHeader().setStretchLastSection(True) self.table.setColumnHidden(1, True) # need to keep a reference for pyside not to segfault! self._selection_model = self.table.selectionModel() # layout buttons_layout = QVBoxLayout() buttons_layout.addWidget(self.button_move_up) buttons_layout.addWidget(self.button_move_down) buttons_layout.addStretch() buttons_layout.addWidget(self.button_delete) group_layout = QHBoxLayout() group_layout.addWidget(self.table) group_layout.addLayout(buttons_layout) self.group_box.setLayout(group_layout) layout = QVBoxLayout() layout.addWidget(self.group_box) layout.addWidget(self.button_box) self.setLayout(layout) # signals and slots self.button_box.accepted.connect(self.accept) self.button_box.rejected.connect(self.close) self.button_delete.clicked.connect(self.delete_layout) self.button_move_up.clicked.connect(lambda: self.move_layout(True)) self.button_move_down.clicked.connect(lambda: self.move_layout(False)) self.table.model().dataChanged.connect( lambda: self.selection_changed(None, None)) self._selection_model.selectionChanged.connect( lambda: self.selection_changed(None, None)) # focus table index = self.table.model().index(0, 0) self.table.setCurrentIndex(index) self.table.setFocus() def delete_layout(self): """ """ names, order, active = self.names, self.order, self.order name = from_qvariant(self.table.selectionModel().currentIndex().data(), to_text_string) if name in names: index = names.index(name) # In case nothing has focus in the table if index != -1: order.remove(name) names[index] = None if name in active: active.remove(name) self.names, self.order, self.active = names, order, active self.table.model().set_data(order, active) index = self.table.model().index(0, 0) self.table.setCurrentIndex(index) self.table.setFocus() self.selection_changed(None, None) if len(order) == 0: self.button_move_up.setDisabled(True) self.button_move_down.setDisabled(True) self.button_delete.setDisabled(True) def move_layout(self, up=True): """ """ names, order, active = self.names, self.order, self.active row = self.table.selectionModel().currentIndex().row() row_new = row if up: row_new -= 1 else: row_new += 1 order[row], order[row_new] = order[row_new], order[row] self.order = order self.table.model().set_data(order, active) index = self.table.model().index(row_new, 0) self.table.setCurrentIndex(index) self.table.setFocus() self.selection_changed(None, None) def selection_changed(self, selection, deselection): """ """ model = self.table.model() index = self.table.currentIndex() row = index.row() order, names, active = self.order, self.names, self.active state = model.row(row)[1] name = model.row(row)[0] # Check if name changed if name not in names: # Did changed if row != -1: # row == -1, means no items left to delete old_name = order[row] order[row] = name names[names.index(old_name)] = name if old_name in active: active[active.index(old_name)] = name # Check if checbox clicked if state: if name not in active: active.append(name) else: if name in active: active.remove(name) self.active = active self.button_move_up.setDisabled(False) self.button_move_down.setDisabled(False) if row == 0: self.button_move_up.setDisabled(True) if row == len(names) - 1: self.button_move_down.setDisabled(True) if len(names) == 0: self.button_move_up.setDisabled(True) self.button_move_down.setDisabled(True)
class ApplicationsDialog(QDialog): """Dialog for selection of installed system/user applications.""" def __init__(self, parent=None): """Dialog for selection of installed system/user applications.""" super(ApplicationsDialog, self).__init__(parent=parent) # Widgets self.label = QLabel() self.label_browse = QLabel() self.edit_filter = QLineEdit() self.list = QListWidget() self.button_browse = QPushButton(_('Browse...')) self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.button_ok = self.button_box.button(QDialogButtonBox.Ok) self.button_cancel = self.button_box.button(QDialogButtonBox.Cancel) # Widget setup self.setWindowTitle(_('Applications')) self.edit_filter.setPlaceholderText(_('Type to filter by name')) self.list.setIconSize(QSize(16, 16)) # FIXME: Use metrics # Layout layout = QVBoxLayout() layout.addWidget(self.label) layout.addWidget(self.edit_filter) layout.addWidget(self.list) layout_browse = QHBoxLayout() layout_browse.addWidget(self.button_browse) layout_browse.addWidget(self.label_browse) layout.addLayout(layout_browse) layout.addSpacing(12) # FIXME: Use metrics layout.addWidget(self.button_box) self.setLayout(layout) # Signals self.edit_filter.textChanged.connect(self.filter) self.button_browse.clicked.connect(lambda x: self.browse()) self.button_ok.clicked.connect(self.accept) self.button_cancel.clicked.connect(self.reject) self.list.currentItemChanged.connect(self._refresh) self._refresh() self.setup() def setup(self, applications=None): """Load installed applications.""" QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) self.list.clear() if applications is None: apps = get_installed_applications() else: apps = applications for app in sorted(apps, key=lambda x: x.lower()): fpath = apps[app] icon = get_application_icon(fpath) item = QListWidgetItem(icon, app) item.setToolTip(fpath) item.fpath = fpath self.list.addItem(item) # FIXME: Use metrics self.list.setMinimumWidth(self.list.sizeHintForColumn(0) + 24) QApplication.restoreOverrideCursor() self._refresh() def _refresh(self): """Refresh the status of buttons on widget.""" self.button_ok.setEnabled(self.list.currentRow() != -1) def browse(self, fpath=None): """Prompt user to select an application not found on the list.""" app = None item = None if sys.platform == 'darwin': if fpath is None: basedir = '/Applications/' filters = _('Applications (*.app)') title = _('Select application') fpath, __ = getopenfilename(self, title, basedir, filters) if fpath and fpath.endswith('.app') and os.path.isdir(fpath): app = os.path.basename(fpath).split('.app')[0] for row in range(self.list.count()): item = self.list.item(row) if app == item.text() and fpath == item.fpath: break else: item = None elif os.name == 'nt': if fpath is None: basedir = 'C:\\' filters = _('Applications (*.exe *.bat *.com)') title = _('Select application') fpath, __ = getopenfilename(self, title, basedir, filters) if fpath: check_1 = fpath.endswith('.bat') and is_text_file(fpath) check_2 = (fpath.endswith(('.exe', '.com')) and not is_text_file(fpath)) if check_1 or check_2: app = os.path.basename(fpath).capitalize().rsplit('.')[0] for row in range(self.list.count()): item = self.list.item(row) if app == item.text() and fpath == item.fpath: break else: item = None else: if fpath is None: basedir = '/' filters = _('Applications (*.desktop)') title = _('Select application') fpath, __ = getopenfilename(self, title, basedir, filters) if fpath and fpath.endswith(('.desktop')) and is_text_file(fpath): entry_data = parse_linux_desktop_entry(fpath) app = entry_data['name'] for row in range(self.list.count()): item = self.list.item(row) if app == item.text() and fpath == item.fpath: break else: item = None if fpath: if item: self.list.setCurrentItem(item) elif app: icon = get_application_icon(fpath) item = QListWidgetItem(icon, app) item.fpath = fpath self.list.addItem(item) self.list.setCurrentItem(item) self.list.setFocus() self._refresh() def filter(self, text): """Filter the list of applications based on text.""" text = self.edit_filter.text().lower().strip() for row in range(self.list.count()): item = self.list.item(row) item.setHidden(text not in item.text().lower()) self._refresh() def set_extension(self, extension): """Set the extension on the label of the dialog.""" self.label.setText( _('Choose the application for files of type ') + extension) @property def application_path(self): """Return the selected application path to executable.""" item = self.list.currentItem() path = item.fpath if item else '' return path @property def application_name(self): """Return the selected application name.""" item = self.list.currentItem() text = item.text() if item else '' return text
def __init__(self): super().__init__() self.resize(400, 200) self.setWindowTitle("Load Run From Database") self._id_uid = None self._mode_id_uid = "id" label = QLabel("Enter run ID or UID:") self.le_id_uid = LineEditExtended() self.le_id_uid.textChanged.connect(self.le_id_uid_text_changed) self.le_id_uid.editingFinished.connect(self.le_id_uid_editing_finished) set_tooltip(self.le_id_uid, "Enter <b>Run ID</b> or <b>Run UID</b>.") self._validator_id = IntValidatorStrict() # Short UID example: "04c9afa7" self._validator_uid_short = QRegExpValidator(QRegExp(r"[0-9a-f]{8}")) # Full UID example: "04c9afa7-a43a-4af1-8e55-2034384d4a77" self._validator_uid_full = QRegExpValidator( QRegExp( r"[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}" )) self.rb_id = QRadioButton("Run ID") set_tooltip( self.rb_id, "The value in the line edit box is <b>Run ID</b> (e.g. <b>34235</b> or <b>-1</b>)" ) self.rb_id.setChecked(self._mode_id_uid == "id") self.rb_uid = QRadioButton("Run UID") self.rb_uid.setChecked(self._mode_id_uid == "uid") set_tooltip( self.rb_uid, "The value in the line edit box is <b>Run UID</b> " "(e.g. <b>04c9afb7-a43a-4af1-8e55-2034384d4a77</b> or <b>04c9afb7</b>)", ) self.btn_group = QButtonGroup() self.btn_group.addButton(self.rb_id) self.btn_group.addButton(self.rb_uid) self.btn_group.buttonToggled.connect(self.btn_group_button_toggled) button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) button_box.accepted.connect(self.accept) button_box.rejected.connect(self.reject) self.pb_ok = button_box.button(QDialogButtonBox.Ok) vbox = QVBoxLayout() vbox.addStretch(1) hbox = QHBoxLayout() hbox.addWidget(label) hbox.addStretch(1) vbox.addLayout(hbox) vbox.addWidget(self.le_id_uid) hbox = QHBoxLayout() hbox.addStretch(1) hbox.addWidget(self.rb_id) hbox.addStretch(1) hbox.addWidget(self.rb_uid) hbox.addStretch(1) vbox.addLayout(hbox) vbox.addStretch(1) vbox.addWidget(button_box) self.setLayout(vbox) # This is how the button from QDialogButtonBox can be disabled # button_box.button(QDialogButtonBox.Ok).setEnabled(False) self.le_id_uid.setText("") self._validate_id_uid()
class EpochDialog(QDialog): def __init__(self, parent, events): super().__init__(parent) self.setWindowTitle("Create Epochs") grid = QGridLayout(self) label = QLabel("Events:") label.setAlignment(Qt.AlignTop) grid.addWidget(label, 0, 0, 1, 1) self.events = QListWidget() self.events.insertItems(0, unique(events[:, 2]).astype(str)) self.events.setSelectionMode(QListWidget.ExtendedSelection) grid.addWidget(self.events, 0, 1, 1, 2) grid.addWidget(QLabel("Interval around events:"), 1, 0, 1, 1) self.tmin = QDoubleSpinBox() self.tmin.setMinimum(-10000) self.tmin.setValue(-0.2) self.tmin.setSingleStep(0.1) self.tmin.setAlignment(Qt.AlignRight) self.tmax = QDoubleSpinBox() self.tmax.setMinimum(-10000) self.tmax.setValue(0.5) self.tmax.setSingleStep(0.1) self.tmax.setAlignment(Qt.AlignRight) grid.addWidget(self.tmin, 1, 1, 1, 1) grid.addWidget(self.tmax, 1, 2, 1, 1) self.baseline = QCheckBox("Baseline Correction:") self.baseline.setChecked(True) self.baseline.stateChanged.connect(self.toggle_baseline) grid.addWidget(self.baseline, 2, 0, 1, 1) self.a = QDoubleSpinBox() self.a.setMinimum(-10000) self.a.setValue(-0.2) self.a.setSingleStep(0.1) self.a.setAlignment(Qt.AlignRight) self.b = QDoubleSpinBox() self.b.setMinimum(-10000) self.b.setValue(0) self.b.setSingleStep(0.1) self.b.setAlignment(Qt.AlignRight) grid.addWidget(self.a, 2, 1, 1, 1) grid.addWidget(self.b, 2, 2, 1, 1) self.buttonbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.buttonbox.accepted.connect(self.accept) self.buttonbox.rejected.connect(self.reject) grid.addWidget(self.buttonbox, 3, 0, 1, -1) self.events.itemSelectionChanged.connect(self.toggle_ok) self.toggle_ok() grid.setSizeConstraint(QGridLayout.SetFixedSize) @Slot() def toggle_ok(self): if self.events.selectedItems(): self.buttonbox.button(QDialogButtonBox.Ok).setEnabled(True) else: self.buttonbox.button(QDialogButtonBox.Ok).setEnabled(False) @Slot() def toggle_baseline(self): if self.baseline.isChecked(): self.a.setEnabled(True) self.b.setEnabled(True) else: self.a.setEnabled(False) self.b.setEnabled(False)
class PathManager(QDialog): """Path manager dialog.""" redirect_stdio = Signal(bool) sig_path_changed = Signal(object) def __init__(self, parent, path=None, read_only_path=None, not_active_path=None, sync=True): """Path manager dialog.""" super(PathManager, self).__init__(parent) assert isinstance(path, (tuple, None)) self.path = path or () self.read_only_path = read_only_path or () self.not_active_path = not_active_path or () self.last_path = getcwd_or_home() self.original_path_dict = None # Widgets self.add_button = None self.remove_button = None self.movetop_button = None self.moveup_button = None self.movedown_button = None self.movebottom_button = None self.sync_button = None self.selection_widgets = [] self.top_toolbar_widgets = self._setup_top_toolbar() self.bottom_toolbar_widgets = self._setup_bottom_toolbar() self.listwidget = QListWidget(self) self.bbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.button_ok = self.bbox.button(QDialogButtonBox.Ok) # Widget setup # Destroying the C++ object right after closing the dialog box, # otherwise it may be garbage-collected in another QThread # (e.g. the editor's analysis thread in Spyder), thus leading to # a segmentation fault on UNIX or an application crash on Windows self.setAttribute(Qt.WA_DeleteOnClose) self.setWindowTitle(_("PYTHONPATH manager")) self.setWindowIcon(ima.icon('pythonpath')) self.resize(500, 300) self.sync_button.setVisible(os.name == 'nt' and sync) # Layouts top_layout = QHBoxLayout() self._add_widgets_to_layout(self.top_toolbar_widgets, top_layout) bottom_layout = QHBoxLayout() self._add_widgets_to_layout(self.bottom_toolbar_widgets, bottom_layout) bottom_layout.addWidget(self.bbox) layout = QVBoxLayout() layout.addLayout(top_layout) layout.addWidget(self.listwidget) layout.addLayout(bottom_layout) self.setLayout(layout) # Signals self.listwidget.currentRowChanged.connect(lambda x: self.refresh()) self.listwidget.itemChanged.connect(lambda x: self.refresh()) self.bbox.accepted.connect(self.accept) self.bbox.rejected.connect(self.reject) # Setup self.setup() def _add_widgets_to_layout(self, widgets, layout): """Helper to add toolbar widgets to top and bottom layout.""" layout.setAlignment(Qt.AlignLeft) for widget in widgets: if widget is None: layout.addStretch(1) else: layout.addWidget(widget) def _setup_top_toolbar(self): """Create top toolbar and actions.""" self.movetop_button = create_toolbutton( self, text=_("Move to top"), icon=ima.icon('2uparrow'), triggered=lambda: self.move_to(absolute=0), text_beside_icon=True) self.moveup_button = create_toolbutton( self, text=_("Move up"), icon=ima.icon('1uparrow'), triggered=lambda: self.move_to(relative=-1), text_beside_icon=True) self.movedown_button = create_toolbutton( self, text=_("Move down"), icon=ima.icon('1downarrow'), triggered=lambda: self.move_to(relative=1), text_beside_icon=True) self.movebottom_button = create_toolbutton( self, text=_("Move to bottom"), icon=ima.icon('2downarrow'), triggered=lambda: self.move_to(absolute=1), text_beside_icon=True) toolbar = [ self.movetop_button, self.moveup_button, self.movedown_button, self.movebottom_button ] self.selection_widgets.extend(toolbar) return toolbar def _setup_bottom_toolbar(self): """Create bottom toolbar and actions.""" self.add_button = create_toolbutton( self, text=_('Add path'), icon=ima.icon('edit_add'), triggered=lambda x: self.add_path(), text_beside_icon=True) self.remove_button = create_toolbutton( self, text=_('Remove path'), icon=ima.icon('edit_remove'), triggered=lambda x: self.remove_path(), text_beside_icon=True) self.sync_button = create_toolbutton( self, text=_("Synchronize..."), icon=ima.icon('fileimport'), triggered=self.synchronize, tip=_("Synchronize Spyder's path list with PYTHONPATH " "environment variable"), text_beside_icon=True) self.selection_widgets.append(self.remove_button) return [self.add_button, self.remove_button, None, self.sync_button] def _create_item(self, path): """Helper to create a new list item.""" item = QListWidgetItem(path) item.setIcon(ima.icon('DirClosedIcon')) if path in self.read_only_path: item.setFlags(Qt.NoItemFlags | Qt.ItemIsUserCheckable) item.setCheckState(Qt.Checked) elif path in self.not_active_path: item.setFlags(item.flags() | Qt.ItemIsUserCheckable) item.setCheckState(Qt.Unchecked) else: item.setFlags(item.flags() | Qt.ItemIsUserCheckable) item.setCheckState(Qt.Checked) return item @property def editable_bottom_row(self): """Maximum bottom row count that is editable.""" read_only_count = len(self.read_only_path) if read_only_count == 0: max_row = self.listwidget.count() - 1 else: max_row = self.listwidget.count() - read_only_count - 1 return max_row def setup(self): """Populate list widget.""" self.listwidget.clear() for path in self.path + self.read_only_path: item = self._create_item(path) self.listwidget.addItem(item) self.listwidget.setCurrentRow(0) self.original_path_dict = self.get_path_dict() self.refresh() @Slot() def synchronize(self): """ Synchronize Spyder's path list with PYTHONPATH environment variable Only apply to: current user, on Windows platforms. """ answer = QMessageBox.question( self, _("Synchronize"), _("This will synchronize Spyder's path list with " "<b>PYTHONPATH</b> environment variable for the current user, " "allowing you to run your Python modules outside Spyder " "without having to configure sys.path. " "<br>" "Do you want to clear contents of PYTHONPATH before " "adding Spyder's path list?"), QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel) if answer == QMessageBox.Cancel: return elif answer == QMessageBox.Yes: remove = True else: remove = False from spyder.utils.environ import (get_user_env, listdict2envdict, set_user_env) env = get_user_env() # Includes read only paths active_path = tuple(k for k, v in self.get_path_dict(True).items() if v) if remove: ppath = active_path else: ppath = env.get('PYTHONPATH', []) if not isinstance(ppath, list): ppath = [ppath] ppath = tuple(p for p in ppath if p not in active_path) ppath = ppath + active_path env['PYTHONPATH'] = list(ppath) set_user_env(listdict2envdict(env), parent=self) def get_path_dict(self, read_only=False): """ Return an ordered dict with the path entries as keys and the active state as the value. If `read_only` is True, the read_only entries are also included. `read_only` entry refers to the project path entry. """ odict = OrderedDict() for row in range(self.listwidget.count()): item = self.listwidget.item(row) path = item.text() if path in self.read_only_path and not read_only: continue odict[path] = item.checkState() == Qt.Checked return odict def refresh(self): """Refresh toolbar widgets.""" enabled = self.listwidget.currentItem() is not None for widget in self.selection_widgets: widget.setEnabled(enabled) # Disable buttons based on row row = self.listwidget.currentRow() disable_widgets = [] # Move up/top disabled for top item if row == 0: disable_widgets.extend([self.movetop_button, self.moveup_button]) # Move down/bottom disabled for bottom item if row == self.editable_bottom_row: disable_widgets.extend( [self.movebottom_button, self.movedown_button]) for widget in disable_widgets: widget.setEnabled(False) self.sync_button.setEnabled(self.listwidget.count() > 0) # Ok button only enabled if actual changes occur self.button_ok.setEnabled( self.original_path_dict != self.get_path_dict()) def check_path(self, path): """Check that the path is not a [site|dist]-packages folder.""" if os.name == 'nt': pat = re.compile(r'.*lib/(?:site|dist)-packages.*') else: pat = re.compile(r'.*lib/python.../(?:site|dist)-packages.*') path_norm = path.replace('\\', '/') return pat.match(path_norm) is None @Slot() def add_path(self, directory=None): """ Add path to list widget. If `directory` is provided, the folder dialog is overridden. """ if directory is None: self.redirect_stdio.emit(False) directory = getexistingdirectory(self, _("Select directory"), self.last_path) self.redirect_stdio.emit(True) if PY2: is_unicode = False try: directory.decode('ascii') except (UnicodeEncodeError, UnicodeDecodeError): is_unicode = True if is_unicode: QMessageBox.warning( self, _("Add path"), _("You are using Python 2 and the selected path has " "Unicode characters." "<br> " "Therefore, this path will not be added."), QMessageBox.Ok) return directory = osp.abspath(directory) self.last_path = directory if directory in self.get_path_dict(): item = self.listwidget.findItems(directory, Qt.MatchExactly)[0] item.setCheckState(Qt.Checked) answer = QMessageBox.question( self, _("Add path"), _("This directory is already included in the list." "<br> " "Do you want to move it to the top of it?"), QMessageBox.Yes | QMessageBox.No) if answer == QMessageBox.Yes: item = self.listwidget.takeItem(self.listwidget.row(item)) self.listwidget.insertItem(0, item) self.listwidget.setCurrentRow(0) else: if self.check_path(directory): item = self._create_item(directory) self.listwidget.insertItem(0, item) self.listwidget.setCurrentRow(0) else: answer = QMessageBox.warning( self, _("Add path"), _("This directory cannot be added to the path!" "<br><br>" "If you want to set a different Python interpreter, " "please go to <tt>Preferences > Main interpreter</tt>" "."), QMessageBox.Ok) self.refresh() @Slot() def remove_path(self, force=False): """ Remove path from list widget. If `force` is True, the message box is overridden. """ if self.listwidget.currentItem(): if not force: answer = QMessageBox.warning( self, _("Remove path"), _("Do you really want to remove the selected path?"), QMessageBox.Yes | QMessageBox.No) if force or answer == QMessageBox.Yes: self.listwidget.takeItem(self.listwidget.currentRow()) self.refresh() def move_to(self, absolute=None, relative=None): """Move items of list widget.""" index = self.listwidget.currentRow() if absolute is not None: if absolute: new_index = self.listwidget.count() - 1 else: new_index = 0 else: new_index = index + relative new_index = max(0, min(self.editable_bottom_row, new_index)) item = self.listwidget.takeItem(index) self.listwidget.insertItem(new_index, item) self.listwidget.setCurrentRow(new_index) self.refresh() def current_row(self): """Returns the current row of the list.""" return self.listwidget.currentRow() def set_current_row(self, row): """Set the current row of the list.""" self.listwidget.setCurrentRow(row) def row_check_state(self, row): """Return the checked state for item in row.""" item = self.listwidget.item(row) return item.checkState() def set_row_check_state(self, row, value): """Set the current checked state for item in row.""" item = self.listwidget.item(row) item.setCheckState(value) def count(self): """Return the number of items.""" return self.listwidget.count() def accept(self): """Override Qt method.""" path_dict = self.get_path_dict() if self.original_path_dict != path_dict: self.sig_path_changed.emit(path_dict) super(PathManager, self).accept()
class DialogLoadMask(QDialog): """ Dialog box for selecting spatial ROI and mask. Typical use: default_dir = "/home/user/data" n_rows, n_columns = 15, 20 # Some values # Values that are changed by the dialog box roi = (2, 3, 11, 9) use_roi = True mask_f_path = "" use_mask = False dlg = DialogLoadMask() dlg.set_image_size(n_rows=n_rows, n_columns=n_columns) dlg.set_roi(row_start=roi[0], column_start=roi[1], row_end=roi[2], column_end=roi[3]) dlg.set_roi_active(use_roi) dlg.set_default_directory(default_dir) dlg.set_mask_file_path(mask_f_path) dlg.set_mask_file_active(use_mask) if dlg.exec() == QDialog.Accepted: # If success, then read the values back. Discard changes if rejected. roi = dlg.get_roi() use_roi = dlg.get_roi_active() mask_f_path = dlg.get_mask_file_path() use_mask = dlg.get_mask_file_active() """ def __init__(self): super().__init__() self._validation_enabled = False self.resize(500, 300) self.setWindowTitle("Load Mask or Select ROI") # Initialize variables used with ROI selection group self._n_rows = 0 self._n_columns = 0 self._roi_active = False self._row_start = -1 self._column_start = -1 self._row_end = -1 self._column_end = -1 # ... with Mask group self._mask_active = False self._mask_file_path = "" self._default_directory = "" # Fields for entering spatial ROI coordinates self.validator_rows = QIntValidator() self.validator_rows.setBottom(1) self.validator_cols = QIntValidator() self.validator_cols.setBottom(1) self.le_roi_start_row = LineEditExtended() self.le_roi_start_row.setValidator(self.validator_rows) self.le_roi_start_row.editingFinished.connect( self.le_roi_start_row_editing_finished) self.le_roi_start_row.textChanged.connect( self.le_roi_start_row_text_changed) self.le_roi_start_col = LineEditExtended() self.le_roi_start_col.setValidator(self.validator_cols) self.le_roi_start_col.editingFinished.connect( self.le_roi_start_col_editing_finished) self.le_roi_start_col.textChanged.connect( self.le_roi_start_col_text_changed) self.le_roi_end_row = LineEditExtended() self.le_roi_end_row.setValidator(self.validator_rows) self.le_roi_end_row.editingFinished.connect( self.le_roi_end_row_editing_finished) self.le_roi_end_row.textChanged.connect( self.le_roi_end_row_text_changed) self.le_roi_end_col = LineEditExtended() self.le_roi_end_col.setValidator(self.validator_cols) self.le_roi_end_col.editingFinished.connect( self.le_roi_end_col_editing_finished) self.le_roi_end_col.textChanged.connect( self.le_roi_end_col_text_changed) self._text_map_size_base = " * Map size: " self.label_map_size = QLabel(self._text_map_size_base + "not set") # Group box for spatial ROI selection self.gb_roi = QGroupBox("Select ROI (in pixels)") set_tooltip( self.gb_roi, "Select rectangular <b>spatial ROI</b>. If <b>mask</b> is " "loaded, then ROI is applied to the masked data.", ) self.gb_roi.setCheckable(True) self.gb_roi.toggled.connect(self.gb_roi_toggled) self.gb_roi.setChecked(self._roi_active) vbox = QVBoxLayout() grid = QGridLayout() grid.addWidget(QLabel("Start position(*):"), 0, 0) grid.addWidget(QLabel("row"), 0, 1) grid.addWidget(self.le_roi_start_row, 0, 2) grid.addWidget(QLabel("column"), 0, 3) grid.addWidget(self.le_roi_start_col, 0, 4) grid.addWidget(QLabel("End position(*):"), 1, 0) grid.addWidget(QLabel("row"), 1, 1) grid.addWidget(self.le_roi_end_row, 1, 2) grid.addWidget(QLabel("column"), 1, 3) grid.addWidget(self.le_roi_end_col, 1, 4) vbox.addLayout(grid) vbox.addWidget(self.label_map_size) self.gb_roi.setLayout(vbox) # Widgets for loading mask self.pb_load_mask = QPushButton("Load Mask ...") self.pb_load_mask.clicked.connect(self.pb_load_mask_clicked) self._le_load_mask_default_text = "select 'mask' file" self.le_load_mask = LineEditReadOnly(self._le_load_mask_default_text) # Group box for setting mask self.gb_mask = QGroupBox("Set mask") set_tooltip( self.gb_mask, "Load <b>mask</b> from file. Active pixels in the mask are " "represented by positive integers. If <b>spatial ROI</b> is " "selected, then it is applied to the masked data.", ) self.gb_mask.setCheckable(True) self.gb_mask.toggled.connect(self.gb_mask_toggled) self.gb_mask.setChecked(self._mask_active) hbox = QHBoxLayout() hbox.addWidget(self.pb_load_mask) hbox.addWidget(self.le_load_mask) self.gb_mask.setLayout(hbox) # Yes/No button box self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.button_ok = self.button_box.button(QDialogButtonBox.Ok) self.button_box.accepted.connect(self.accept) self.button_box.rejected.connect(self.reject) vbox = QVBoxLayout() vbox.addWidget(self.gb_roi) vbox.addStretch(1) vbox.addWidget(self.gb_mask) vbox.addStretch(2) vbox.addWidget(self.button_box) self.setLayout(vbox) self._validation_enabled = True self._validate_all_widgets() def _compute_home_directory(self): dir_name = "." if self._default_directory: dir_name = self._default_directory if self._mask_file_path: d, _ = os.path.split(self._mask_file_path) dir_name = d if d else dir_name return dir_name def pb_load_mask_clicked(self): dir_name = self._compute_home_directory() file_name = QFileDialog.getOpenFileName(self, "Load Mask From File", dir_name, "All (*)") file_name = file_name[0] if file_name: self._mask_file_path = file_name self._show_mask_file_path() logger.debug(f"Mask file is selected: '{file_name}'") def gb_roi_toggled(self, state): self._roi_activate(state) def _read_le_roi_value(self, line_edit, v_default): """ Attempt to read value from line edit box as int, return `v_default` if not successful. Parameters ---------- line_edit: QLineEdit reference to QLineEdit object v_default: int default value returned if the value read from edit box is incorrect """ try: val = int(line_edit.text()) if val < 1: raise Exception() except Exception: val = v_default return val def le_roi_start_row_editing_finished(self): self._row_start = self._read_le_roi_value(self.le_roi_start_row, self._row_start + 1) - 1 def le_roi_end_row_editing_finished(self): self._row_end = self._read_le_roi_value(self.le_roi_end_row, self._row_end) def le_roi_start_col_editing_finished(self): self._column_start = self._read_le_roi_value( self.le_roi_start_col, self._column_start + 1) - 1 def le_roi_end_col_editing_finished(self): self._column_end = self._read_le_roi_value(self.le_roi_end_col, self._column_end) def le_roi_start_row_text_changed(self): self._validate_all_widgets() def le_roi_end_row_text_changed(self): self._validate_all_widgets() def le_roi_start_col_text_changed(self): self._validate_all_widgets() def le_roi_end_col_text_changed(self): self._validate_all_widgets() def gb_mask_toggled(self, state): self._mask_file_activate(state) def _validate_all_widgets(self): """ Validate the values and state of all widgets, update the 'valid' state of the widgets and enable/disable Ok button. """ if not self._validation_enabled: return # Check if all fields have valid input values def _check_valid_input(line_edit, flag_valid): val = self._read_le_roi_value(line_edit, -1) state = val > 0 line_edit.setValid(state) flag_valid = flag_valid if state else False return val, flag_valid # Set all line edits to 'valid' state self.le_roi_start_row.setValid(True) self.le_roi_end_row.setValid(True) self.le_roi_start_col.setValid(True) self.le_roi_end_col.setValid(True) self.le_load_mask.setValid(True) flag_valid = True if self._roi_active: # Perform the following checks only if ROI group is active rs, flag_valid = _check_valid_input(self.le_roi_start_row, flag_valid) re, flag_valid = _check_valid_input(self.le_roi_end_row, flag_valid) cs, flag_valid = _check_valid_input(self.le_roi_start_col, flag_valid) ce, flag_valid = _check_valid_input(self.le_roi_end_col, flag_valid) # Check if start if (rs > 0) and (re > 0) and (rs > re): self.le_roi_start_row.setValid(False) self.le_roi_end_row.setValid(False) flag_valid = False if (cs > 0) and (ce > 0) and (cs > ce): self.le_roi_start_col.setValid(False) self.le_roi_end_col.setValid(False) flag_valid = False if self._mask_active: if not self._mask_file_path: self.le_load_mask.setValid(False) flag_valid = False self.button_ok.setEnabled(flag_valid) def set_image_size(self, *, n_rows, n_columns): """ Set the image size. Image size is used to for input validation. When image size is set, the selection is reset to cover the whole image. Parameters ---------- n_rows: int The number of rows in the image: (1..) n_columns: int The number of columns in the image: (1..) """ if n_rows < 1 or n_columns < 1: raise ValueError( "DialogLoadMask: image size have zero rows or zero columns: " f"n_rows={n_rows} n_columns={n_columns}. " "Report the error to the development team.") self._n_rows = n_rows self._n_columns = n_columns self._row_start = 0 self._row_end = n_rows self._column_start = 0 self._column_end = n_columns self._show_selection(True) self._validate_all_widgets() self.validator_rows.setTop(n_rows) self.validator_cols.setTop(n_columns) # Set label self.label_map_size.setText( f"{self._text_map_size_base}{self._n_rows} rows, {self._n_columns} columns." ) def _show_selection(self, visible): """visible: True - show values, False - hide values""" def _show_number(l_edit, value): if value > 0: l_edit.setText(f"{value}") else: # This would typically mean incorrect initialization of the dialog box l_edit.setText("") _show_number(self.le_roi_start_row, self._row_start + 1) _show_number(self.le_roi_start_col, self._column_start + 1) _show_number(self.le_roi_end_row, self._row_end) _show_number(self.le_roi_end_col, self._column_end) def set_roi(self, *, row_start, column_start, row_end, column_end): """ Set the values of fields that define selection of the image region. First set the image size (`set_image_size()`) and then set the selection. Parameters ---------- row_start: int The row number of the first pixel in the selection (0..n_rows-1). Negative (-1) - resets value to 0. column_start: int The column number of the first pixel in the selection (0..n_columns-1). Negative (-1) - resets value to 0. row_end: int The row number following the last pixel in the selection (1..n_rows). This row is not included in the selection. Negative (-1) - resets value to n_rows. column_end: int The column number following the last pixel in the selection (1..n_columns). This column is not included in the selection. Negative (-1) - resets value to n_columns. """ # The variables holding the selected region are following Python conventions for the # selections: row_start, column_start are in the range 0..n_rows-1, 0..n_columns-1 # and row_end, column_end are in the range 1..n_rows, 1..n_columns. # The values displayed in the dialog box are just pixels numbers in the range # 1..n_rows, 1..n_columns that define the rectangle in the way intuitive to the user. self._row_start = int( np.clip(row_start, a_min=0, a_max=self._n_rows - 1)) self._column_start = int( np.clip(column_start, a_min=0, a_max=self._n_columns - 1)) def _adjust_last_index(index, n_elements): if index < 0: index = n_elements index = int(np.clip(index, 1, n_elements)) return index self._row_end = _adjust_last_index(row_end, self._n_rows) self._column_end = _adjust_last_index(column_end, self._n_columns) self._show_selection(self._roi_active) self._validate_all_widgets() def _roi_activate(self, state): self._roi_active = state self._show_selection(state) self._validate_all_widgets() def set_roi_active(self, state): self._roi_activate(state) self.gb_roi.setChecked(self._roi_active) def get_roi(self): return self._row_start, self._column_start, self._row_end, self._column_end def get_roi_active(self): return self._roi_active def _show_mask_file_path(self): fpath = self._mask_file_path if self._mask_file_path else self._le_load_mask_default_text self.le_load_mask.setText(fpath) self._validate_all_widgets() def _mask_file_activate(self, state): self._mask_active = state self._show_mask_file_path() self._validate_all_widgets() def set_mask_file_active(self, state): self._mask_file_activate(state) self.gb_mask.setChecked(self._mask_active) def get_mask_file_active(self): return self._mask_active def set_mask_file_path(self, fpath): self._mask_file_path = fpath self._show_mask_file_path() def get_mask_file_path(self): return self._mask_file_path def set_default_directory(self, dir_name): self._default_directory = dir_name def get_default_directory(self): return self._default_directory
class HeaderEditDialog(QDialog): # name of the current preset; whether to set this preset as default; list of Columns header_changed = Signal(str, bool, list) def __init__(self, parent, table_header): super().__init__(parent) self.table_header = table_header self.default_preset_name = None self.preset_name = table_header.preset_name self.columns = deepcopy(table_header.columns) self.setupUi() self.update_output() def setupUi(self): self.resize(240, 400) self.vbox = QVBoxLayout(self) self.presetLabel = QLabel(self) self.columnList = QListWidget(self) self.setAsDefaultCheckbox = QCheckBox("Set as default preset", self) self.vbox.addWidget(self.presetLabel) self.vbox.addWidget(self.columnList) self.vbox.addWidget(self.setAsDefaultCheckbox) self.columnList.setDragDropMode(QListWidget.InternalMove) self.columnList.setDefaultDropAction(Qt.MoveAction) self.columnList.setSelectionMode(QListWidget.ExtendedSelection) self.columnList.setAlternatingRowColors(True) self.columnList.installEventFilter(self) self.columnList.setContextMenuPolicy(Qt.CustomContextMenu) self.columnList.customContextMenuRequested.connect(self.open_menu) self.columnList.model().rowsMoved.connect(self.read_columns_from_list) # for a dumb qss hack to make selected checkboxes not white on a light theme self.columnList.setObjectName("ColumnList") buttons = QDialogButtonBox.Reset | QDialogButtonBox.Save | QDialogButtonBox.Cancel self.buttonBox = QDialogButtonBox(buttons, self) self.vbox.addWidget(self.buttonBox) self.buttonBox.accepted.connect(self.accept) self.buttonBox.rejected.connect(self.reject) self.resetButton = self.buttonBox.button(QDialogButtonBox.Reset) self.resetButton.clicked.connect(self.reset_to_stock) def eventFilter(self, object, event): if event.type() == QEvent.KeyPress: if event.key() == Qt.Key_Space or event.key() == Qt.Key_Return: self.toggle_selected_columns() return True if event.key() == Qt.Key_Delete: self.delete_selected() return True return False def update_output(self): self.presetLabel.setText("Preset: {}".format(self.preset_name)) self.setAsDefaultCheckbox.setChecked( CONFIG['default_header_preset'] == self.preset_name) self.columnList.clear() for column in self.columns: ColumnListItem(self.columnList, column) def accept(self): self.read_columns_from_list() self.header_changed.emit(self.preset_name, self.setAsDefaultCheckbox.isChecked(), self.columns) self.done(0) def reject(self): self.done(0) def reset_to_stock(self): self.columns = deepcopy(DEFAULT_COLUMNS) self.update_output() def read_columns_from_list(self): new_columns = [] for i in range(self.columnList.count()): item = self.columnList.item(i) new_columns.append(item.column) self.columns = new_columns def toggle_selected_columns(self): selected = self.columnList.selectedItems() for item in selected: value_now = item.data(Qt.CheckStateRole) item.setData(Qt.CheckStateRole, not value_now) self.columnList.reset( ) # @Improvement: is there a better way to update QListWidget? def open_menu(self, position): menu = QMenu(self) preset_menu = menu.addMenu('Presets') preset_menu.addAction('New preset', self.new_preset_dialog) preset_menu.addSeparator() preset_names = CONFIG.get_header_presets() if len(preset_names) == 0: action = preset_menu.addAction('No presets') action.setEnabled(False) else: delete_menu = menu.addMenu('Delete preset') for name in preset_names: preset_menu.addAction(name, partial(self.load_preset, name)) delete_menu.addAction(name, partial(self.delete_preset, name)) menu.addSeparator() menu.addAction('New column...', self.create_new_column_dialog) if len(self.columnList.selectedIndexes()) > 0: menu.addAction('Delete selected', self.delete_selected) menu.popup(self.columnList.viewport().mapToGlobal(position)) def load_preset(self, name): new_columns = CONFIG.load_header_preset(name) if not new_columns: return self.columns = new_columns self.preset_name = name self.update_output() def new_preset_dialog(self): d = QInputDialog(self) d.setLabelText('Enter the new name for the new preset:') d.setWindowTitle('Create new preset') d.textValueSelected.connect(self.create_new_preset) d.open() def create_new_preset(self, name): if name in CONFIG.get_header_presets(): show_warning_dialog( self, "Preset creation error", 'Preset named "{}" already exists.'.format(name)) return if len(name.strip()) == 0: show_warning_dialog( self, "Preset creation error", 'This preset name is not allowed.'.format(name)) return self.preset_name = name self.update_output() CONFIG.save_header_preset(name, self.columns) def delete_preset(self, name): CONFIG.delete_header_preset(name) if name == self.preset_name: self.columns = deepcopy(DEFAULT_COLUMNS) self.update_output() def create_new_column_dialog(self): d = CreateNewColumnDialog(self) d.add_new_column.connect(self.add_new_column) d.setWindowTitle('Create new column') d.open() def add_new_column(self, name, title): new_column = Column(name, title) # if the last column is message, insert this column before it (I think that makes sense?) if len(self.columns) == 0: self.columns.append(new_column) elif self.columns[-1].name in ('message', 'msg'): self.columns.insert(-1, new_column) else: self.columns.append(new_column) self.update_output() def delete_selected(self): selected = self.columnList.selectedItems() for item in selected: self.columnList.takeItem(self.columnList.row(item)) self.read_columns_from_list() self.update_output()