class LogWidget(QWidget): def __init__(self, parent=None, thread=None, iconsize=32): QWidget.__init__(self, parent=parent) self.log = QListWidget() self.log.setSortingEnabled(True) layout = QVBoxLayout(self) layout.addWidget(self.log) self.iconsize = iconsize self.thread = thread assert (thread is not None) self.thread.update.connect(self.update_progress) self.thread.start() def update_progress(self, progress): if not progress: return title, description, date, icon = progress entry = EntryWidget() entry.setTitle(title) entry.setDescription(description) entry.setDate(date) entry.setIcon(QIcon.fromTheme(icon)) item = QListWidgetItem(self.log) item.setSizeHint(entry.sizeHint()) self.log.insertItem(0, item) self.log.setItemWidget(item, entry)
def insertItem(self, row, item): """Override Qt method.""" QListWidget.insertItem(self, row, item) self._items = self._items[:row] + [item] + self._items[row:] self.setItemWidget(item, item.widget) item.setSizeHint(item.widget_size()) self.setup_item(item) if self._current_item is None: self._current_item = item self.setCurrentRow(0) else: item.set_selected(False) if self.style_sheet: item.widget.setStyleSheet(self.style_sheet)
class SidebarWidget(QWidget): def __init__(self, parent=None, thread=None, iconsize=32): QWidget.__init__(self, parent=parent) self.list = QListWidget() self.list.setAttribute(Qt.WA_TranslucentBackground) self.list.setSortingEnabled(True) layout = QVBoxLayout(self) layout.addWidget(self.list) self.iconsize = iconsize def addEntry(self, title, description, icon): entry = EntryWidget() entry.setTitle(title) entry.setDescription(description) entry.setDate("") entry.setIcon(QIcon.fromTheme(icon)) item = QListWidgetItem(self.list) item.setSizeHint(entry.sizeHint()) self.list.insertItem(0, item) self.list.setItemWidget(item, entry) return self.list.count()
class MeasurementSettings(QWidget): """ :type settings: Settings """ def __init__(self, settings: PartSettings, parent=None): super().__init__(parent) self.chosen_element: Optional[MeasurementListWidgetItem] = None self.chosen_element_area: Optional[Tuple[AreaType, float]] = None self.settings = settings self.profile_list = QListWidget(self) self.profile_description = QTextEdit(self) self.profile_description.setReadOnly(True) self.profile_options = QListWidget() self.profile_options_chosen = QListWidget() self.measurement_area_choose = QEnumComboBox(enum_class=AreaType) self.per_component = QEnumComboBox(enum_class=PerComponent) self.power_num = QDoubleSpinBox() self.power_num.setDecimals(3) self.power_num.setRange(-100, 100) self.power_num.setValue(1) self.choose_butt = QPushButton("→", self) self.discard_butt = QPushButton("←", self) self.proportion_butt = QPushButton("Ratio", self) self.proportion_butt.setToolTip("Create proportion from two parameter") self.move_up = QPushButton("↑", self) self.move_down = QPushButton("↓", self) self.remove_button = QPushButton("Remove") self.save_butt = QPushButton("Save") self.save_butt.setToolTip( "Set name for set and choose at least one parameter") self.save_butt_with_name = QPushButton( "Save with custom parameters designation") self.save_butt_with_name.setToolTip( "Set name for set and choose at least one parameter") self.reset_butt = QPushButton("Clear") self.soft_reset_butt = QPushButton("Remove user parameters") self.profile_name = QLineEdit(self) self.delete_profile_butt = QPushButton("Delete ") self.export_profiles_butt = QPushButton("Export") self.import_profiles_butt = QPushButton("Import") self.edit_profile_butt = QPushButton("Edit") self.choose_butt.setDisabled(True) self.choose_butt.clicked.connect(self.choose_option) self.discard_butt.setDisabled(True) self.discard_butt.clicked.connect(self.discard_option) self.proportion_butt.setDisabled(True) self.proportion_butt.clicked.connect(self.proportion_action) self.save_butt.setDisabled(True) self.save_butt.clicked.connect(self.save_action) self.save_butt_with_name.setDisabled(True) self.save_butt_with_name.clicked.connect(self.named_save_action) self.profile_name.textChanged.connect(self.name_changed) self.move_down.setDisabled(True) self.move_down.clicked.connect(self.move_down_fun) self.move_up.setDisabled(True) self.move_up.clicked.connect(self.move_up_fun) self.remove_button.setDisabled(True) self.remove_button.clicked.connect(self.remove_element) self.reset_butt.clicked.connect(self.reset_action) self.soft_reset_butt.clicked.connect(self.soft_reset) self.delete_profile_butt.setDisabled(True) self.delete_profile_butt.clicked.connect(self.delete_profile) self.export_profiles_butt.clicked.connect( self.export_measurement_profiles) self.import_profiles_butt.clicked.connect( self.import_measurement_profiles) self.edit_profile_butt.clicked.connect(self.edit_profile) self.profile_list.itemSelectionChanged.connect(self.profile_chosen) self.profile_options.itemSelectionChanged.connect( self.create_selection_changed) self.profile_options_chosen.itemSelectionChanged.connect( self.create_selection_chosen_changed) self.settings.measurement_profiles_changed.connect( self._refresh_profiles) layout = QVBoxLayout() layout.addWidget(QLabel("Measurement set:")) profile_layout = QHBoxLayout() profile_layout.addWidget(self.profile_list) profile_layout.addWidget(self.profile_description) profile_buttons_layout = QHBoxLayout() profile_buttons_layout.addWidget(self.delete_profile_butt) profile_buttons_layout.addWidget(self.export_profiles_butt) profile_buttons_layout.addWidget(self.import_profiles_butt) profile_buttons_layout.addWidget(self.edit_profile_butt) profile_buttons_layout.addStretch() layout.addLayout(profile_layout) layout.addLayout(profile_buttons_layout) heading_layout = QHBoxLayout() # heading_layout.addWidget(QLabel("Create profile"), 1) heading_layout.addWidget(h_line(), 6) layout.addLayout(heading_layout) name_layout = QHBoxLayout() name_layout.addWidget(QLabel("Set name:")) name_layout.addWidget(self.profile_name) name_layout.addStretch() name_layout.addWidget(QLabel("Per component:")) name_layout.addWidget(self.per_component) name_layout.addWidget(QLabel("Area:")) name_layout.addWidget(self.measurement_area_choose) name_layout.addWidget(QLabel("to power:")) name_layout.addWidget(self.power_num) layout.addLayout(name_layout) create_layout = QHBoxLayout() create_layout.addWidget(self.profile_options) butt_op_layout = QVBoxLayout() butt_op_layout.addStretch() butt_op_layout.addWidget(self.choose_butt) butt_op_layout.addWidget(self.discard_butt) butt_op_layout.addWidget(self.proportion_butt) butt_op_layout.addWidget(self.reset_butt) butt_op_layout.addStretch() create_layout.addLayout(butt_op_layout) create_layout.addWidget(self.profile_options_chosen) butt_move_layout = QVBoxLayout() butt_move_layout.addStretch() butt_move_layout.addWidget(self.move_up) butt_move_layout.addWidget(self.move_down) butt_move_layout.addWidget(self.remove_button) butt_move_layout.addStretch() create_layout.addLayout(butt_move_layout) layout.addLayout(create_layout) save_butt_layout = QHBoxLayout() save_butt_layout.addWidget(self.soft_reset_butt) save_butt_layout.addStretch() save_butt_layout.addWidget(self.save_butt) save_butt_layout.addWidget(self.save_butt_with_name) layout.addLayout(save_butt_layout) self.setLayout(layout) for profile in MEASUREMENT_DICT.values(): help_text = profile.get_description() lw = MeasurementListWidgetItem(profile.get_starting_leaf()) lw.setToolTip(help_text) self.profile_options.addItem(lw) self._refresh_profiles() def _refresh_profiles(self): item = self.profile_list.currentItem() items = list(self.settings.measurement_profiles.keys()) try: index = items.index(item.text()) except (ValueError, AttributeError): index = -1 self.profile_list.clear() self.profile_list.addItems(items) self.profile_list.setCurrentRow(index) if self.profile_list.count() == 0: self.export_profiles_butt.setDisabled(True) else: self.export_profiles_butt.setEnabled(True) def remove_element(self): elem = self.profile_options_chosen.currentItem() if elem is None: return index = self.profile_options_chosen.currentRow() self.profile_options_chosen.takeItem(index) if self.profile_options_chosen.count() == 0: self.move_down.setDisabled(True) self.move_up.setDisabled(True) self.remove_button.setDisabled(True) self.discard_butt.setDisabled(True) self.save_butt.setDisabled(True) self.save_butt_with_name.setDisabled(True) def delete_profile(self): item = self.profile_list.currentItem() del self.settings.measurement_profiles[str(item.text())] def profile_chosen(self): self.delete_profile_butt.setEnabled(True) if self.profile_list.count() == 0: self.profile_description.setText("") return item = self.profile_list.currentItem() if item is None: self.profile_description.setText("") return profile = self.settings.measurement_profiles[item.text()] self.profile_description.setText(str(profile)) def create_selection_changed(self): self.choose_butt.setEnabled(True) self.proportion_butt.setEnabled(True) def proportion_action(self): # TODO use get_parameters if self.chosen_element is None: item = self.profile_options.currentItem() self.chosen_element_area = self.get_parameters( deepcopy(item.stat), self.measurement_area_choose.currentEnum(), self.per_component.currentEnum(), self.power_num.value(), ) if self.chosen_element_area is None: return self.chosen_element = item item.setIcon(QIcon(os.path.join(icons_dir, "task-accepted.png"))) elif (self.profile_options.currentItem() == self.chosen_element and self.measurement_area_choose.currentEnum() == self.chosen_element_area.area and self.per_component.currentEnum() == self.chosen_element_area.per_component): self.chosen_element.setIcon(QIcon()) self.chosen_element = None else: item: MeasurementListWidgetItem = self.profile_options.currentItem( ) leaf = self.get_parameters( deepcopy(item.stat), self.measurement_area_choose.currentEnum(), self.per_component.currentEnum(), self.power_num.value(), ) if leaf is None: return lw = MeasurementListWidgetItem( Node(op="/", left=self.chosen_element_area, right=leaf)) lw.setToolTip("User defined") self._add_option(lw) self.chosen_element.setIcon(QIcon()) self.chosen_element = None self.chosen_element_area = None def _add_option(self, item: MeasurementListWidgetItem): for i in range(self.profile_options_chosen.count()): if item.text() == self.profile_options_chosen.item(i).text(): return self.profile_options_chosen.addItem(item) if self.good_name(): self.save_butt.setEnabled(True) self.save_butt_with_name.setEnabled(True) if self.profile_options.count() == 0: self.choose_butt.setDisabled(True) def create_selection_chosen_changed(self): # print(self.profile_options_chosen.count()) self.remove_button.setEnabled(True) if self.profile_options_chosen.count() == 0: self.move_down.setDisabled(True) self.move_up.setDisabled(True) self.remove_button.setDisabled(True) return self.discard_butt.setEnabled(True) if self.profile_options_chosen.currentRow() != 0: self.move_up.setEnabled(True) else: self.move_up.setDisabled(True) if self.profile_options_chosen.currentRow( ) != self.profile_options_chosen.count() - 1: self.move_down.setEnabled(True) else: self.move_down.setDisabled(True) def good_name(self): return str(self.profile_name.text()).strip() != "" def move_down_fun(self): row = self.profile_options_chosen.currentRow() item = self.profile_options_chosen.takeItem(row) self.profile_options_chosen.insertItem(row + 1, item) self.profile_options_chosen.setCurrentRow(row + 1) self.create_selection_chosen_changed() def move_up_fun(self): row = self.profile_options_chosen.currentRow() item = self.profile_options_chosen.takeItem(row) self.profile_options_chosen.insertItem(row - 1, item) self.profile_options_chosen.setCurrentRow(row - 1) self.create_selection_chosen_changed() def name_changed(self): if self.good_name() and self.profile_options_chosen.count() > 0: self.save_butt.setEnabled(True) self.save_butt_with_name.setEnabled(True) else: self.save_butt.setDisabled(True) self.save_butt_with_name.setDisabled(True) def form_dialog(self, arguments): return FormDialog(arguments, settings=self.settings, parent=self) def get_parameters(self, node: Union[Node, Leaf], area: AreaType, component: PerComponent, power: float): if isinstance(node, Node): return node node = node.replace_(power=power) if node.area is None: node = node.replace_(area=area) if node.per_component is None: node = node.replace_(per_component=component) with suppress(KeyError): arguments = MEASUREMENT_DICT[str(node.name)].get_fields() if len(arguments) > 0 and len(node.dict) == 0: dial = self.form_dialog(arguments) if dial.exec_(): node = node._replace(dict=dial.get_values()) else: return return node def choose_option(self): selected_item = self.profile_options.currentItem() # selected_row = self.profile_options.currentRow() if not isinstance(selected_item, MeasurementListWidgetItem): raise ValueError( f"Current item (type: {type(selected_item)} is not instance of MeasurementListWidgetItem" ) node = deepcopy(selected_item.stat) # noinspection PyTypeChecker node = self.get_parameters(node, self.measurement_area_choose.currentEnum(), self.per_component.currentEnum(), self.power_num.value()) if node is None: return lw = MeasurementListWidgetItem(node) lw.setToolTip(selected_item.toolTip()) self._add_option(lw) def discard_option(self): selected_item: MeasurementListWidgetItem = self.profile_options_chosen.currentItem( ) # selected_row = self.profile_options_chosen.currentRow() lw = MeasurementListWidgetItem(deepcopy(selected_item.stat)) lw.setToolTip(selected_item.toolTip()) self.create_selection_chosen_changed() for i in range(self.profile_options.count()): if lw.text() == self.profile_options.item(i).text(): return self.profile_options.addItem(lw) def edit_profile(self): item = self.profile_list.currentItem() if item is None: return profile = self.settings.measurement_profiles[str(item.text())] self.profile_options_chosen.clear() self.profile_name.setText(item.text()) for ch in profile.chosen_fields: self.profile_options_chosen.addItem( MeasurementListWidgetItem(ch.calculation_tree)) # self.gauss_img.setChecked(profile.use_gauss_image) self.save_butt.setEnabled(True) self.save_butt_with_name.setEnabled(True) def save_action(self): if self.profile_name.text() in self.settings.measurement_profiles: ret = QMessageBox.warning( self, "Profile exist", "Profile exist\nWould you like to overwrite it?", QMessageBox.No | QMessageBox.Yes, ) if ret == QMessageBox.No: return selected_values = [] for i in range(self.profile_options_chosen.count()): element: MeasurementListWidgetItem = self.profile_options_chosen.item( i) selected_values.append( MeasurementEntry(element.text(), element.stat)) stat_prof = MeasurementProfile(self.profile_name.text(), selected_values) self.settings.measurement_profiles[stat_prof.name] = stat_prof self.settings.dump() self.export_profiles_butt.setEnabled(True) def named_save_action(self): if self.profile_name.text() in self.settings.measurement_profiles: ret = QMessageBox.warning( self, "Profile exist", "Profile exist\nWould you like to overwrite it?", QMessageBox.No | QMessageBox.Yes, ) if ret == QMessageBox.No: return selected_values = [] for i in range(self.profile_options_chosen.count()): txt = str(self.profile_options_chosen.item(i).text()) selected_values.append((txt, str, txt)) val_dialog = MultipleInput("Set fields name", list(selected_values), parent=self) if val_dialog.exec_(): selected_values = [] for i in range(self.profile_options_chosen.count()): element: MeasurementListWidgetItem = self.profile_options_chosen.item( i) selected_values.append( MeasurementEntry(val_dialog.result[element.text()], element.stat)) stat_prof = MeasurementProfile(self.profile_name.text(), selected_values) self.settings.measurement_profiles[stat_prof.name] = stat_prof self.export_profiles_butt.setEnabled(True) def reset_action(self): self.profile_options.clear() self.profile_options_chosen.clear() self.profile_name.setText("") self.save_butt.setDisabled(True) self.save_butt_with_name.setDisabled(True) self.move_down.setDisabled(True) self.move_up.setDisabled(True) self.proportion_butt.setDisabled(True) self.choose_butt.setDisabled(True) self.discard_butt.setDisabled(True) for profile in MEASUREMENT_DICT.values(): help_text = profile.get_description() lw = MeasurementListWidgetItem(profile.get_starting_leaf()) lw.setToolTip(help_text) self.profile_options.addItem(lw) def soft_reset(self): # TODO rim should not be removed shift = 0 for i in range(self.profile_options.count()): item = self.profile_options.item(i - shift) if str(item.text()) not in MEASUREMENT_DICT: self.profile_options.takeItem(i - shift) if item == self.chosen_element: self.chosen_element = None del item shift += 1 self.create_selection_changed() def export_measurement_profiles(self): exp = ExportDialog(self.settings.measurement_profiles, StringViewer, parent=self) if not exp.exec_(): return dial = PSaveDialog( "Measurement profile (*.json)", settings=self.settings, path="io.export_directory", caption="Export settings profiles", ) dial.selectFile("measurements_profile.json") if dial.exec_(): file_path = str(dial.selectedFiles()[0]) data = { x: self.settings.measurement_profiles[x] for x in exp.get_export_list() } with open(file_path, "w", encoding="utf-8") as ff: json.dump(data, ff, cls=self.settings.json_encoder_class, indent=2) def import_measurement_profiles(self): dial = PLoadDialog( "Measurement profile (*.json)", settings=self.settings, path="io.export_directory", caption="Import settings profiles", parent=self, ) if dial.exec_(): file_path = str(dial.selectedFiles()[0]) stat, err = self.settings.load_part(file_path) if err: QMessageBox.warning( self, "Import error", "error during importing, part of data were filtered.") measurement_dict = self.settings.measurement_profiles imp = ImportDialog(stat, measurement_dict, StringViewer) if not imp.exec_(): return for original_name, final_name in imp.get_import_list(): measurement_dict[final_name] = stat[original_name] self.settings.dump()
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 SelectFromListDialog(QDialog): """Dialog box for selecting multiple items. The original order of items are preserved in the candidate list. """ def __init__(self, parent=None, allItems=(), selectedItems=()): QDialog.__init__( self, parent, flags=Qt.WindowSystemMenuHint | Qt.WindowTitleHint) self.allItems = allItems self.fromKeys = list(range(len(allItems))) self.selectedKeys = [] for item in selectedItems: key = allItems.index(item) self.fromKeys.remove(key) self.selectedKeys.append(key) self.setAttribute(Qt.WA_DeleteOnClose) fromLabel = QLabel(_("Select from")) self.fromList = QListWidget(self) self.fromList.addItems(allItems[key] for key in self.fromKeys) selectedLabel = QLabel(_("Selected")) self.selectedList = QListWidget(self) self.selectedList.addItems(allItems[key] for key in self.selectedKeys) self.selectButton = QPushButton(_("Select")) self.deselectButton = QPushButton(_("Deselect")) self.selectBox = QDialogButtonBox(Qt.Vertical) self.selectBox.addButton(self.selectButton, QDialogButtonBox.ActionRole) self.selectBox.addButton(self.deselectButton, QDialogButtonBox.ActionRole) self.selectButton.clicked.connect(self.on_select) self.deselectButton.clicked.connect(self.on_deselect) self.buttonBox = QDialogButtonBox(self) self.buttonBox.setOrientation(Qt.Horizontal) self.buttonBox.setStandardButtons( QDialogButtonBox.Cancel|QDialogButtonBox.Ok) self.buttonBox.accepted.connect(self.accept) self.buttonBox.rejected.connect(self.reject) mainLayout = QGridLayout(self) mainLayout.addWidget(fromLabel, 0, 0) mainLayout.addWidget(selectedLabel, 0, 2) mainLayout.addWidget(self.fromList, 1, 0) mainLayout.addWidget(self.selectBox, 1, 1) mainLayout.addWidget(self.selectedList, 1, 2) mainLayout.addWidget(self.buttonBox, 2, 0, 1, 3) mainLayout.setAlignment(self.selectBox, Qt.AlignCenter) self.setLayout(mainLayout) def on_select(self): if len(self.fromList.selectedItems()): idx = self.fromList.currentRow() key = self.fromKeys.pop(idx) self.selectedKeys.append(key) item = self.fromList.takeItem(idx) self.selectedList.addItem(item) def on_deselect(self): if len(self.selectedList.selectedItems()): idx = self.selectedList.currentRow() item = self.selectedList.takeItem(idx) key = self.selectedKeys.pop(idx) idx = next((i for i, v in enumerate(self.fromKeys) if v > key), len(self.fromKeys)) self.fromKeys.insert(idx, key) self.fromList.insertItem(idx, item)