def __setupUi(self): layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) view = QTreeView(objectName="tool-tree-view") view.setUniformRowHeights(True) view.setFrameStyle(QTreeView.NoFrame) view.setModel(self.__model) view.setRootIsDecorated(False) view.setHeaderHidden(True) view.setItemsExpandable(True) view.setEditTriggers(QTreeView.NoEditTriggers) view.setItemDelegate(ToolTreeItemDelegate(self)) view.activated.connect(self.__onActivated) view.clicked.connect(self.__onActivated) view.entered.connect(self.__onEntered) view.installEventFilter(self) self.__view = view layout.addWidget(view) self.setLayout(layout)
def __init__(self, parent=None) -> None: QWidget.__init__(self, parent) # Основные компоненты # Вывод варианта моделирования в виде иерархического дерева variant_tree_view = QTreeView() # Не хотим пользователю дать разрешение на его редактирование variant_tree_view.setEditTriggers(QAbstractItemView.NoEditTriggers) # Ссылка на экземпляр класса, управляющего заголовком header = variant_tree_view.header() # Задание режима для изменения размеров секций (автоматически растягивается) header.setSectionResizeMode(QHeaderView.ResizeToContents) # Модель представления данных self.model = QStandardItemModel(self) variant_tree_view.setModel(self.model) # Групбокс с вариантом моделирования group_box = QGroupBox("Получившийся вариант моделирования") group_box_layout = QVBoxLayout(group_box) group_box_layout.addWidget(variant_tree_view) # Контейнер с кнопками назад/далее control_layout = LayoutWithBackAndNextButtons() # Основной контейнер main_layout = QVBoxLayout(self) main_layout.addWidget(group_box) main_layout.addLayout(control_layout) # Для удобного доступа self.save_file_button = control_layout.next_button self.back_button = control_layout.back_button # Изменим надпись на кнопке на подходящую self.save_file_button.setText("Сохранить вариант моделирования")
class GetNodeDialog(QDialog): def __init__(self, parent, startnode, currentnode=None): QDialog.__init__(self, parent) layout = QVBoxLayout(self) self.treeview = QTreeView(self) self.treeview.setEditTriggers(QAbstractItemView.NoEditTriggers) self.tree = TreeWidget(self.treeview) self.tree.set_root_node(startnode) layout.addWidget(self.treeview) self.buttons = QDialogButtonBox( QDialogButtonBox.Ok | QDialogButtonBox.Cancel, Qt.Horizontal, self) layout.addWidget(self.buttons) self.resize(800, 600) self.buttons.accepted.connect(self.accept) self.buttons.rejected.connect(self.reject) self.treeview.activated.connect(self.accept) if currentnode: self.tree.expand_to_node(currentnode) def get_node(self): return self.tree.get_current_node() @staticmethod def getNode(parent, startnode, currentnode=None): dialog = GetNodeDialog(parent, startnode, currentnode) result = dialog.exec_() node = dialog.get_node() return node, result == QDialog.Accepted
def __init__(self): QWidget.__init__(self, flags=Qt.Widget) self.setWindowTitle("ItemView QListView") self.setFixedWidth(310) self.setFixedHeight(200) data = [ {"type": "Sword", "objects": ["Long Sword", "Short Sword"], "picture": "sword.png"}, {"type": "Shield", "objects": ["Wood Shield", "iron Shied"], "picture": "shield.png"}, ] self.layout = QBoxLayout(QBoxLayout.LeftToRight, self) self.setLayout(self.layout) # QTreeView 생성 및 설정 view = QTreeView(self) view.setEditTriggers(QAbstractItemView.DoubleClicked) self.model = Model(data) view.setModel(self.model) self.layout.addWidget(view) # 그림을 띄울 QLabel 생성 self.lb = QLabel() self.lb.setFixedSize(50, 50) self.layout.addWidget(self.lb) # 뷰 클릭시 발생하는 시그널 # 뷰를 클릭하면 QModelIndex를 넘겨준다. view.clicked.connect(self.slot_show_picture)
class BinaryDetailsDialog(QDialog): def __init__(self, data, *args, title=None, **kwargs): super().__init__(*args, **kwargs) self.setWindowFlag(Qt.WindowContextHelpButtonHint, False) self._data = data if not title: self.setWindowTitle(f"{self._data.binaries[0].binary_name} ({self._data.release_name})") self.setup_interface() def setup_interface(self): self.resize(600, 380) self.layout = QVBoxLayout() self.layout.setContentsMargins(0, 0, 0, 0) self.details_tree_model = BinaryDetailsTreeModel(self._data) self.details_tree_view = QTreeView() self.details_tree_view.setEditTriggers(QAbstractItemView.NoEditTriggers) self.details_tree_view.setAnimated(True) self.details_tree_view.setModel(self.details_tree_model) self.details_tree_view.expandAll() self.details_tree_view.resizeColumnToContents(0) self.details_tree_view.doubleClicked.connect( lambda index: QApplication.clipboard().setText( self.details_tree_model.data(index, Qt.DisplayRole) ) ) self.layout.addWidget(self.details_tree_view) self.setLayout(self.layout)
def __initFilesView(self, model: QFileSystemModel): view = QTreeView() view.setEditTriggers(QTreeView.NoEditTriggers) view.hideColumn(1) view.hideColumn(3) view.setGeometry(0, 0, 10, 10) view.setModel(model) view.setRootIndex(model.index(str(model.rootDirectory()))) return view
def __init__(self): QWidget.__init__(self, flags=Qt.Widget) self.setWindowTitle("ItemView QListView") box = QBoxLayout(QBoxLayout.TopToBottom) # QTreeView 생성 및 설정 view = QTreeView(self) view.setEditTriggers(QAbstractItemView.DoubleClicked) model = TreeModel(None) view.setModel(model) box.addWidget(view) self.setLayout(box)
def main(): import sys app = QApplication(sys.argv) v = QTreeView() model = TreeModel(utils.find_data_file('disease_num2name.p'), 'Disease') v.setModel(model) v.selectionModel().select( QItemSelection(model.index(0, 0), model.index(5, 0)), QItemSelectionModel.Select) v.setSelectionMode(QAbstractItemView.NoSelection) v.setEditTriggers(QAbstractItemView.NoEditTriggers) v.show() app.exec()
def __init__(self, *args): super().__init__(*args) self.__skip_next_hide = False tree_view = QTreeView(self) tree_view.setFrameShape(QFrame.NoFrame) tree_view.setEditTriggers(tree_view.NoEditTriggers) tree_view.setAlternatingRowColors(True) tree_view.setSelectionBehavior(tree_view.SelectRows) tree_view.setWordWrap(True) tree_view.setAllColumnsShowFocus(True) tree_view.setHeaderHidden(True) self.setView(tree_view) self.view().viewport().installEventFilter(self)
def __init__(self, parent=None): super(TreeComboBox, self).__init__(parent) self.__skip_next_hide = False self._last_pick = None tree_view = QTreeView(self) tree_view.setFrameShape(QFrame.NoFrame) tree_view.setEditTriggers(tree_view.NoEditTriggers) tree_view.setAlternatingRowColors(True) tree_view.setSelectionBehavior(tree_view.SelectRows) tree_view.setWordWrap(True) tree_view.setAllColumnsShowFocus(True) self.setView(tree_view) self.view().viewport().installEventFilter(self)
def __init__(self): QWidget.__init__(self, flags=Qt.Widget) self.setWindowTitle("ItemView QTreeView") self.setFixedWidth(210) self.setFixedHeight(150) data = [ {"type": "Sword", "objects": ["Long Sword", "Short Sword"], "picture": "sword.png"}, {"type": "Shield", "objects": ["Wood Shield", "iron Shied"], "picture": "shield.png"}, ] # QTreeView 생성 및 설정 view = QTreeView(self) view.setEditTriggers(QAbstractItemView.DoubleClicked) model = Model(data) view.setModel(model)
def __init__(self): QWidget.__init__(self, flags=Qt.Widget) self.setWindowTitle("ItemView QTreeView") self.setFixedWidth(300) self.setFixedHeight(700) # 데이터 data = [ {"type": "Fruit", "objects": ["Apple", "Banana"]}, {"type": "Vegetable", "objects": ["Carrot", "Tomato"]}, ] # QTreeView 생성 및 설정 view = QTreeView(self) view.setEditTriggers(QAbstractItemView.DoubleClicked) model = Model(data) view.setModel(model)
def __init__(self): QWidget.__init__(self, flags=Qt.Widget) self.setWindowTitle("ItemView QTreeView") self.setFixedWidth(210) self.setFixedHeight(150) # 데이터 data = [ { "type": "Fruit", "objects": [("Apple", "Red"), ("Banana", "Yellow")] }, ] # QTreeView 생성 및 설정 view = QTreeView(self) view.setEditTriggers(QAbstractItemView.DoubleClicked) model = Model(data) view.setModel(model)
class MetadataList(QWidget): def __init__(self, parent=None): super().__init__(parent) self._parent = parent #self.resize(100, 100) #sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) #sizePolicy.setHorizontalStretch(2) #sizePolicy.setVerticalStretch(0) #sizePolicy.setHeightForWidth(self.sizePolicy().hasHeightForWidth()) #self.setSizePolicy(sizePolicy) self.mdataview = QTreeView() self.mdataview.setHeaderHidden(True) self.mdataview.setSortingEnabled(True) self.mdataview.setAlternatingRowColors(True) self.mdataview.setEditTriggers(QAbstractItemView.NoEditTriggers) self.mdataview.sortByColumn(0, Qt.AscendingOrder) # Qt.DescendingOrder model = QStandardItemModel(0, 2) self.mdataview.setModel(model) layout = QVBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) layout.addWidget(self.mdataview) self.setLayout(layout) self.setVisible(True) def setupDataMapper(self, treeview): self._dataMapper = treeview.dataMapper self._dataMapper.setItemDelegate(MetadataDelegate(self)) # self._parent.parent().parent().db self._dataMapper.addMapping(self.mdataview, 1) treeview.selectionModel().currentChanged.connect(self.setSelection) def setSelection(self, current): parent = current.parent() self._dataMapper.setRootIndex(parent) self._dataMapper.setCurrentModelIndex(current) def clearData(self, layout=None): # NOT IMPLEMENTED---------------------------------------------- if hasattr(self, "_dataMapper"): self._dataMapper.clearMapping()
def __init__(self): QWidget.__init__(self, flags=Qt.Widget) self.setWindowTitle("ItemView QListView") box = QBoxLayout(QBoxLayout.TopToBottom) # 데이터 data = [ { "type": "Fruit", "objects": [("Apple", "Red"), ("Banana", "Yellow")]}, { "type": "Vegetable", "objects": [("Carrot", "Red"), ("Tomato", "Red")]}, ] # QTreeView 생성 및 설정 view = QTreeView(self) view.setEditTriggers(QAbstractItemView.DoubleClicked) view.setItemDelegateForColumn(1, ComboBoxDelegate(["Red", "Yellow"])) model = Model(data) view.setModel(model) box.addWidget(view) self.setLayout(box)
class DriveAnalysisWidget(QWidget): def __init__(self): super().__init__() self.setWindowTitle('Drive Analysis Tool') # self.root_path = os.path.expanduser('~') # BUG 2018-09-18: No permission to access certain files for os.stat self.root_path = os.path.expanduser('~\\Downloads') self.threadpool = QThreadPool() self.resize_mode = 'dynamic' select_btn = QPushButton('Select Folder', self) select_btn.setToolTip('Select <b>root folder</b> to simplify.') select_btn.clicked.connect(self.show_file_dialog) select_btn.resize(select_btn.sizeHint()) test_btn = QPushButton('Test Scripts', self) test_btn.clicked.connect(self.run_tests) test_btn.resize(test_btn.sizeHint()) self.folder_edit = QLabel() self.folder_edit.setText(self.root_path) self.status_label = QLabel() self.status_label.setText('') self.status_label.setStyleSheet("color: red;" "font: bold;") ogtree_label = QLabel() ogtree_label.setAlignment(Qt.AlignCenter) ogtree_label.setText('Original folders data') anontree_label = QLabel() anontree_label.setAlignment(Qt.AlignCenter) anontree_label.setText('Anonymized folders data used for research') self.ogtree = QTreeView(self) self.ogtree.setEditTriggers(QAbstractItemView.NoEditTriggers) self.ogmodel = QStandardItemModel() self.ogmodel.setHorizontalHeaderLabels(['Folder Name', 'Number of Files']) self.ogmodel.itemChanged.connect(self.on_item_change) self.anontree = QTreeView(self) self.anontree.setEditTriggers(QAbstractItemView.NoEditTriggers) self.anonmodel = QStandardItemModel() self.anonmodel.setHorizontalHeaderLabels(['Folder Name', 'Number of Files']) self.dir_dict, self.og_dir_dict = dict(), dict() self.build_tree_structure_threaded(self.root_path) grid = QGridLayout() grid.addWidget(select_btn, 0, 0, 1, 1) grid.addWidget(self.folder_edit, 0, 1, 1, 8) grid.addWidget(self.status_label, 1, 0, 1, 9) grid.addWidget(test_btn, 2, 0, 1, 1) grid.addWidget(ogtree_label, 3, 0, 1, 4) grid.addWidget(anontree_label, 3, 5, 1, 4) grid.addWidget(self.ogtree, 4, 0, 1, 4) grid.addWidget(self.anontree, 4, 5, 1, 4) self.setLayout(grid) self.resize(640, 480) self.show() def refresh_tree_header(self, tree, resize_mode='dynamic'): if resize_mode == 'dynamic': tree.header().setSectionResizeMode(0, QHeaderView.ResizeToContents) tree.header().setSectionResizeMode(1, QHeaderView.ResizeToContents) # tree.header().setSectionResizeMode(2, QHeaderView.ResizeToContents) elif resize_mode == 'static': tree.header().setSectionResizeMode(0, QHeaderView.Interactive) tree.header().setSectionResizeMode(1, QHeaderView.Interactive) # tree.header().setSectionResizeMode(2, QHeaderView.Interactive) def show_file_dialog(self): dirpath = QFileDialog.getExistingDirectory(self, 'Select Folder', self.root_path) if dirpath: self.root_path = os.path.abspath(dirpath) self.folder_edit.setText(self.root_path) self.build_tree_structure_threaded(self.root_path, self.prune_thold) def build_tree_structure_threaded(self, root_path): worker = Worker(self.build_tree_structure, root_path) worker.signals.started.connect(self.build_tree_started) worker.signals.result.connect(self.build_tree_finished) self.threadpool.start(worker) def anonymize_tree_structure_threaded(self, root_path, dir_dict, rename_dict, remove_dict): worker = Worker(self.anonymize_tree_structure, root_path, dir_dict, rename_dict, remove_dict) worker.signals.started.connect(self.anonymize_tree_started) worker.signals.result.connect(self.anonymize_tree_finished) self.threadpool.start(worker) def build_tree_structure(self, root_path): # self.slider.setDisabled(True) dir_dict = record_stat(root_path) og_dir_dict = deepcopy(dir_dict) # dir_dict = simplify_tree(root_path, 1, dir_dict, 0.95, self.scale_pruning(prune_thold), print_=False) # self.slider.setEnabled(True) return og_dir_dict, dir_dict def anonymize_tree_structure(self, dir_dict, rename_dict, remove_dict): # dir_dict = simplify_tree(root_path, 1, dir_dict, 0.95, self.scale_pruning(prune_thold), print_=False) dir_dict = anonymize_stat(dir_dict, rename_dict, remove_dict) return dir_dict def build_tree_started(self): self.status_label.setText('Building tree, please wait...') def build_tree_finished(self, result): self.og_dir_dict, self.dir_dict = result # print(self.og_dir_dict[1],self.og_dir_dict[2], self.og_dir_dict[3]) self.refresh_treeview(self.ogmodel, self.ogtree, self.og_dir_dict) # #BUG 2018-09-18: crashes the interface because i am referring to non-existent dictionary keys # (see append_all_children, e.g., dir_dict[dirkey][0]) # I've updated my dir_dict to use string names instead of integers so append_all_children # needs to be updated as well. # self.refresh_treeview(self.anonmodel, self.anontree, self.dir_dict) self.status_label.setText('') def anonymize_tree_started(self): self.status_label.setText('Anonymizing tree, please wait...') def anonymize_tree_finished(self, dir_dict): self.refresh_treeview(self.anonmodel, self.anontree, dir_dict) self.status_label.setText('') def refresh_treeview(self, model, tree, dir_dict): model.removeRow(0) parent = model.invisibleRootItem() self.append_all_children(1, dir_dict, parent) # dir_dict key starts at 1 since 0==False tree.setModel(model) self.refresh_tree_header(tree, self.resize_mode) tree.expandToDepth(0) def append_all_children(self, dirkey, dir_dict, qitem): qitem.setData(dirkey, Qt.UserRole) # PAUSING HERE FOR NOW 20180918-1254 print(dirkey, dir_dict[dirkey]['dirname']) # print(qitem.data(Qt.UserRole)) dirname = QStandardItem(dir_dict[dirkey]['dirname']) nfiles = QStandardItem(str(dir_dict[dirkey]['nfiles'])) # dirname.setFlags(Qt.ItemIsUserCheckable | Qt.ItemIsEnabled) # dirname.setData(QVariant(Qt.Checked), Qt.CheckStateRole) dirname.setCheckable(True) dirname.setCheckState(Qt.Checked) # Qt.Checked == 2 # dirname.setData(QVariant(str(dirkey)), role=Qt.UserRole+1) # doesn't work # qitem.appendRow([QStandardItem(dir_dict[dirkey]['dirname']), # QStandardItem(str(dir_dict[dirkey]['nfiles']))]) qitem.appendRow([dirname, nfiles, QStandardItem(str(dirkey))]) current_row = qitem.rowCount() - 1 print('Current row: ', current_row) print('dirkey for root: ', self.ogmodel.item(0).data(Qt.UserRole), qitem.data(Qt.UserRole)) children_keys = dir_dict[dirkey]['childkeys'] children_names = [dir_dict[child]['dirname'].lower() for child in children_keys] for child_name, child_key in sorted(zip(children_names, children_keys)): self.append_all_children(child_key, dir_dict, qitem.child(current_row)) def on_item_change(self, qitem): # print(type(qitem)) dirkey = qitem.data(Qt.UserRole) print(dirkey) # print(qitem.row(), qitem.column()) # print(qitem.rowCount(), qitem.columnCount()) # print(qitem.hasChildren()) # print(qitem.parent()) # print(qitem.text()) # if qitem.hasChildren(): # doesn't work # for child_item in qitem.sortChildren(): # self.on_item_change(child_item) # qitem.index(1, 1).setCheckState(check_state) # current_row = qitem.rowCount() - 1 # print(qitem.sortChildren(1)) # print(current_row) # print(qitem.checkState()) def run_tests(self): print(self.ogtree.model().rowCount()) for row in range(self.ogmodel.rowCount()): print(self.ogmodel.item(row).text(), self.ogmodel.item(row).data(Qt.UserRole)) print(self.ogmodel.item(row).hasChildren())
class OWGeneSets(OWWidget): name = "Gene Sets" description = "" icon = "icons/OWGeneSets.svg" priority = 9 want_main_area = True # settings selected_organism = Setting(0) auto_commit = Setting(True) auto_apply = Setting(True) gene_col_index = ContextSetting(0) use_attr_names = ContextSetting(False) class Inputs: genes = Input("Genes", Table) class Outputs: matched_genes = Output("Matched Genes", Table) class Information(OWWidget.Information): pass class Error(OWWidget.Error): cant_reach_host = Msg("Host orange.biolab.si is unreachable.") cant_load_organisms = Msg( "No available organisms, please check your connection.") def __init__(self): super().__init__() # commit self.commit_button = None # progress bar self.progress_bar = None self.progress_bar_iterations = None # data self.input_data = None self.tax_id = None self.input_genes = None self.mapped_genes = None self.organisms = list() self.input_info = None self.column_candidates = [] # filter self.lineEdit_filter = None self.search_pattern = '' self.organism_select_combobox = None # data model view self.data_view = None self.data_model = None # gene matcher NCBI self.gene_matcher = None # filter proxy model self.filter_proxy_model = None # hierarchy widget self.hierarchy_widget = None self.hierarchy_state = None # input options self.gene_columns = None self.gene_column_combobox = None self.attr_names_checkbox = None # threads self.threadpool = QThreadPool(self) self.workers = None # gui self.setup_gui() self._get_available_organisms() # self.handle_input(self.input_genes) # self.on_organism_change() def _progress_advance(self): # GUI should be updated in main thread. That's why we are calling advance method here if self.progress_bar: self.progress_bar.advance() def _get_selected_organism(self): return self.organisms[self.selected_organism] def _get_available_organisms(self): available_organism = sorted([(tax_id, taxonomy.name(tax_id)) for tax_id in taxonomy.common_taxids()], key=lambda x: x[1]) self.organisms = [tax_id[0] for tax_id in available_organism] self.organism_select_combobox.addItems( [tax_id[1] for tax_id in available_organism]) def _gene_names_from_table(self): """ Extract and return gene names from `Orange.data.Table`. """ self.input_genes = [] if self.input_data: if self.use_attr_names: self.input_genes = [ str(attr.name).strip() for attr in self.input_data.domain.attributes ] elif self.gene_columns: column = self.gene_columns[self.gene_col_index] self.input_genes = [ str(e[column]) for e in self.input_data if not np.isnan(e[column]) ] def _update_gene_matcher(self): self._gene_names_from_table() if self.gene_matcher: self.gene_matcher.genes = self.input_genes self.gene_matcher.organism = self._get_selected_organism() def on_input_option_change(self): self._update_gene_matcher() self.match_genes() @Inputs.genes def handle_input(self, data): if data: self.input_data = data self.gene_matcher = gene.GeneMatcher(self._get_selected_organism()) self.gene_column_combobox.clear() self.column_candidates = [ attr for attr in data.domain.variables + data.domain.metas if isinstance(attr, (StringVariable, DiscreteVariable)) ] for var in self.column_candidates: self.gene_column_combobox.addItem(*attributeItem(var)) self.tax_id = str(data_hints.get_hint(self.input_data, TAX_ID)) self.use_attr_names = data_hints.get_hint( self.input_data, GENE_NAME, default=self.use_attr_names) self.gene_col_index = min(self.gene_col_index, len(self.column_candidates) - 1) if self.tax_id in self.organisms: self.selected_organism = self.organisms.index(self.tax_id) self.on_input_option_change() def update_info_box(self): info_string = '' if self.input_genes: info_string += '{} unique gene names on input.\n'.format( len(self.input_genes)) mapped = self.gene_matcher.get_known_genes() if mapped: ratio = (len(mapped) / len(self.input_genes)) * 100 info_string += '{} ({:.2f}%) gene names matched.\n'.format( len(mapped), ratio) else: info_string += 'No genes on input.\n' self.input_info.setText(info_string) def match_genes(self): if self.gene_matcher: # init progress bar self.progress_bar = ProgressBar(self, iterations=len( self.gene_matcher.genes)) # status message self.setStatusMessage('gene matcher running') worker = Worker(self.gene_matcher.run_matcher, progress_callback=True) worker.signals.progress.connect(self._progress_advance) worker.signals.finished.connect(self.handle_matcher_results) # move download process to worker thread self.threadpool.start(worker) def handle_matcher_results(self): assert threading.current_thread() == threading.main_thread() if self.progress_bar: self.progress_bar.finish() self.setStatusMessage('') if self.gene_matcher.map_input_to_ncbi(): self.download_gene_sets() self.update_info_box() else: # reset gene sets self.init_item_model() self.update_info_box() def on_gene_sets_download(self, result): # make sure this happens in the main thread. # Qt insists that widgets be created within the GUI(main) thread. assert threading.current_thread() == threading.main_thread() self.progress_bar.finish() self.setStatusMessage('') tax_id, sets = result self.set_hierarchy_model(self.hierarchy_widget, *hierarchy_tree(tax_id, sets)) self.organism_select_combobox.setEnabled(True) # re-enable combobox self.update_info_box() self.mapped_genes = self.gene_matcher.map_input_to_ncbi() self.workers = defaultdict(list) self.progress_bar_iterations = dict() for selected_hierarchy in [*self.get_hierarchies()]: gene_sets = geneset.load_gene_sets(selected_hierarchy) worker = Worker(get_collections, gene_sets, self.mapped_genes, progress_callback=True, partial_result=True) worker.signals.error.connect(self.handle_error) worker.signals.finished.connect(self.handle_worker_finished) worker.signals.progress.connect(self._progress_advance) worker.signals.partial_result.connect(self.populate_data_model) worker.setAutoDelete(False) self.workers[selected_hierarchy] = worker self.progress_bar_iterations[selected_hierarchy] = len(gene_sets) def handle_worker_finished(self): # We check if all workers have completed. If not, continue # dirty hax, is this ok? if self.progress_bar and self.progress_bar.widget.progressBarValue == 100: self.progress_bar.finish() self.setStatusMessage('') self.hierarchy_widget.setDisabled(False) # adjust column width for i in range(len(DATA_HEADER_LABELS) - 1): self.data_view.resizeColumnToContents(i) self.filter_proxy_model.setSourceModel(self.data_model) def populate_data_model(self, partial_result): assert threading.current_thread() == threading.main_thread() if partial_result: self.data_model.appendRow(partial_result) def set_hierarchy_model(self, model, tax_id, sets): # TODO: maybe optimize this code? for key, value in sets.items(): item = QTreeWidgetItem(model, [key]) item.setFlags(item.flags() & (Qt.ItemIsUserCheckable | ~Qt.ItemIsSelectable | Qt.ItemIsEnabled)) # item.setDisabled(True) item.setData(0, Qt.CheckStateRole, Qt.Unchecked) item.setExpanded(True) item.tax_id = tax_id item.hierarchy = key if value: item.setFlags(item.flags() | Qt.ItemIsTristate) self.set_hierarchy_model(item, tax_id, value) else: if item.parent(): item.hierarchy = ((item.parent().hierarchy, key), tax_id) if not item.childCount() and not item.parent(): item.hierarchy = ((key, ), tax_id) def download_gene_sets(self): tax_id = self._get_selected_organism() self.Error.clear() # do not allow user to change organism when download task is running self.organism_select_combobox.setEnabled(False) # reset hierarchy widget state self.hierarchy_widget.clear() # clear data view self.init_item_model() # get all gene sets for selected organism gene_sets = geneset.list_all(organism=tax_id) # init progress bar self.progress_bar = ProgressBar(self, iterations=len(gene_sets) * 100) # status message self.setStatusMessage('downloading sets') worker = Worker(download_gene_sets, gene_sets, progress_callback=True) worker.signals.progress.connect(self._progress_advance) worker.signals.result.connect(self.on_gene_sets_download) worker.signals.error.connect(self.handle_error) # move download process to worker thread self.threadpool.start(worker) def display_gene_sets(self): self.init_item_model() self.hierarchy_widget.setDisabled(True) only_selected_hier = [*self.get_hierarchies(only_selected=True)] # init progress bar iterations = sum([ self.progress_bar_iterations[hier] for hier in only_selected_hier ]) self.progress_bar = ProgressBar(self, iterations=iterations) self.setStatusMessage('displaying gene sets') if not only_selected_hier: self.progress_bar.finish() self.setStatusMessage('') self.hierarchy_widget.setDisabled(False) return for selected_hierarchy in only_selected_hier: self.threadpool.start(self.workers[selected_hierarchy]) def handle_error(self, ex): self.progress_bar.finish() self.setStatusMessage('') if isinstance(ex, ConnectionError): self.organism_select_combobox.setEnabled( True) # re-enable combobox self.Error.cant_reach_host() print(ex) def get_hierarchies(self, **kwargs): """ return selected hierarchy """ only_selected = kwargs.get('only_selected', None) sets_to_display = list() if only_selected: iterator = QTreeWidgetItemIterator(self.hierarchy_widget, QTreeWidgetItemIterator.Checked) else: iterator = QTreeWidgetItemIterator(self.hierarchy_widget) while iterator.value(): # note: if hierarchy value is not a tuple, then this is just top level qTreeWidgetItem that # holds subcategories. We don't want to display all sets from category if type(iterator.value().hierarchy) is not str: if not only_selected: sets_to_display.append(iterator.value().hierarchy) else: if not iterator.value().isDisabled(): sets_to_display.append(iterator.value().hierarchy) iterator += 1 return sets_to_display def commit(self): selection_model = self.data_view.selectionModel() if selection_model: # genes_from_set = selection_model.selectedRows(GENES) matched_genes = selection_model.selectedRows(MATCHED) if matched_genes and self.input_genes: genes = [ model_index.data(Qt.UserRole) for model_index in matched_genes ] output_genes = [ gene_name for gene_name in list(set.union(*genes)) ] input_to_ncbi = self.gene_matcher.map_input_to_ncbi() ncbi_to_input = { ncbi_id: input_name for input_name, ncbi_id in self.gene_matcher.map_input_to_ncbi().items() } if self.use_attr_names: selected = [ self.input_data.domain[ncbi_to_input[output_gene]] for output_gene in output_genes ] domain = Domain(selected, self.input_data.domain.class_vars, self.input_data.domain.metas) new_data = self.input_data.from_table( domain, self.input_data) self.Outputs.matched_genes.send(new_data) elif self.column_candidates: column = self.column_candidates[self.gene_col_index] selected_rows = [] for row_index, row in enumerate(self.input_data): if str(row[column]) in input_to_ncbi.keys( ) and input_to_ncbi[str(row[column])] in output_genes: selected_rows.append(row_index) if selected_rows: selected = self.input_data[selected_rows] else: selected = None self.Outputs.matched_genes.send(selected) def setup_gui(self): # control area info_box = vBox(self.controlArea, 'Input info') self.input_info = widgetLabel(info_box) organism_box = vBox(self.controlArea, 'Organisms') self.organism_select_combobox = comboBox( organism_box, self, 'selected_organism', callback=self.on_input_option_change) # Selection of genes attribute box = widgetBox(self.controlArea, 'Gene attribute') self.gene_columns = itemmodels.VariableListModel(parent=self) self.gene_column_combobox = comboBox( box, self, 'gene_col_index', callback=self.on_input_option_change) self.gene_column_combobox.setModel(self.gene_columns) self.attr_names_checkbox = checkBox( box, self, 'use_attr_names', 'Use attribute names', disables=[(-1, self.gene_column_combobox)], callback=self.on_input_option_change) self.gene_column_combobox.setDisabled(bool(self.use_attr_names)) hierarchy_box = widgetBox(self.controlArea, "Entity Sets") self.hierarchy_widget = QTreeWidget(self) self.hierarchy_widget.setEditTriggers(QTreeView.NoEditTriggers) self.hierarchy_widget.setHeaderLabels(HIERARCHY_HEADER_LABELS) self.hierarchy_widget.itemClicked.connect(self.display_gene_sets) hierarchy_box.layout().addWidget(self.hierarchy_widget) self.commit_button = auto_commit(self.controlArea, self, "auto_commit", "&Commit", box=False) # rubber(self.controlArea) # main area self.filter_proxy_model = QSortFilterProxyModel(self.data_view) self.filter_proxy_model.setFilterKeyColumn(3) self.data_view = QTreeView() self.data_view.setModel(self.filter_proxy_model) self.data_view.setAlternatingRowColors(True) self.data_view.setSortingEnabled(True) self.data_view.setSelectionMode(QTreeView.ExtendedSelection) self.data_view.setEditTriggers(QTreeView.NoEditTriggers) self.data_view.viewport().setMouseTracking(True) self.data_view.setItemDelegateForColumn( TERM, LinkStyledItemDelegate(self.data_view)) self.data_view.selectionModel().selectionChanged.connect(self.commit) self.lineEdit_filter = lineEdit(self.mainArea, self, 'search_pattern', 'Filter gene sets:') self.lineEdit_filter.setPlaceholderText('search pattern ...') self.lineEdit_filter.textChanged.connect( self.filter_proxy_model.setFilterRegExp) self.mainArea.layout().addWidget(self.data_view) def init_item_model(self): if self.data_model: self.data_model.clear() self.filter_proxy_model.setSourceModel(None) else: self.data_model = QStandardItemModel() self.data_model.setSortRole(Qt.UserRole) self.data_model.setHorizontalHeaderLabels(DATA_HEADER_LABELS) def sizeHint(self): return QSize(1280, 960)
class EncoderSelector(QComboBox): __PATH_SEPARATOR__ = " → " _PATH_ROLE = 1 _DETAILS_ROLE = 2 class EncoderModel(QStandardItemModel): def __init__(self, json_file, disable_selections=False): super().__init__() self.disable_selections = disable_selections self.json_file = json_file self.json = None self.reload() def reload(self): with open(self.json_file) as config: self.json = json.load(config) self.beginResetModel() self.clear() self._build_descendents(self.invisibleRootItem(), self.json, self.disable_selections) self.endResetModel() def get_item(self, model_index): item = self.itemFromIndex(model_index) if item: return item.text() return None def del_item(self, media_type, encoder_group, name): del self.json[media_type][encoder_group][name] # Clean up ancestors if they dont have any children if len(self.json[media_type][encoder_group].keys()) == 0: del self.json[media_type][encoder_group] if len(self.json[media_type].keys()) == 0: del self.json[media_type] def add_item(self, media_type, group, name, extension, executable, command): if media_type not in self.json: self.json[media_type] = {} if group not in self.json[media_type]: self.json[media_type][group] = {} self.json[media_type][group][name] = { "extension": extension, "executable": executable, "command": command } def backup_and_save(self): # Backup current config backup_config_file() # Save new config with open(self.json_file, "w") as config: json.dump(self.json, config, indent=4) self.reload() @staticmethod def _build_descendents(parent, _json, disable_selections): encoder_keys = {"extension", "executable", "command"} intersection = set(_json.keys()) & encoder_keys if not intersection: # add node and build further for key in _json: section_root = QStandardItem(key) parent.appendRow(section_root) if disable_selections: parent.setSelectable(False) EncoderSelector.EncoderModel._build_descendents( section_root, _json[key], disable_selections) else: if len(intersection) < 3: raise SyntaxError( f"Missing value(s) {encoder_keys - intersection} in node {parent.text()}" ) else: path = "" item = parent while item.parent() is not None: path = f"{EncoderSelector.__PATH_SEPARATOR__}{item.text()}" + path item = item.parent() path = f"{item.text()}" + path parent.setData(path, Qt.UserRole + EncoderSelector._PATH_ROLE) parent.setData(_json, Qt.UserRole + EncoderSelector._DETAILS_ROLE) encoder_changed = pyqtSignal('PyQt_PyObject', 'PyQt_PyObject') def __init__(self): super().__init__() self.tree_view = QTreeView(self) self.tree_view.setEditTriggers(QTreeView.NoEditTriggers) self.tree_view.setSelectionBehavior(QTreeView.SelectRows) self.tree_view.setWordWrap(True) self.tree_view.setHeaderHidden(True) self.setView(self.tree_view) self.encoder_model = EncoderSelector.EncoderModel( get_config_file(), disable_selections=True) self.setModel(self.encoder_model) self.refresh_encoders() self._selected_encoder = "" def paintEvent(self, event): style = QApplication.style() opt = QStyleOptionComboBox() opt.rect = self.rect() self.initStyleOption(opt) painter = QPainter(self) painter.save() style.drawComplexControl(QStyle.CC_ComboBox, opt, painter) opt.currentText = self._encoder_path() style.drawControl(QStyle.CE_ComboBoxLabel, opt, painter) painter.restore() def hidePopup(self): super(EncoderSelector, self).hidePopup() selected_index = self.tree_view.selectionModel().currentIndex() if selected_index: encoder = self.tree_view.model().get_item(selected_index) if encoder: self._selected_encoder = encoder self.encoder_changed.emit(self._encoder_path(), self._encoder_details()) def get_encoder(self): return self._encoder_path(), self._encoder_details() def add_encoder(self, media_type, encoder_group, name, command, executable, extension): self.encoder_model.add_item(media_type, encoder_group, name, extension, executable, command) self.encoder_model.backup_and_save() self.refresh_encoders() def del_encoder(self, media_type, encoder_group, name): self.encoder_model.del_item(media_type, encoder_group, name) self.encoder_model.backup_and_save() self.refresh_encoders() def update_encoder(self, media_type, encoder_group, name, command, executable, extension): self.encoder_model.del_item(media_type, encoder_group, name) self.encoder_model.add_item(media_type, encoder_group, name, extension, executable, command) self.encoder_model.backup_and_save() self.refresh_encoders() def refresh_encoders(self): self.encoder_model.reload() self.tree_view.expandAll() def select_encoder(self, encoder_name): match = self._find_encoder(encoder_name) if match: self._selected_encoder = encoder_name self.encoder_changed.emit(self._encoder_path(), self._encoder_details()) def _encoder_path(self): if self._selected_encoder: match = self._find_encoder(self._selected_encoder) if match: return match[0].data(Qt.UserRole + self._PATH_ROLE) return None def _encoder_details(self): if self._selected_encoder: match = self._find_encoder(self._selected_encoder) if match: return match[0].data(Qt.UserRole + self._DETAILS_ROLE) return None def _find_encoder(self, encoder_name): return self.model().match(self.model().index(0, 0), Qt.UserRole + self._PATH_ROLE, encoder_name, 1, Qt.MatchEndsWith | Qt.MatchRecursive)
class StandardLibraryPage(QSplitter): """ The GUI for the standard library page of a project. """ # The page's label. label = "Standard Library" @property def project(self): """ The project property getter. """ return self._project @project.setter def project(self, value): """ The project property setter. """ if self._project != value: self._project = value self._update_page() def __init__(self): """ Initialise the page. """ super().__init__() self._project = None # Create the page's GUI. stdlib_pane = QWidget() stdlib_layout = QVBoxLayout() self._stdlib_edit = QTreeWidget( whatsThis="This shows the packages and modules in the target " "Python version's standard library. Check those " "packages and modules that are explicitly imported by " "the application. A module will be partially checked " "(and automatically included) if another module " "requires it.") self._stdlib_edit.setHeaderLabels(["Package"]) self._stdlib_edit.itemChanged.connect(self._module_changed) stdlib_layout.addWidget(self._stdlib_edit) stdlib_pane.setLayout(stdlib_layout) self.addWidget(stdlib_pane) extlib_pane = QWidget() extlib_layout = QVBoxLayout() extlib_sublayout = QFormLayout() self._version_edit = QComboBox( whatsThis="Select the target Python version. This will cause " "the standard library package hierarchy to be " "updated.") self._version_edit.addItems(get_supported_python_versions()) self._version_edit.currentIndexChanged.connect(self._version_changed) extlib_sublayout.addRow("Target Python version", self._version_edit) self._ssl_edit = QCheckBox( whatsThis="Enable SSL for the standard library modules " "that have optional support for it.", stateChanged=self._ssl_changed) extlib_sublayout.addRow("Enable optional SSL support", self._ssl_edit) extlib_layout.addLayout(extlib_sublayout) plat_gb = QGroupBox("Use standard Python shared library") plat_gb_layout = QVBoxLayout() self._platform_buttons = [] for scope, plat, subscopes in PLATFORM_SCOPES: plat_cb = QCheckBox(plat, whatsThis="Enable the use of the standard Python shared " "library on {0} rather than a statically compiled " "library.".format(plat), stateChanged=self._platforms_changed) plat_cb._scope = scope plat_gb_layout.addWidget(plat_cb) self._platform_buttons.append(plat_cb) plat_gb.setLayout(plat_gb_layout) extlib_layout.addWidget(plat_gb) self._extlib_edit = QTreeView( whatsThis="This is the list of external libraries that must " "be linked with the application. A library will only " "be enabled if a module in the standard library uses " "it. Double-click in the <b>DEFINES</b>, " "<b>INCLUDEPATH</b> and <b>LIBS</b> columns to modify " "the corresponding <tt>qmake</tt> variable as " "required. Values may be prefixed by a platform " "specific <tt>qmake</tt> scope.") self._extlib_edit.setRootIsDecorated(False) self._extlib_edit.setEditTriggers( QTreeView.DoubleClicked|QTreeView.SelectedClicked| QTreeView.EditKeyPressed) model = QStandardItemModel(self._extlib_edit) model.setHorizontalHeaderLabels( ("External Library", 'DEFINES', 'INCLUDEPATH', 'LIBS')) model.itemChanged.connect(self._extlib_changed) for extlib in external_libraries_metadata: name_itm = QStandardItem(extlib.user_name) extlib._items = (name_itm, QStandardItem(), QStandardItem(), QStandardItem()) model.appendRow(extlib._items) self._extlib_edit.setModel(model) for col in range(3): self._extlib_edit.resizeColumnToContents(col) extlib_layout.addWidget(self._extlib_edit) self._ignore_extlib_changes = False extlib_pane.setLayout(extlib_layout) self.addWidget(extlib_pane) def _update_page(self): """ Update the page using the current project. """ project = self.project blocked = self._version_edit.blockSignals(True) self._version_edit.setCurrentIndex( get_supported_python_version_index( project.python_target_version)) self._version_edit.blockSignals(blocked) blocked = self._ssl_edit.blockSignals(True) self._ssl_edit.setCheckState( Qt.Checked if project.python_ssl else Qt.Unchecked) self._ssl_edit.blockSignals(blocked) for plat in self._platform_buttons: blocked = plat.blockSignals(True) plat.setCheckState( Qt.Checked if plat._scope in project.python_use_platform else Qt.Unchecked) plat.blockSignals(blocked) self._update_extlib_editor() self._update_stdlib_editor() def _update_stdlib_editor(self): """ Update the standard library module editor. """ project = self.project editor = self._stdlib_edit metadata = get_python_metadata(project.python_target_version) blocked = editor.blockSignals(True) editor.clear() def add_module(name, module, parent): itm = QTreeWidgetItem(parent, name.split('.')[-1:]) itm.setFlags(Qt.ItemIsEnabled|Qt.ItemIsUserCheckable) itm._name = name # Handle any sub-modules. if module.modules is not None: for submodule_name in module.modules: # We assume that a missing sub-module is because it is not # in the current version rather than bad meta-data. submodule = metadata.get(submodule_name) if submodule is not None: add_module(submodule_name, submodule, itm) for name, module in metadata.items(): if not module.internal and '.' not in name: add_module(name, module, editor) editor.sortItems(0, Qt.AscendingOrder) editor.blockSignals(blocked) self._set_dependencies() def _set_dependencies(self): """ Set the dependency information. """ project = self.project editor = self._stdlib_edit required_modules, required_libraries = project.get_stdlib_requirements() blocked = editor.blockSignals(True) it = QTreeWidgetItemIterator(editor) itm = it.value() while itm is not None: external = required_modules.get(itm._name) expanded = False if external is None: state = Qt.Unchecked elif external: state = Qt.Checked expanded = True else: state = Qt.PartiallyChecked itm.setCheckState(0, state) # Make sure every explicitly checked item is visible. if expanded: parent = itm.parent() while parent is not None: parent.setExpanded(True) parent = parent.parent() it += 1 itm = it.value() editor.blockSignals(blocked) model = self._extlib_edit.model() # Note that we can't simply block the model's signals as this would # interfere with the model/view interactions. self._ignore_extlib_changes = True for extlib in external_libraries_metadata: if extlib.name in required_libraries: for idx, itm in enumerate(extlib._items): itm.setFlags( Qt.ItemIsEnabled|Qt.ItemIsEditable if idx != 0 else Qt.ItemIsEnabled) else: for itm in extlib._items: itm.setFlags(Qt.NoItemFlags) self._ignore_extlib_changes = False def _update_extlib_editor(self): """ Update the external library editor. """ project = self.project model = self._extlib_edit.model() blocked = model.blockSignals(True) for extlib in external_libraries_metadata: _, defs, incp, libs = extlib._items for prj_extlib in project.external_libraries: if prj_extlib.name == extlib.name: defs.setText(prj_extlib.defines) incp.setText(prj_extlib.includepath) libs.setText(prj_extlib.libs) break else: defs.setText('') incp.setText('') libs.setText(extlib.libs) model.blockSignals(blocked) def _version_changed(self, idx): """ Invoked when the target Python version changes. """ project = self.project project.python_target_version = get_supported_python_version(idx) self._update_page() project.modified = True def _ssl_changed(self, state): """ Invoked when the SSL support changes. """ project = self.project project.python_ssl = (state == Qt.Checked) self._set_dependencies() project.modified = True def _platforms_changed(self, state): """ Invoked when the platforms change. """ project = self._project project.python_use_platform = [] for plat in self._platform_buttons: if plat.checkState() == Qt.Checked: project.python_use_platform.append(plat._scope) project.modified = True def _module_changed(self, itm, col): """ Invoked when a standard library module has changed. """ project = self._project name = itm._name if name in project.standard_library: project.standard_library.remove(name) else: project.standard_library.append(name) self._set_dependencies() project.modified = True def _extlib_changed(self, itm): """ Invoked when an external library has changed. """ if self._ignore_extlib_changes: return self._ignore_extlib_changes = True project = self.project idx = self._extlib_edit.model().indexFromItem(itm) extlib = external_libraries_metadata[idx.row()] col = idx.column() # Get the project entry, creating it if necessary. for prj_extlib in project.external_libraries: if prj_extlib.name == extlib.name: break else: prj_extlib = ExternalLibrary(extlib.name, '', '', extlib.libs) project.external_libraries.append(prj_extlib) # Update the project. text = itm.text().strip() if col == 1: prj_extlib.defines = text elif col == 2: prj_extlib.includepath = text elif col == 3: if text == '': text = extlib.libs itm.setText(text) prj_extlib.libs = text # If the project entry corresponds to the default then remove it. if prj_extlib.defines == '' and prj_extlib.includepath == '' and prj_extlib.libs == extlib.libs: project.external_libraries.remove(prj_extlib) project.modified = True self._ignore_extlib_changes = False
class MainWindow(QMainWindow): def __init__(self): super().__init__() dockWidget = QDockWidget("Explorer", self) self.editor = QPlainTextEdit() self.editor.document().setDefaultFont(QFont("monospace")) self.editor.zoomIn(2) self.explorer = QTreeView() self.model = QFileSystemModel(self.explorer) self.root = self.model.setRootPath("../..") self.explorer.setModel(self.model) self.explorer.setRootIndex(self.root) self.file_path = None # Instances of menus and message self.custom_menu = Custom_menu(self, self.explorer, self.model, self.editor, app) self.menu = Menu(self, self.explorer, self.editor, self.model) self.message = Message(self) # Menu bar self.file_menu = self.menuBar().addMenu("&File") self.help_menu = self.menuBar().addMenu("&Help") self.menu.menu_actions() # Other custom tweaks self.explorer.setSortingEnabled(True) self.explorer.setMinimumWidth(400) self.explorer.setContextMenuPolicy(Qt.CustomContextMenu) self.resize(1500, 900) # Enabling renaming on context menu self.model.setReadOnly(False) self.explorer.setEditTriggers(self.explorer.NoEditTriggers) # Change default explorers column width self.header = self.explorer.header() self.header.setSectionResizeMode(0, QHeaderView.Interactive) self.header.setDefaultSectionSize(200) self.header.setSectionResizeMode(1, QHeaderView.ResizeToContents) self.header.setSectionResizeMode(2, QHeaderView.ResizeToContents) # Setting editor as central widget self.setCentralWidget(self.editor) self.addDockWidget(Qt.LeftDockWidgetArea, dockWidget) # Setting view as widget of dock widget dockWidget.setWidget(self.explorer) dockWidget.setFloating(False) ### Double & right click self.explorer.doubleClicked.connect(self.custom_menu.on_double_click) self.explorer.customContextMenuRequested.connect( self.custom_menu.context_menu) def closeEvent(self, e): """This function prevents from closing without saving, it works with the "Close" event""" if not self.editor.document().isModified(): return answer = self.message.ask_for_confirmation() if answer == QMessageBox.Save: if not self.menu.save(): e.ignore() elif answer == QMessageBox.Cancel: e.ignore()
class OpenedFileExplorer(DockWidget): """Opened File Explorer is list widget with list of opened files. It implements switching current file, files sorting. Uses _OpenedFileModel internally. Class instance created by Workspace. """ def __init__(self, workspace): DockWidget.__init__(self, workspace, "&Opened Files", QIcon(":/enkiicons/filtered.png"), "Alt+O") self._workspace = workspace self.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea) self.tvFiles = QTreeView(self) self.tvFiles.setHeaderHidden(True) self.tvFiles.setEditTriggers(QAbstractItemView.SelectedClicked) self.tvFiles.setContextMenuPolicy(Qt.CustomContextMenu) self.tvFiles.setDragEnabled(True) self.tvFiles.setDragDropMode(QAbstractItemView.InternalMove) self.tvFiles.setRootIsDecorated(False) self.tvFiles.setTextElideMode(Qt.ElideMiddle) self.tvFiles.setUniformRowHeights(True) self.tvFiles.customContextMenuRequested.connect(self._onTvFilesCustomContextMenuRequested) self.setWidget(self.tvFiles) self.setFocusProxy(self.tvFiles) self.model = _OpenedFileModel(self) # Not protected, because used by Configurator self.tvFiles.setModel(self.model) self.tvFiles.setAttribute(Qt.WA_MacShowFocusRect, False) self.tvFiles.setAttribute(Qt.WA_MacSmallSize) self._workspace.currentDocumentChanged.connect(self._onCurrentDocumentChanged) # disconnected by startModifyModel() self.tvFiles.selectionModel().selectionChanged.connect(self._onSelectionModelSelectionChanged) self.tvFiles.activated.connect(self._workspace.focusCurrentDocument) core.actionManager().addAction("mView/aOpenedFiles", self.showAction()) def terminate(self): """Explicitly called destructor """ core.actionManager().removeAction("mView/aOpenedFiles") def startModifyModel(self): """Blocks signals from model while it is modified by code """ self.tvFiles.selectionModel().selectionChanged.disconnect(self._onSelectionModelSelectionChanged) def finishModifyModel(self): """Unblocks signals from model """ self.tvFiles.selectionModel().selectionChanged.connect(self._onSelectionModelSelectionChanged) @pyqtSlot(Document, Document) def _onCurrentDocumentChanged(self, oldDocument, currentDocument): # pylint: disable=W0613 """ Current document has been changed on workspace """ if currentDocument is not None: index = self.model.documentIndex(currentDocument) self.startModifyModel() self.tvFiles.setCurrentIndex(index) # scroll the view self.tvFiles.scrollTo(index) self.finishModifyModel() @pyqtSlot(QItemSelection, QItemSelection) def _onSelectionModelSelectionChanged(self, selected, deselected): # pylint: disable=W0613 """ Item selected in the list. Switch current document """ if not selected.indexes(): # empty list, last file closed return index = selected.indexes()[0] # backup/restore current focused widget as setting active mdi window will steal it focusWidget = self.window().focusWidget() # set current document document = self._workspace.sortedDocuments[index.row()] self._workspace.setCurrentDocument(document) # restore focus widget if focusWidget: focusWidget.setFocus() @pyqtSlot(QPoint) def _onTvFilesCustomContextMenuRequested(self, pos): """Connected automatically by uic """ menu = QMenu() menu.addAction(core.actionManager().action("mFile/mClose/aCurrent")) menu.addAction(core.actionManager().action("mFile/mSave/aCurrent")) menu.addAction(core.actionManager().action("mFile/mReload/aCurrent")) menu.addSeparator() menu.addAction(core.actionManager().action("mFile/mFileSystem/aRename")) toggleExecutableAction = core.actionManager().action("mFile/mFileSystem/aToggleExecutable") if toggleExecutableAction: # not available on Windows menu.addAction(toggleExecutableAction) core.actionManager().action("mFile/mFileSystem").menu().aboutToShow.emit() # to update aToggleExecutable menu.exec_(self.tvFiles.mapToGlobal(pos))
class ZoomerWidget(QWidget): def __init__(self, parent=None): # super().__init__() # self.initUI() super(ZoomerWidget, self).__init__(parent) self.parent = parent self.initUI() def initUI(self): self.setWindowTitle('GIMZoomer') # self.prune_thold = 95 # only use when slider scroll direction is reversed self.prune_thold = 4 # self.root_path = os.path.expanduser('~') self.root_path = os.path.expanduser('C:\\Users\\ultra\\Dropbox\\mcgill') # self.root_path = os.path.expanduser('C:\\Users\\ultra\\Documents') # grid = QGridLayout() # grid.setSpacing(10) # grid.setColumnStretch(3, 8) # self.proc_lbl = QLabel() # self.proc_lbl.setAlignment(Qt.AlignCenter) # self.proc_mov = QMovie('ajax-loader.gif') # self.proc_lbl.setMovie(self.proc_mov) # # self.proc_lbl.setText('placeholder') # # self.proc_mov.start() select_btn = QPushButton('Select Folder', self) select_btn.setToolTip('Select <b>root folder</b> to simplify.') select_btn.clicked.connect(self.showFileDialog) select_btn.resize(select_btn.sizeHint()) # grid.addWidget(select_btn, 0, 0) # self.folder_edit = QLineEdit() # self.folder_edit.setReadOnly(True) self.folder_edit = QLabel() self.folder_edit.setText(self.root_path) # grid.addWidget(self.folder_edit, 0, 1) self.status_label = QLabel() self.status_label.setText('') self.slider_label_top = QLabel() self.slider_label_top.setAlignment(Qt.AlignCenter) # self.slider_label_top.setText('Pruning\nThreshold:\n' + '{:.3f}'.format(self.scalePruning(self.prune_thold))) self.slider_label_top.setText('Few important folders') # grid.addWidget(self.slider_label_top, 1, 0) self.slider_label_btm = QLabel() self.slider_label_btm.setAlignment(Qt.AlignCenter) self.slider_label_btm.setText('All folders') slider = QSlider(Qt.Vertical, self) # for dynamically changing pruning threshold, default is 0.02 slider.setValue(self.prune_thold) # grid.addWidget(slider, 2, 4, 1, 5) slider.setTickPosition(QSlider.TicksBothSides) slider.setTickInterval(10) slider.valueChanged[int].connect(self.changeValue) # slider.setInvertedAppearance(True) ogtree_label = QLabel() ogtree_label.setAlignment(Qt.AlignCenter) ogtree_label.setText('Original Structure') tree_label = QLabel() tree_label.setAlignment(Qt.AlignCenter) tree_label.setText('Pruned Structure\n(Ordered by Folder Importance)') self.ogtree = QTreeView(self) self.ogtree.setEditTriggers(QAbstractItemView.NoEditTriggers) self.ogmodel = QStandardItemModel() self.ogmodel.setHorizontalHeaderLabels(['Folder Name', 'Accessible Files', 'Number of Files']) # self.ogtree.header().setSectionResizeMode(QHeaderView.ResizeToContents) self.tree = QTreeView(self) self.tree.setEditTriggers(QAbstractItemView.NoEditTriggers) self.model = QStandardItemModel() self.model.setHorizontalHeaderLabels(['Folder Name', 'Accessible Files', 'Number of Files']) # self.tree.header().setSectionResizeMode(QHeaderView.ResizeToContents) self.dir_dict, self.og_dir_dict = self.buildStructure(self.root_path) self.simplifyStructure(self.root_path, self.dir_dict, self.prune_thold) grid = QGridLayout() grid.addWidget(select_btn, 0, 0, 1, 1) grid.addWidget(self.folder_edit, 0, 1, 1, 7) grid.addWidget(self.status_label, 0, 8, 1, 1) grid.addWidget(ogtree_label, 1, 0, 1, 4) grid.addWidget(self.slider_label_top, 1, 4, 1, 1) grid.addWidget(tree_label, 1, 5, 1, 4) grid.addWidget(self.ogtree, 2, 0, 1, 4) grid.addWidget(slider, 2, 4, 1, 1, alignment=Qt.AlignHCenter) grid.addWidget(self.tree, 2, 5, 1, 4) grid.addWidget(self.slider_label_btm, 3, 4, 1, 1) self.setLayout(grid) # self.resize(640, 480) # self.show() def scalePruning(self, prune_thold): # # for reversing the slider scroll direction # scale_dict = dict(zip(range(100), range(100)[::-1])) # return scale_dict[prune_thold] * 0.005 return prune_thold * 0.005 # return math.log(prune_thold, 10)/2 # return math.exp(prune_thold)/math.exp(100) def showFileDialog(self): dirpath = QFileDialog.getExistingDirectory(self, 'Select Folder', self.root_path) if dirpath: self.root_path = os.path.abspath(dirpath) self.folder_edit.setText(self.root_path) self.dir_dict, self.og_dir_dict = self.buildStructure(self.root_path) self.simplifyStructure(self.root_path, self.dir_dict, self.prune_thold) def changeValue(self, value): # print(value) self.prune_thold = value # self.slider_label.setText('Pruning\nThreshold:\n' + '{:.3f}'.format(self.scalePruning(self.prune_thold))) self.dir_dict = deepcopy(self.og_dir_dict) self.simplifyStructure(self.root_path, self.dir_dict, self.prune_thold) def buildStructure(self, root_path): dir_dict = read_and_count(root_path) og_dir_dict = deepcopy(dir_dict) self.refreshTreeView(self.ogmodel, self.ogtree, og_dir_dict) return og_dir_dict, dir_dict def simplifyStructure(self, root_path, dir_dict, prune_thold): def simplify_started(): self.status_label.setText('<font color="red">Please wait...</font>') self.parent.statusBar().showMessage('Hello World') def simplify_finished(returned_dict): self.refreshTreeView(self.model, self.tree, returned_dict) self.status_label.setText('') self.parent.statusBar().showMessage('Goodbye World') # [0] directory name, [1] parent key, [2] children keys, # [3] names of files found in directory, [4] cumulative count of accessible files # root_path = 'C:\\Users\\ultra\\Dropbox\\mcgill' # dir_dict = read_and_count(root_path) # og_dir_dict = deepcopy(dir_dict) # dir_dict = simplify_tree(root_path, 1, dir_dict, 0.95, self.scalePruning(prune_thold), print_=False) # self.status_label.setText('<font color="red">Please wait...</font>') # self.parent.statusBar().showMessage('Hello World') simplify_worker = SimplifyThread(root_path, dir_dict, self.scalePruning(prune_thold)) simplify_worker.started.connect(simplify_started) simplify_worker.finished.connect(simplify_finished) simplify_worker.start() # print_tree(root_path, dir_dict) # self.refreshTreeView(self.model, self.tree, dir_dict) # # self.ogmodel.clear() # # self.ogmodel.setHorizontalHeaderLabels(['Folder Name', 'Accessible Files', 'Number of Files']) # self.ogmodel.removeRow(0) # ogparent = self.ogmodel.invisibleRootItem() # self.append_all_children(1, og_dir_dict, ogparent) # dir_dict key starts at 1 since 0==False # self.ogtree.setModel(self.ogmodel) # self.ogtree.expandAll() # self.ogtree.resizeColumnToContents(0) # self.ogtree.resizeColumnToContents(1) # self.ogtree.resizeColumnToContents(2) # # self.model.clear() # # self.model.setHorizontalHeaderLabels(['Folder Name', 'Accessible Files', 'Number of Files']) # self.model.removeRow(0) # parent = self.model.invisibleRootItem() # self.append_all_children(1, dir_dict, parent) # dir_dict key starts at 1 since 0==False # self.tree.setModel(self.model) # self.tree.expandAll() # self.tree.resizeColumnToContents(0) # self.tree.resizeColumnToContents(1) # self.tree.resizeColumnToContents(2) # return dir_dict, og_dir_dict def refreshTreeView(self, model, tree, dir_dict): model.removeRow(0) parent = model.invisibleRootItem() self.append_all_children(1, dir_dict, parent) # dir_dict key starts at 1 since 0==False tree.setModel(model) # tree.expandAll() tree.expandToDepth(0) tree.resizeColumnToContents(0) tree.resizeColumnToContents(1) tree.resizeColumnToContents(2) def append_all_children(self, dirkey, dir_dict, qitem): qitem.appendRow([QStandardItem(dir_dict[dirkey][0]), QStandardItem(str(dir_dict[dirkey][4])), QStandardItem(str(len(dir_dict[dirkey][3])))]) current_row = qitem.rowCount()-1 # for child in sorted(dir_dict[dirkey][2]): # self.append_all_children(child, dir_dict, qitem.child(current_row)) children_keys = dir_dict[dirkey][2] children_names = [dir_dict[child][0].lower() for child in children_keys] for child_name, child_key in sorted(zip(children_names, children_keys)): self.append_all_children(child_key, dir_dict, qitem.child(current_row))
class Installed(preferences.Group): """Overview of installed extensions. A QTreeView lists the metadata of all installed extensions. If the currently selected extension provides a configuration widget it is displayed in the bottom group of the page. With a checkbox individual extensions can be deactivated. Metadata is listed for all *installed* extensions, regardless of manual deactivation or load failure. """ def __init__(self, page): super(Installed, self).__init__(page) layout = QVBoxLayout() self.setLayout(layout) # This must be called before self.populate() because # the data model relies on the labels app.translateUI(self) self.tree = QTreeView() self.name_items = {} self._selected_extension = '' self.tree.setModel(QStandardItemModel()) self.tree.model().setColumnCount(2) self.tree.setSelectionBehavior(QAbstractItemView.SelectRows) self.tree.setEditTriggers(QAbstractItemView.NoEditTriggers) self.tree.setHeaderHidden(True) self.tree.header().setSectionResizeMode( 0, QHeaderView.ResizeToContents) self.tree.selectionModel().selectionChanged.connect( self.selection_changed) self.populate() layout.addWidget(self.tree) def translateUI(self): self.setTitle(_("Installed Extensions")) self.config_labels = { 'extension-name': _("Name"), 'maintainers': _("Maintainer(s)"), 'version': _("Version"), 'api-version': _("API version"), 'license': _("License"), 'short-description': _("Short Description"), 'description': _("Description"), 'repository': _("Repository"), 'website': _("Website"), 'dependencies': _("Dependencies") } def loadSettings(self): s = QSettings() self.setEnabled(self.page().active()) s.beginGroup("extension-settings/installed") inactive = s.value("inactive", [], list) for ext in self.name_items.keys(): self.name_items[ext].setCheckState( Qt.Checked if ext not in inactive else Qt.Unchecked) self.tree.model().dataChanged.connect(self.page().changed) def saveSettings(self): s = QSettings() s.beginGroup("extension-settings/installed") inactive = [ext for ext in self.name_items.keys() if self.name_items[ext].checkState() == Qt.Unchecked] s.setValue("inactive", inactive) def populate(self): """Populate the tree view with data from the installed extensions. """ # TODO/Question: # Would it make sense to move this to a dedicated model class # complementing the FailedModel? root = self.tree.model().invisibleRootItem() extensions = app.extensions() for ext in extensions.installed_extensions(): ext_infos = extensions.infos(ext) display_name = ext_infos.get(ext, ext) if ext_infos else ext.name() loaded_extension = extensions.get(ext) if loaded_extension: display_name += ' ({})'.format(loaded_extension.load_time()) name_item = QStandardItem(display_name) name_item.extension_name = ext name_item.setCheckable(True) self.name_items[ext] = name_item icon = extensions.icon(ext) if icon: name_item.setIcon(icon) root.appendRow([name_item]) for entry in [ 'extension-name', 'short-description', 'description', 'version', 'api-version', 'dependencies', 'maintainers', 'repository', 'website', 'license' ]: label_item = QStandardItem('{}:'.format( self.config_labels[entry])) label_item.setTextAlignment(Qt.AlignTop) bold = QFont() bold.setWeight(QFont.Bold) label_item.setFont(bold) details = ext_infos.get(entry, "") if ext_infos else "" if type(details) == list: details = '\n'.join(details) details_item = QStandardItem(details) details_item.setTextAlignment(Qt.AlignTop) if entry == 'api-version': # Check for correct(ly formatted) api-version entry # and highlight it in case of mismatch api_version = appinfo.extension_api if not details: details_item.setFont(bold) details_item.setText( _("Misformat: {api}").format(details)) elif not details == api_version: details_item.setFont(bold) details_item.setText('{} ({}: {})'.format( details, appinfo.appname, api_version)) name_item.appendRow([label_item, details_item]) def selected_extension(self): """Return the (directory) name of the extension that is currently selected.""" return self._selected_extension def selection_changed(self, new, old): """Show the configuration widget for the selected extension, if available.""" config = self.siblingGroup(Config) if new.indexes(): ext_item = self.tree.model().itemFromIndex(new.indexes()[0]) # NOTE: This may have to be changed if there should be # more complexity in the tree model than now (a selected # row is either a top-level row or its immediate child) if not hasattr(ext_item, 'extension_name'): ext_item = ext_item.parent() name = ext_item.extension_name if name == self.selected_extension(): return else: # If nothing is selected, show the "empty" widget name = "" config.hide_extension() self._selected_extension = name config.show_extension(name)
class DirectoriesDialog(QMainWindow): def __init__(self, app, **kwargs): super().__init__(None, **kwargs) self.app = app self.lastAddedFolder = platform.INITIAL_FOLDER_IN_DIALOGS self.recentFolders = Recent(self.app, 'recentFolders') self._setupUi() self.directoriesModel = DirectoriesModel(self.app.model.directory_tree, view=self.treeView) self.directoriesDelegate = DirectoriesDelegate() self.treeView.setItemDelegate(self.directoriesDelegate) self._setupColumns() self.app.recentResults.addMenu(self.menuLoadRecent) self.app.recentResults.addMenu(self.menuRecentResults) self.recentFolders.addMenu(self.menuRecentFolders) self._updateAddButton() self._updateRemoveButton() self._updateLoadResultsButton() self._setupBindings() def _setupBindings(self): self.scanButton.clicked.connect(self.scanButtonClicked) self.loadResultsButton.clicked.connect(self.actionLoadResults.trigger) self.addFolderButton.clicked.connect(self.actionAddFolder.trigger) self.removeFolderButton.clicked.connect(self.removeFolderButtonClicked) self.treeView.selectionModel().selectionChanged.connect( self.selectionChanged) self.app.recentResults.itemsChanged.connect( self._updateLoadResultsButton) self.recentFolders.itemsChanged.connect(self._updateAddButton) self.recentFolders.mustOpenItem.connect(self.app.model.add_directory) self.directoriesModel.foldersAdded.connect( self.directoriesModelAddedFolders) self.app.willSavePrefs.connect(self.appWillSavePrefs) def _setupActions(self): # (name, shortcut, icon, desc, func) ACTIONS = [ ('actionLoadResults', 'Ctrl+L', '', tr("Load Results..."), self.loadResultsTriggered), ('actionShowResultsWindow', '', '', tr("Results Window"), self.app.showResultsWindow), ('actionAddFolder', '', '', tr("Add Folder..."), self.addFolderTriggered), ] createActions(ACTIONS, self) def _setupMenu(self): self.menubar = QMenuBar(self) self.menubar.setGeometry(QRect(0, 0, 42, 22)) self.menuFile = QMenu(self.menubar) self.menuFile.setTitle(tr("File")) self.menuView = QMenu(self.menubar) self.menuView.setTitle(tr("View")) self.menuHelp = QMenu(self.menubar) self.menuHelp.setTitle(tr("Help")) self.menuLoadRecent = QMenu(self.menuFile) self.menuLoadRecent.setTitle(tr("Load Recent Results")) self.setMenuBar(self.menubar) self.menuFile.addAction(self.actionLoadResults) self.menuFile.addAction(self.menuLoadRecent.menuAction()) self.menuFile.addSeparator() self.menuFile.addAction(self.app.actionQuit) self.menuView.addAction(self.app.actionPreferences) self.menuView.addAction(self.actionShowResultsWindow) self.menuView.addAction(self.app.actionIgnoreList) self.menuHelp.addAction(self.app.actionShowHelp) self.menuHelp.addAction(self.app.actionOpenDebugLog) self.menuHelp.addAction(self.app.actionAbout) self.menubar.addAction(self.menuFile.menuAction()) self.menubar.addAction(self.menuView.menuAction()) self.menubar.addAction(self.menuHelp.menuAction()) # Recent folders menu self.menuRecentFolders = QMenu() self.menuRecentFolders.addAction(self.actionAddFolder) self.menuRecentFolders.addSeparator() # Recent results menu self.menuRecentResults = QMenu() self.menuRecentResults.addAction(self.actionLoadResults) self.menuRecentResults.addSeparator() def _setupUi(self): self.setWindowTitle(self.app.NAME) self.resize(420, 338) self.centralwidget = QWidget(self) self.verticalLayout = QVBoxLayout(self.centralwidget) self.promptLabel = QLabel( tr("Select folders to scan and press \"Scan\"."), self.centralwidget) self.verticalLayout.addWidget(self.promptLabel) self.treeView = QTreeView(self.centralwidget) self.treeView.setSelectionMode(QAbstractItemView.ExtendedSelection) self.treeView.setSelectionBehavior(QAbstractItemView.SelectRows) self.treeView.setAcceptDrops(True) triggers = QAbstractItemView.DoubleClicked|QAbstractItemView.EditKeyPressed\ |QAbstractItemView.SelectedClicked self.treeView.setEditTriggers(triggers) self.treeView.setDragDropOverwriteMode(True) self.treeView.setDragDropMode(QAbstractItemView.DropOnly) self.treeView.setUniformRowHeights(True) self.verticalLayout.addWidget(self.treeView) self.horizontalLayout = QHBoxLayout() self.removeFolderButton = QPushButton(self.centralwidget) self.removeFolderButton.setIcon(QIcon(QPixmap(":/minus"))) self.removeFolderButton.setShortcut("Del") self.horizontalLayout.addWidget(self.removeFolderButton) self.addFolderButton = QPushButton(self.centralwidget) self.addFolderButton.setIcon(QIcon(QPixmap(":/plus"))) self.horizontalLayout.addWidget(self.addFolderButton) spacerItem1 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.horizontalLayout.addItem(spacerItem1) self.loadResultsButton = QPushButton(self.centralwidget) self.loadResultsButton.setText(tr("Load Results")) self.horizontalLayout.addWidget(self.loadResultsButton) self.scanButton = QPushButton(self.centralwidget) self.scanButton.setText(tr("Scan")) self.scanButton.setDefault(True) self.horizontalLayout.addWidget(self.scanButton) self.verticalLayout.addLayout(self.horizontalLayout) self.setCentralWidget(self.centralwidget) self._setupActions() self._setupMenu() if self.app.prefs.directoriesWindowRect is not None: self.setGeometry(self.app.prefs.directoriesWindowRect) else: moveToScreenCenter(self) def _setupColumns(self): header = self.treeView.header() header.setStretchLastSection(False) header.setSectionResizeMode(0, QHeaderView.Stretch) header.setSectionResizeMode(1, QHeaderView.Fixed) header.resizeSection(1, 100) def _updateAddButton(self): if self.recentFolders.isEmpty(): self.addFolderButton.setMenu(None) else: self.addFolderButton.setMenu(self.menuRecentFolders) def _updateRemoveButton(self): indexes = self.treeView.selectedIndexes() if not indexes: self.removeFolderButton.setEnabled(False) return self.removeFolderButton.setEnabled(True) def _updateLoadResultsButton(self): if self.app.recentResults.isEmpty(): self.loadResultsButton.setMenu(None) else: self.loadResultsButton.setMenu(self.menuRecentResults) #--- QWidget overrides def closeEvent(self, event): event.accept() if self.app.model.results.is_modified: title = tr("Unsaved results") msg = tr("You have unsaved results, do you really want to quit?") if not self.app.confirm(title, msg): event.ignore() if event.isAccepted(): QApplication.quit() #--- Events def addFolderTriggered(self): title = tr("Select a folder to add to the scanning list") flags = QFileDialog.ShowDirsOnly dirpath = str( QFileDialog.getExistingDirectory(self, title, self.lastAddedFolder, flags)) if not dirpath: return self.lastAddedFolder = dirpath self.app.model.add_directory(dirpath) self.recentFolders.insertItem(dirpath) def appWillSavePrefs(self): self.app.prefs.directoriesWindowRect = self.geometry() def directoriesModelAddedFolders(self, folders): for folder in folders: self.recentFolders.insertItem(folder) def loadResultsTriggered(self): title = tr("Select a results file to load") files = ';;'.join( [tr("dupeGuru Results (*.dupeguru)"), tr("All Files (*.*)")]) destination = QFileDialog.getOpenFileName(self, title, '', files) if destination: self.app.model.load_from(destination) self.app.recentResults.insertItem(destination) def removeFolderButtonClicked(self): self.directoriesModel.model.remove_selected() def scanButtonClicked(self): if self.app.model.results.is_modified: title = tr("Start a new scan") msg = tr( "You have unsaved results, do you really want to continue?") if not self.app.confirm(title, msg): return self.app.model.start_scanning() def selectionChanged(self, selected, deselected): self._updateRemoveButton()
class DirectoriesDialog(QMainWindow): def __init__(self, app, **kwargs): super().__init__(None, **kwargs) self.app = app self.lastAddedFolder = platform.INITIAL_FOLDER_IN_DIALOGS self.recentFolders = Recent(self.app, 'recentFolders') self._setupUi() self._updateScanTypeList() self.directoriesModel = DirectoriesModel(self.app.model.directory_tree, view=self.treeView) self.directoriesDelegate = DirectoriesDelegate() self.treeView.setItemDelegate(self.directoriesDelegate) self._setupColumns() self.app.recentResults.addMenu(self.menuLoadRecent) self.app.recentResults.addMenu(self.menuRecentResults) self.recentFolders.addMenu(self.menuRecentFolders) self._updateAddButton() self._updateRemoveButton() self._updateLoadResultsButton() self._updateActionsState() self._setupBindings() def _setupBindings(self): self.appModeRadioBox.itemSelected.connect(self.appModeButtonSelected) self.showPreferencesButton.clicked.connect(self.app.actionPreferences.trigger) self.scanButton.clicked.connect(self.scanButtonClicked) self.loadResultsButton.clicked.connect(self.actionLoadResults.trigger) self.addFolderButton.clicked.connect(self.actionAddFolder.trigger) self.removeFolderButton.clicked.connect(self.removeFolderButtonClicked) self.treeView.selectionModel().selectionChanged.connect(self.selectionChanged) self.app.recentResults.itemsChanged.connect(self._updateLoadResultsButton) self.recentFolders.itemsChanged.connect(self._updateAddButton) self.recentFolders.mustOpenItem.connect(self.app.model.add_directory) self.directoriesModel.foldersAdded.connect(self.directoriesModelAddedFolders) self.app.willSavePrefs.connect(self.appWillSavePrefs) def _setupActions(self): # (name, shortcut, icon, desc, func) ACTIONS = [ ('actionLoadResults', 'Ctrl+L', '', tr("Load Results..."), self.loadResultsTriggered), ('actionShowResultsWindow', '', '', tr("Results Window"), self.app.showResultsWindow), ('actionAddFolder', '', '', tr("Add Folder..."), self.addFolderTriggered), ] createActions(ACTIONS, self) def _setupMenu(self): self.menubar = QMenuBar(self) self.menubar.setGeometry(QRect(0, 0, 42, 22)) self.menuFile = QMenu(self.menubar) self.menuFile.setTitle(tr("File")) self.menuView = QMenu(self.menubar) self.menuView.setTitle(tr("View")) self.menuHelp = QMenu(self.menubar) self.menuHelp.setTitle(tr("Help")) self.menuLoadRecent = QMenu(self.menuFile) self.menuLoadRecent.setTitle(tr("Load Recent Results")) self.setMenuBar(self.menubar) self.menuFile.addAction(self.actionLoadResults) self.menuFile.addAction(self.menuLoadRecent.menuAction()) self.menuFile.addSeparator() self.menuFile.addAction(self.app.actionClearPictureCache) self.menuFile.addSeparator() self.menuFile.addAction(self.app.actionQuit) self.menuView.addAction(self.app.actionPreferences) self.menuView.addAction(self.actionShowResultsWindow) self.menuView.addAction(self.app.actionIgnoreList) self.menuHelp.addAction(self.app.actionShowHelp) self.menuHelp.addAction(self.app.actionOpenDebugLog) self.menuHelp.addAction(self.app.actionAbout) self.menubar.addAction(self.menuFile.menuAction()) self.menubar.addAction(self.menuView.menuAction()) self.menubar.addAction(self.menuHelp.menuAction()) # Recent folders menu self.menuRecentFolders = QMenu() self.menuRecentFolders.addAction(self.actionAddFolder) self.menuRecentFolders.addSeparator() # Recent results menu self.menuRecentResults = QMenu() self.menuRecentResults.addAction(self.actionLoadResults) self.menuRecentResults.addSeparator() def _setupUi(self): self.setWindowTitle(self.app.NAME) self.resize(420, 338) self.centralwidget = QWidget(self) self.verticalLayout = QVBoxLayout(self.centralwidget) hl = QHBoxLayout() label = QLabel(tr("Application Mode:"), self) label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) hl.addWidget(label) self.appModeRadioBox = RadioBox( self, items=[tr("Standard"), tr("Music"), tr("Picture")], spread=False ) hl.addWidget(self.appModeRadioBox) self.verticalLayout.addLayout(hl) hl = QHBoxLayout() hl.setAlignment(Qt.AlignLeft) label = QLabel(tr("Scan Type:"), self) label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) hl.addWidget(label) self.scanTypeComboBox = QComboBox(self) self.scanTypeComboBox.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)) self.scanTypeComboBox.setMaximumWidth(400) hl.addWidget(self.scanTypeComboBox) self.showPreferencesButton = QPushButton(tr("More Options"), self.centralwidget) self.showPreferencesButton.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) hl.addWidget(self.showPreferencesButton) self.verticalLayout.addLayout(hl) self.promptLabel = QLabel(tr("Select folders to scan and press \"Scan\"."), self.centralwidget) self.verticalLayout.addWidget(self.promptLabel) self.treeView = QTreeView(self.centralwidget) self.treeView.setSelectionMode(QAbstractItemView.ExtendedSelection) self.treeView.setSelectionBehavior(QAbstractItemView.SelectRows) self.treeView.setAcceptDrops(True) triggers = QAbstractItemView.DoubleClicked | QAbstractItemView.EditKeyPressed\ | QAbstractItemView.SelectedClicked self.treeView.setEditTriggers(triggers) self.treeView.setDragDropOverwriteMode(True) self.treeView.setDragDropMode(QAbstractItemView.DropOnly) self.treeView.setUniformRowHeights(True) self.verticalLayout.addWidget(self.treeView) self.horizontalLayout = QHBoxLayout() self.removeFolderButton = QPushButton(self.centralwidget) self.removeFolderButton.setIcon(QIcon(QPixmap(":/minus"))) self.removeFolderButton.setShortcut("Del") self.horizontalLayout.addWidget(self.removeFolderButton) self.addFolderButton = QPushButton(self.centralwidget) self.addFolderButton.setIcon(QIcon(QPixmap(":/plus"))) self.horizontalLayout.addWidget(self.addFolderButton) spacerItem1 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.horizontalLayout.addItem(spacerItem1) self.loadResultsButton = QPushButton(self.centralwidget) self.loadResultsButton.setText(tr("Load Results")) self.horizontalLayout.addWidget(self.loadResultsButton) self.scanButton = QPushButton(self.centralwidget) self.scanButton.setText(tr("Scan")) self.scanButton.setDefault(True) self.horizontalLayout.addWidget(self.scanButton) self.verticalLayout.addLayout(self.horizontalLayout) self.setCentralWidget(self.centralwidget) self._setupActions() self._setupMenu() if self.app.prefs.directoriesWindowRect is not None: self.setGeometry(self.app.prefs.directoriesWindowRect) else: moveToScreenCenter(self) def _setupColumns(self): header = self.treeView.header() header.setStretchLastSection(False) header.setSectionResizeMode(0, QHeaderView.Stretch) header.setSectionResizeMode(1, QHeaderView.Fixed) header.resizeSection(1, 100) def _updateActionsState(self): self.actionShowResultsWindow.setEnabled(self.app.resultWindow is not None) def _updateAddButton(self): if self.recentFolders.isEmpty(): self.addFolderButton.setMenu(None) else: self.addFolderButton.setMenu(self.menuRecentFolders) def _updateRemoveButton(self): indexes = self.treeView.selectedIndexes() if not indexes: self.removeFolderButton.setEnabled(False) return self.removeFolderButton.setEnabled(True) def _updateLoadResultsButton(self): if self.app.recentResults.isEmpty(): self.loadResultsButton.setMenu(None) else: self.loadResultsButton.setMenu(self.menuRecentResults) def _updateScanTypeList(self): try: self.scanTypeComboBox.currentIndexChanged[int].disconnect(self.scanTypeChanged) except TypeError: # Not connected, ignore pass self.scanTypeComboBox.clear() scan_options = self.app.model.SCANNER_CLASS.get_scan_options() for scan_option in scan_options: self.scanTypeComboBox.addItem(scan_option.label) SCAN_TYPE_ORDER = [so.scan_type for so in scan_options] selected_scan_type = self.app.prefs.get_scan_type(self.app.model.app_mode) scan_type_index = SCAN_TYPE_ORDER.index(selected_scan_type) self.scanTypeComboBox.setCurrentIndex(scan_type_index) self.scanTypeComboBox.currentIndexChanged[int].connect(self.scanTypeChanged) self.app._update_options() #--- QWidget overrides def closeEvent(self, event): event.accept() if self.app.model.results.is_modified: title = tr("Unsaved results") msg = tr("You have unsaved results, do you really want to quit?") if not self.app.confirm(title, msg): event.ignore() if event.isAccepted(): QApplication.quit() #--- Events def addFolderTriggered(self): title = tr("Select a folder to add to the scanning list") flags = QFileDialog.ShowDirsOnly dirpath = str(QFileDialog.getExistingDirectory(self, title, self.lastAddedFolder, flags)) if not dirpath: return self.lastAddedFolder = dirpath self.app.model.add_directory(dirpath) self.recentFolders.insertItem(dirpath) def appModeButtonSelected(self, index): if index == 2: mode = AppMode.Picture elif index == 1: mode = AppMode.Music else: mode = AppMode.Standard self.app.model.app_mode = mode self._updateScanTypeList() def appWillSavePrefs(self): self.app.prefs.directoriesWindowRect = self.geometry() def directoriesModelAddedFolders(self, folders): for folder in folders: self.recentFolders.insertItem(folder) def loadResultsTriggered(self): title = tr("Select a results file to load") files = ';;'.join([tr("dupeGuru Results (*.dupeguru)"), tr("All Files (*.*)")]) destination = QFileDialog.getOpenFileName(self, title, '', files) if destination: self.app.model.load_from(destination) self.app.recentResults.insertItem(destination) def removeFolderButtonClicked(self): self.directoriesModel.model.remove_selected() def scanButtonClicked(self): if self.app.model.results.is_modified: title = tr("Start a new scan") msg = tr("You have unsaved results, do you really want to continue?") if not self.app.confirm(title, msg): return self.app.model.start_scanning() def scanTypeChanged(self, index): scan_options = self.app.model.SCANNER_CLASS.get_scan_options() self.app.prefs.set_scan_type(self.app.model.app_mode, scan_options[index].scan_type) self.app._update_options() def selectionChanged(self, selected, deselected): self._updateRemoveButton()
class DirectoriesDialog(QMainWindow): def __init__(self, app, **kwargs): super().__init__(None, **kwargs) self.app = app self.lastAddedFolder = platform.INITIAL_FOLDER_IN_DIALOGS self.recentFolders = Recent(self.app, "recentFolders") self._setupUi() self.directoriesModel = DirectoriesModel(self.app.model.directory_tree, view=self.treeView) self.directoriesDelegate = DirectoriesDelegate() self.treeView.setItemDelegate(self.directoriesDelegate) self._setupColumns() self.app.recentResults.addMenu(self.menuLoadRecent) self.app.recentResults.addMenu(self.menuRecentResults) self.recentFolders.addMenu(self.menuRecentFolders) self._updateAddButton() self._updateRemoveButton() self._updateLoadResultsButton() self._setupBindings() def _setupBindings(self): self.scanButton.clicked.connect(self.scanButtonClicked) self.loadResultsButton.clicked.connect(self.actionLoadResults.trigger) self.addFolderButton.clicked.connect(self.actionAddFolder.trigger) self.removeFolderButton.clicked.connect(self.removeFolderButtonClicked) self.treeView.selectionModel().selectionChanged.connect(self.selectionChanged) self.app.recentResults.itemsChanged.connect(self._updateLoadResultsButton) self.recentFolders.itemsChanged.connect(self._updateAddButton) self.recentFolders.mustOpenItem.connect(self.app.model.add_directory) self.directoriesModel.foldersAdded.connect(self.directoriesModelAddedFolders) self.app.willSavePrefs.connect(self.appWillSavePrefs) def _setupActions(self): # (name, shortcut, icon, desc, func) ACTIONS = [ ("actionLoadResults", "Ctrl+L", "", tr("Load Results..."), self.loadResultsTriggered), ("actionShowResultsWindow", "", "", tr("Results Window"), self.app.showResultsWindow), ("actionAddFolder", "", "", tr("Add Folder..."), self.addFolderTriggered), ] createActions(ACTIONS, self) def _setupMenu(self): self.menubar = QMenuBar(self) self.menubar.setGeometry(QRect(0, 0, 42, 22)) self.menuFile = QMenu(self.menubar) self.menuFile.setTitle(tr("File")) self.menuView = QMenu(self.menubar) self.menuView.setTitle(tr("View")) self.menuHelp = QMenu(self.menubar) self.menuHelp.setTitle(tr("Help")) self.menuLoadRecent = QMenu(self.menuFile) self.menuLoadRecent.setTitle(tr("Load Recent Results")) self.setMenuBar(self.menubar) self.menuFile.addAction(self.actionLoadResults) self.menuFile.addAction(self.menuLoadRecent.menuAction()) self.menuFile.addSeparator() self.menuFile.addAction(self.app.actionQuit) self.menuView.addAction(self.app.actionPreferences) self.menuView.addAction(self.actionShowResultsWindow) self.menuView.addAction(self.app.actionIgnoreList) self.menuHelp.addAction(self.app.actionShowHelp) self.menuHelp.addAction(self.app.actionOpenDebugLog) self.menuHelp.addAction(self.app.actionAbout) self.menubar.addAction(self.menuFile.menuAction()) self.menubar.addAction(self.menuView.menuAction()) self.menubar.addAction(self.menuHelp.menuAction()) # Recent folders menu self.menuRecentFolders = QMenu() self.menuRecentFolders.addAction(self.actionAddFolder) self.menuRecentFolders.addSeparator() # Recent results menu self.menuRecentResults = QMenu() self.menuRecentResults.addAction(self.actionLoadResults) self.menuRecentResults.addSeparator() def _setupUi(self): self.setWindowTitle(self.app.NAME) self.resize(420, 338) self.centralwidget = QWidget(self) self.verticalLayout = QVBoxLayout(self.centralwidget) self.promptLabel = QLabel(tr('Select folders to scan and press "Scan".'), self.centralwidget) self.verticalLayout.addWidget(self.promptLabel) self.treeView = QTreeView(self.centralwidget) self.treeView.setSelectionMode(QAbstractItemView.ExtendedSelection) self.treeView.setSelectionBehavior(QAbstractItemView.SelectRows) self.treeView.setAcceptDrops(True) triggers = ( QAbstractItemView.DoubleClicked | QAbstractItemView.EditKeyPressed | QAbstractItemView.SelectedClicked ) self.treeView.setEditTriggers(triggers) self.treeView.setDragDropOverwriteMode(True) self.treeView.setDragDropMode(QAbstractItemView.DropOnly) self.treeView.setUniformRowHeights(True) self.verticalLayout.addWidget(self.treeView) self.horizontalLayout = QHBoxLayout() self.removeFolderButton = QPushButton(self.centralwidget) self.removeFolderButton.setIcon(QIcon(QPixmap(":/minus"))) self.removeFolderButton.setShortcut("Del") self.horizontalLayout.addWidget(self.removeFolderButton) self.addFolderButton = QPushButton(self.centralwidget) self.addFolderButton.setIcon(QIcon(QPixmap(":/plus"))) self.horizontalLayout.addWidget(self.addFolderButton) spacerItem1 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.horizontalLayout.addItem(spacerItem1) self.loadResultsButton = QPushButton(self.centralwidget) self.loadResultsButton.setText(tr("Load Results")) self.horizontalLayout.addWidget(self.loadResultsButton) self.scanButton = QPushButton(self.centralwidget) self.scanButton.setText(tr("Scan")) self.scanButton.setDefault(True) self.horizontalLayout.addWidget(self.scanButton) self.verticalLayout.addLayout(self.horizontalLayout) self.setCentralWidget(self.centralwidget) self._setupActions() self._setupMenu() if self.app.prefs.directoriesWindowRect is not None: self.setGeometry(self.app.prefs.directoriesWindowRect) else: moveToScreenCenter(self) def _setupColumns(self): header = self.treeView.header() header.setStretchLastSection(False) header.setSectionResizeMode(0, QHeaderView.Stretch) header.setSectionResizeMode(1, QHeaderView.Fixed) header.resizeSection(1, 100) def _updateAddButton(self): if self.recentFolders.isEmpty(): self.addFolderButton.setMenu(None) else: self.addFolderButton.setMenu(self.menuRecentFolders) def _updateRemoveButton(self): indexes = self.treeView.selectedIndexes() if not indexes: self.removeFolderButton.setEnabled(False) return self.removeFolderButton.setEnabled(True) def _updateLoadResultsButton(self): if self.app.recentResults.isEmpty(): self.loadResultsButton.setMenu(None) else: self.loadResultsButton.setMenu(self.menuRecentResults) # --- QWidget overrides def closeEvent(self, event): event.accept() if self.app.model.results.is_modified: title = tr("Unsaved results") msg = tr("You have unsaved results, do you really want to quit?") if not self.app.confirm(title, msg): event.ignore() if event.isAccepted(): QApplication.quit() # --- Events def addFolderTriggered(self): title = tr("Select a folder to add to the scanning list") flags = QFileDialog.ShowDirsOnly dirpath = str(QFileDialog.getExistingDirectory(self, title, self.lastAddedFolder, flags)) if not dirpath: return self.lastAddedFolder = dirpath self.app.model.add_directory(dirpath) self.recentFolders.insertItem(dirpath) def appWillSavePrefs(self): self.app.prefs.directoriesWindowRect = self.geometry() def directoriesModelAddedFolders(self, folders): for folder in folders: self.recentFolders.insertItem(folder) def loadResultsTriggered(self): title = tr("Select a results file to load") files = ";;".join([tr("dupeGuru Results (*.dupeguru)"), tr("All Files (*.*)")]) destination = QFileDialog.getOpenFileName(self, title, "", files) if destination: self.app.model.load_from(destination) self.app.recentResults.insertItem(destination) def removeFolderButtonClicked(self): self.directoriesModel.model.remove_selected() def scanButtonClicked(self): if self.app.model.results.is_modified: title = tr("Start a new scan") msg = tr("You have unsaved results, do you really want to continue?") if not self.app.confirm(title, msg): return self.app.model.start_scanning() def selectionChanged(self, selected, deselected): self._updateRemoveButton()
class Ui_PoloshirtWindow(QWidget): POLOSHİRTID, POLOSHİRTISIM, POLOSHİRTFIYAT, RESIMURL = range(4) def setupUi(self, PoloshirtWindow): PoloshirtWindow.setObjectName("PoloshirtWindow") PoloshirtWindow.resize(997, 704) PoloshirtWindow.setLayoutDirection(QtCore.Qt.LeftToRight) self.centralwidget = QtWidgets.QWidget(PoloshirtWindow) self.centralwidget.setObjectName("centralwidget") self.gridLayoutWidget = QtWidgets.QWidget(self.centralwidget) self.gridLayoutWidget.setGeometry(QtCore.QRect(60, 120, 791, 271)) self.gridLayoutWidget.setObjectName("gridLayoutWidget") self.gridLayout = QtWidgets.QGridLayout(self.gridLayoutWidget) self.gridLayout.setContentsMargins(0, 0, 0, 0) self.gridLayout.setObjectName("gridLayout") # bukısım c*k onemlı self.treeWidget = QTreeView(self.gridLayoutWidget) self.treeWidget.setEditTriggers( QtWidgets.QTableWidget.NoEditTriggers) # QtWidgets.QAbstractItemView.DoubleClicked | QtWidgets.QAbstractItemView.EditKeyPressed | QtWidgets.QAbstractItemView.SelectedClicked) self.treeWidget.setObjectName("treeWidget") self.gridLayout.addWidget(self.treeWidget, 0, 0, 2, 1) self.gridLayoutWidget_3 = QtWidgets.QWidget(self.centralwidget) self.gridLayoutWidget_3.setGeometry(QtCore.QRect(60, 390, 341, 231)) self.gridLayoutWidget_3.setObjectName("gridLayoutWidget_3") self.gridLayout_3 = QtWidgets.QGridLayout(self.gridLayoutWidget_3) self.gridLayout_3.setContentsMargins(0, 0, 0, 0) self.gridLayout_3.setObjectName("gridLayout_3") self.poloshirtisimlineEdit = QtWidgets.QLineEdit(self.gridLayoutWidget_3) font = QtGui.QFont() font.setPointSize(10) self.poloshirtisimlineEdit.setFont(font) self.poloshirtisimlineEdit.setObjectName("poloshirtisimlineEdit") self.gridLayout_3.addWidget(self.poloshirtisimlineEdit, 1, 1, 1, 1) self.poloshirtisimlabel = QtWidgets.QLabel(self.gridLayoutWidget_3) font = QtGui.QFont() font.setFamily("Microsoft Sans Serif") font.setPointSize(10) font.setBold(True) font.setWeight(75) self.poloshirtisimlabel.setFont(font) self.poloshirtisimlabel.setObjectName("poloshirtisimlabel") self.gridLayout_3.addWidget(self.poloshirtisimlabel, 1, 0, 1, 1) self.poloshirtfyatlabel = QtWidgets.QLabel(self.gridLayoutWidget_3) font = QtGui.QFont() font.setFamily("Microsoft Sans Serif") font.setPointSize(10) font.setBold(True) font.setWeight(75) self.poloshirtfyatlabel.setFont(font) self.poloshirtfyatlabel.setObjectName("poloshirtfyatlabel") self.gridLayout_3.addWidget(self.poloshirtfyatlabel, 2, 0, 1, 1) self.poloshirtidlineEdit = QtWidgets.QLineEdit(self.gridLayoutWidget_3) self.poloshirtidlineEdit.setEnabled(False) font = QtGui.QFont() font.setPointSize(10) self.poloshirtidlineEdit.setFont(font) self.poloshirtidlineEdit.setObjectName("poloshirtidlineEdit") self.gridLayout_3.addWidget(self.poloshirtidlineEdit, 0, 1, 1, 1) self.poloshirtidlabel = QtWidgets.QLabel(self.gridLayoutWidget_3) font = QtGui.QFont() font.setFamily("Microsoft Sans Serif") font.setPointSize(10) font.setBold(True) font.setWeight(75) self.poloshirtidlabel.setFont(font) self.poloshirtidlabel.setObjectName("poloshirtidlabel") self.gridLayout_3.addWidget(self.poloshirtidlabel, 0, 0, 1, 1) self.poloshirtfiyatlineEdit = QtWidgets.QLineEdit(self.gridLayoutWidget_3) font = QtGui.QFont() font.setPointSize(10) self.poloshirtfiyatlineEdit.setFont(font) self.poloshirtfiyatlineEdit.setObjectName("poloshirtfiyatlineEdit") self.gridLayout_3.addWidget(self.poloshirtfiyatlineEdit, 2, 1, 1, 1) self.poloshirtresimurllineEdit = QtWidgets.QLineEdit(self.gridLayoutWidget_3) font = QtGui.QFont() font.setPointSize(10) self.poloshirtresimurllineEdit.setFont(font) self.poloshirtresimurllineEdit.setObjectName("poloshirtresimurllineEdit") self.gridLayout_3.addWidget(self.poloshirtresimurllineEdit, 3, 1, 1, 1) self.poloshirtresimurllabel = QtWidgets.QLabel(self.gridLayoutWidget_3) font = QtGui.QFont() font.setFamily("Microsoft Sans Serif") font.setPointSize(10) font.setBold(True) font.setWeight(75) self.poloshirtresimurllabel.setFont(font) self.poloshirtresimurllabel.setObjectName("poloshirtresimurllabel") self.gridLayout_3.addWidget(self.poloshirtresimurllabel, 3, 0, 1, 1) self.verticalLayoutWidget = QtWidgets.QWidget(self.centralwidget) self.verticalLayoutWidget.setGeometry(QtCore.QRect(419, 410, 211, 211)) self.verticalLayoutWidget.setObjectName("verticalLayoutWidget") self.verticalLayout = QtWidgets.QVBoxLayout(self.verticalLayoutWidget) self.verticalLayout.setContentsMargins(0, 0, 0, 0) self.verticalLayout.setObjectName("verticalLayout") self.poloshirteklebuton = QtWidgets.QPushButton(self.verticalLayoutWidget) font = QtGui.QFont() font.setFamily("MS Sans Serif") font.setPointSize(10) font.setBold(True) font.setWeight(75) self.poloshirteklebuton.setFont(font) self.poloshirteklebuton.setObjectName("poloshirteklebuton") self.verticalLayout.addWidget(self.poloshirteklebuton) self.poloshirtduzenlebuton = QtWidgets.QPushButton(self.verticalLayoutWidget) font = QtGui.QFont() font.setFamily("MS Sans Serif") font.setPointSize(10) font.setBold(True) font.setWeight(75) self.poloshirtduzenlebuton.setFont(font) self.poloshirtduzenlebuton.setObjectName("poloshirtduzenlebuton") self.verticalLayout.addWidget(self.poloshirtduzenlebuton) self.poloshirtsilbuton = QtWidgets.QPushButton(self.verticalLayoutWidget) font = QtGui.QFont() font.setFamily("MS Sans Serif") font.setPointSize(10) font.setBold(True) font.setWeight(75) self.poloshirtsilbuton.setFont(font) self.poloshirtsilbuton.setObjectName("poloshirtsilbuton") self.verticalLayout.addWidget(self.poloshirtsilbuton) self.formLayoutWidget = QtWidgets.QWidget(self.centralwidget) self.formLayoutWidget.setGeometry(QtCore.QRect(650, 500, 271, 31)) self.formLayoutWidget.setObjectName("formLayoutWidget") self.formLayout = QtWidgets.QFormLayout(self.formLayoutWidget) self.formLayout.setContentsMargins(0, 0, 0, 0) self.formLayout.setObjectName("formLayout") font = QtGui.QFont() font.setFamily("MS Sans Serif") font.setPointSize(10) font.setBold(True) font.setWeight(75) font = QtGui.QFont() font.setPointSize(10) self.label = QtWidgets.QLabel(self.centralwidget) self.label.setGeometry(QtCore.QRect(190, 20, 561, 51)) font = QtGui.QFont() font.setFamily("Myanmar Text") font.setPointSize(12) font.setBold(True) font.setWeight(75) self.label.setFont(font) self.label.setFrameShape(QtWidgets.QFrame.NoFrame) self.label.setFrameShadow(QtWidgets.QFrame.Plain) self.label.setObjectName("label") self.label_3 = QtWidgets.QLabel(self.centralwidget) self.label_3.setGeometry(QtCore.QRect(350, 70, 170, 51)) font = QtGui.QFont() font.setFamily("Myanmar Text") font.setPointSize(18) font.setBold(True) font.setWeight(75) self.label_3.setFont(font) self.label_3.setFrameShape(QtWidgets.QFrame.NoFrame) self.label_3.setFrameShadow(QtWidgets.QFrame.Plain) self.label_3.setObjectName("label_3") PoloshirtWindow.setCentralWidget(self.centralwidget) self.statusbar = QtWidgets.QStatusBar(PoloshirtWindow) self.statusbar.setObjectName("statusbar") PoloshirtWindow.setStatusBar(self.statusbar) self.actionPoloshirt = QtWidgets.QAction(PoloshirtWindow) self.actionPoloshirt.setObjectName("actionPoloshirt") self.actionPoloshirt = QtWidgets.QAction(PoloshirtWindow) self.actionPoloshirt.setObjectName("actionPoloshirt") self.actionPoloshirt = QtWidgets.QAction(PoloshirtWindow) self.actionPoloshirt.setObjectName("actionPoloshirt") self.actioncgiyim = QtWidgets.QAction(PoloshirtWindow) self.actioncgiyim.setObjectName("actioncgiyim") self.retranslateUi(PoloshirtWindow) QtCore.QMetaObject.connectSlotsByName(PoloshirtWindow) #SORT SIRALAMA self.treeWidget.setSortingEnabled(True) #only int self.onlyInt = QIntValidator() self.poloshirtfiyatlineEdit.setValidator(self.onlyInt) # bu kısmı kullan nolur model = self.treetablemodelyaratma(self) self.treetablemodelyaratma2(model) self.treeWidget.setModel(model) # Buton clickleri self.treeWidget.doubleClicked.connect(self.on_doubleClicked) self.poloshirteklebuton.clicked.connect(self.eklebuton) self.poloshirtsilbuton.clicked.connect(self.silbuton) self.poloshirtduzenlebuton.clicked.connect(self.duzenlebuton) def treetablemodelyaratma(self, parent): model = QStandardItemModel(0, 4, parent) model.setHeaderData(self.POLOSHİRTID, Qt.Horizontal, "Poloshirt id") model.setHeaderData(self.POLOSHİRTISIM, Qt.Horizontal, "Poloshirt isim") model.setHeaderData(self.POLOSHİRTFIYAT, Qt.Horizontal, "Poloshirt fiyat") model.setHeaderData(self.RESIMURL, Qt.Horizontal, "Poloshirt Resimurl") return model def on_doubleClicked(self, index): item = self.treeWidget.currentIndex() item2 = item.model().itemFromIndex(index).text() self.poloshirtidlineEdit.setText(item2) # tabloyu update etme yöntemı self.updateview() def eklebuton(self): if (self.poloshirtfiyatlineEdit.text() =="" or self.poloshirtisimlineEdit.text()=="" or self.poloshirtresimurllineEdit.text()==""): return else: conn = pymysql.connect(host="localhost", user="******", passwd="12345678", db="bilisim_proje_odevi") cursor = conn.cursor(pymysql.cursors.DictCursor) query = "INSERT INTO `poloshirt` (poloshirt_isim, poloshirt_fiyat,poloshirt_resimurl) VALUES (%s,%s,%s)" val = (self.poloshirtisimlineEdit.text(), self.poloshirtfiyatlineEdit.text(), self.poloshirtresimurllineEdit.text()) cursor.execute(query, val) conn.commit() # tabloyu update etme yöntemı self.updateview() self.poloshirtfiyatlineEdit.clear() self.poloshirtisimlineEdit.clear() self.poloshirtresimurllineEdit.clear() self.poloshirtidlineEdit.clear() def silbuton(self): if (self.poloshirtidlineEdit.text()==""): return else: conn = pymysql.connect(host="localhost", user="******", passwd="12345678", db="bilisim_proje_odevi") cursor = conn.cursor(pymysql.cursors.DictCursor) query = "DELETE FROM `poloshirt` where id=%s" val = (self.poloshirtidlineEdit.text()) cursor.execute(query,val) conn.commit() self.updateview() self.poloshirtidlineEdit.clear() def duzenlebuton(self): if (self.poloshirtidlineEdit.text()==""or self.poloshirtfiyatlineEdit.text() == "" or self.poloshirtisimlineEdit.text() == "" or self.poloshirtresimurllineEdit.text() == ""): return else: conn = pymysql.connect(host="localhost", user="******", passwd="12345678", db="bilisim_proje_odevi") cursor = conn.cursor(pymysql.cursors.DictCursor) query = "UPDATE `poloshirt` SET poloshirt_isim=%s,poloshirt_fiyat=%s,poloshirt_resimurl=%s where id=%s" val = (self.poloshirtisimlineEdit.text(),self.poloshirtfiyatlineEdit.text(),self.poloshirtresimurllineEdit.text(),self.poloshirtidlineEdit.text()) cursor.execute(query, val) conn.commit() self.updateview() self.poloshirtidlineEdit.clear() self.poloshirtfiyatlineEdit.clear() self.poloshirtisimlineEdit.clear() self.poloshirtresimurllineEdit.clear() self.poloshirtidlineEdit.clear() def updateview(self): model = self.treetablemodelyaratma(self) self.treeWidget.setModel(model) self.treetablemodelyaratma2(model) def treetablemodelyaratma2(self, model): conn = pymysql.connect(host="localhost", user="******", passwd="12345678", db="bilisim_proje_odevi") cursor = conn.cursor(pymysql.cursors.DictCursor) query = "SELECT * FROM `poloshirt`" cursor.execute(query) data = cursor.fetchall() for row in data: model.insertRow(0) model.setData(model.index(0, self.POLOSHİRTID), row['id']) model.setData(model.index(0, self.POLOSHİRTISIM), row['poloshirt_isim']) model.setData(model.index(0, self.POLOSHİRTFIYAT), row['poloshirt_fiyat']) model.setData(model.index(0, self.RESIMURL), row['poloshirt_resimurl']) def retranslateUi(self, PoloshirtWindow): _translate = QtCore.QCoreApplication.translate PoloshirtWindow.setWindowTitle(_translate("PoloshirtWindow", "Poloshirtwindow")) self.poloshirtisimlabel.setText(_translate("PoloshirtWindow", "Poloshirt isim:")) self.poloshirtfyatlabel.setText(_translate("PoloshirtWindow", "Poloshirt Fiyat:")) self.poloshirtidlabel.setText(_translate("PoloshirtWindow", "ID:")) self.poloshirtresimurllabel.setText(_translate("PoloshirtWindow", "Resim_Url")) self.poloshirteklebuton.setText(_translate("PoloshirtWindow", "Ekle")) self.poloshirtduzenlebuton.setText(_translate("PoloshirtWindow", "Düzenle")) self.poloshirtsilbuton.setText(_translate("PoloshirtWindow", "Sil")) self.label.setText(_translate("PoloshirtWindow", "TAYFUN TESKTİL ÜRÜN DÜZENLEME")) self.label_3.setText(_translate("PoloshirtWindow", "POLOSHİRT")) self.actionPoloshirt.setText(_translate("PoloshirtWindow", "Poloshirt")) self.actionPoloshirt.setText(_translate("PoloshirtWindow", "Poloshirt")) self.actionPoloshirt.setText(_translate("PoloshirtWindow", "Poloshirt")) self.actioncgiyim.setText(_translate("PoloshirtWindow", "İcgiyim"))
class OpenedFileExplorer(DockWidget): """Opened File Explorer is list widget with list of opened files. It implements switching current file, files sorting. Uses _OpenedFileModel internally. Class instance created by Workspace. """ def __init__(self, workspace): DockWidget.__init__(self, workspace, "&Opened Files", QIcon(":/enkiicons/filtered.png"), "Alt+O") self._workspace = workspace self.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea) self.tvFiles = QTreeView(self) self.tvFiles.setHeaderHidden(True) self.tvFiles.setEditTriggers(QAbstractItemView.SelectedClicked) self.tvFiles.setContextMenuPolicy(Qt.CustomContextMenu) self.tvFiles.setDragEnabled(True) self.tvFiles.setDragDropMode(QAbstractItemView.InternalMove) self.tvFiles.setRootIsDecorated(False) self.tvFiles.setTextElideMode(Qt.ElideMiddle) self.tvFiles.setUniformRowHeights(True) self.tvFiles.customContextMenuRequested.connect( self._onTvFilesCustomContextMenuRequested) self.setWidget(self.tvFiles) self.setFocusProxy(self.tvFiles) self.model = _OpenedFileModel( self) # Not protected, because used by Configurator self.tvFiles.setModel(self.model) self.tvFiles.setAttribute(Qt.WA_MacShowFocusRect, False) self.tvFiles.setAttribute(Qt.WA_MacSmallSize) self._workspace.currentDocumentChanged.connect( self._onCurrentDocumentChanged) # disconnected by startModifyModel() self.tvFiles.selectionModel().selectionChanged.connect( self._onSelectionModelSelectionChanged) self.tvFiles.activated.connect(self._workspace.focusCurrentDocument) core.actionManager().addAction("mView/aOpenedFiles", self.showAction()) def terminate(self): """Explicitly called destructor """ core.actionManager().removeAction("mView/aOpenedFiles") def startModifyModel(self): """Blocks signals from model while it is modified by code """ self.tvFiles.selectionModel().selectionChanged.disconnect( self._onSelectionModelSelectionChanged) def finishModifyModel(self): """Unblocks signals from model """ self.tvFiles.selectionModel().selectionChanged.connect( self._onSelectionModelSelectionChanged) @pyqtSlot(Document, Document) def _onCurrentDocumentChanged(self, oldDocument, currentDocument): # pylint: disable=W0613 """ Current document has been changed on workspace """ if currentDocument is not None: index = self.model.documentIndex(currentDocument) self.startModifyModel() self.tvFiles.setCurrentIndex(index) # scroll the view self.tvFiles.scrollTo(index) self.finishModifyModel() @pyqtSlot(QItemSelection, QItemSelection) def _onSelectionModelSelectionChanged(self, selected, deselected): # pylint: disable=W0613 """ Item selected in the list. Switch current document """ if not selected.indexes(): # empty list, last file closed return index = selected.indexes()[0] # backup/restore current focused widget as setting active mdi window will steal it focusWidget = self.window().focusWidget() # set current document document = self._workspace.sortedDocuments[index.row()] self._workspace.setCurrentDocument(document) # restore focus widget if focusWidget: focusWidget.setFocus() @pyqtSlot(QPoint) def _onTvFilesCustomContextMenuRequested(self, pos): """Connected automatically by uic """ menu = QMenu() menu.addAction(core.actionManager().action("mFile/mClose/aCurrent")) menu.addAction(core.actionManager().action("mFile/mSave/aCurrent")) menu.addAction(core.actionManager().action("mFile/mReload/aCurrent")) menu.addSeparator() menu.addAction( core.actionManager().action("mFile/mFileSystem/aRename")) toggleExecutableAction = core.actionManager().action( "mFile/mFileSystem/aToggleExecutable") if toggleExecutableAction: # not available on Windows menu.addAction(toggleExecutableAction) core.actionManager().action("mFile/mFileSystem").menu( ).aboutToShow.emit() # to update aToggleExecutable menu.exec_(self.tvFiles.mapToGlobal(pos))
class OperacaoLogistica(QDialog): #TODO: Acertar formatação na listagem de items por SIMAFIC def __init__(self, parent=None): super().__init__(parent) self.setWindowFlags(Qt.WindowMinMaxButtonsHint | Qt.WindowCloseButtonHint) self.setWindowTitle('Operação Logística') self.setMinimumSize(QSize(h_size, v_size)) self.setWindowIcon(QIcon(main_icon)) verticalSpacer = QSpacerItem(40, 20, QSizePolicy.Minimum, QSizePolicy.Expanding) #Pedido Input Field self.proxy_list_result_id = QSortFilterProxyModel() self.numero_pedido = QLineEdit(self) self.numero_pedido.setPlaceholderText("Insira o Número do Pedido") self.numero_pedido.textChanged.connect( lambda wildcard: self.proxy_list_result_id.setFilterWildcard( wildcard)) #Voltar Btn self.voltar_btn = QPushButton(self) #self.voltar_btn.setStyleSheet('background-color: rgb(0,0,255); color: #fff') self.voltar_btn.setText('Voltar') self.voltar_btn.clicked.connect(self.goMainWindow) self.close() #Adicionar Cores no StyleSheet colors = ['##393318', ' ##fff'] self.pedidos = services.get_all_pedidos() self.item_result = None self.item_escolhido = None self.id_pedido_list = QListView() self.simafics_do_id = QListWidget() #self.simafics_do_id.setHidden(True) self.createPedidoIdList() self.id_pedido_list.clicked.connect( lambda id_pedido: self.createListaSimafics(id_pedido)) self.simafics_do_id.itemDoubleClicked.connect( lambda pedido: self.simaficSelecionado(pedido)) self.pedidos_label = QLabel() self.pedidos_label.setBuddy(self.id_pedido_list) self.simafic_label = QLabel() self.simafic_label.setBuddy(self.simafics_do_id) self.itensTree = PedidoItensTree() self.treeItensTV = QTreeView() self.treeItensTV.setEditTriggers(QAbstractItemView.NoEditTriggers) self.treeItensTV.setAlternatingRowColors(True) self.treeItensTV.setRootIsDecorated(True) self.treeItensTV.doubleClicked.connect(self.simaficSelecionado) self.treeItensTV.setColumnHidden(8, True) if len(self.pedidos) <= 0: self.pedidos_label.setText( "É necessário adicionar um pedido na tela de cadastro.") self.pedidos_label.setStyleSheet("QLabel { color: red; }") else: self.pedidos_label.setText("Listagem de Pedidos:") self.pedidos_label.setStyleSheet("QLabel { color: black; }") self.simafic_label.setText( "Selecione um pedido para ver a listagem de Itens por SIMAFIC:" ) self.simafic_label.setStyleSheet("QLabel { color: red; }") layout = QGridLayout() layout.setColumnStretch(0, 1) layout.setColumnStretch(1, 4) layout.addWidget(self.numero_pedido, 0, 0) layout.addWidget(self.voltar_btn, 0, 1) layout.addWidget(self.pedidos_label, 1, 0) layout.addWidget(self.simafic_label, 1, 1) layout.addWidget(self.id_pedido_list, 2, 0) layout.addWidget(self.treeItensTV, 2, 1) #layout.addWidget(self.simafics_do_id, 2,1) self.setLayout(layout) def createPedidoIdList(self): print('def createPedidoIdList(self):') onlyids = set() pedidosbyid = [] pedidosCompletos = [] self.proxy_list_result_id = QSortFilterProxyModel() for obj in self.pedidos: if obj.id_pedido not in onlyids: pedidosbyid.append(obj) onlyids.add(obj.id_pedido) self.pedidoId_model = QStringListModel(onlyids, self) self.proxy_list_result_id.setSourceModel(self.pedidoId_model) self.id_pedido_list.setModel(self.proxy_list_result_id) self.id_pedido_list.setAlternatingRowColors(True) self.id_pedido_list.setEditTriggers(QAbstractItemView.NoEditTriggers) def createListaSimafics(self, id_pedido): pedido = id_pedido.data() self.pedidosModel = self.itensTree.createPedidosModel(self.itensTree) self.treeItensTV.setModel(self.pedidosModel) print('def listaSimafics(self, id_pedido): {id_pedido}'.format( id_pedido=pedido)) self.item_result = None self.item_result = [x for x in self.pedidos if x.id_pedido == pedido] self.simafics_do_id.clear() self.pedidosModel.beginResetModel self.pedidosModel.modelReset self.pedidosModel.endResetModel for idx, item in enumerate(self.item_result): print(item) self.itensTree.addItens( self.pedidosModel, item.cod_simafic, item.desc, item.qty_scanneada, item.qty_total, item.nome_responsavel, item.id_caixa, item.time_updated.strftime("%d/%m/%y %H:%M:%S"), item.id_pedido, item) self.simafic_label.setText( "Listagem de Itens do pedido {} por SIMAFIC:".format(pedido)) self.simafic_label.setStyleSheet("QLabel { color: black; }") #self.simafics_do_id.setHidden(False) def simaficSelecionado(self, item): print(item.column(), item.row()) simafic_escolhido = self.treeItensTV.model().index(item.row(), 0).data() id_pedido = self.treeItensTV.model().index(item.row(), 7).data() self.item_escolhido = [ x for x in self.item_result if x.cod_simafic == simafic_escolhido and x.id_pedido == id_pedido ] self.cams = ItemScanner(self.item_escolhido[0]) self.cams.show() self.close() def goMainWindow(self): self.cams = mainView self.cams.show() self.close() def goScan(self): self.cams = ItemScanner("Eu sou o Pedido", "Eu sou o Simafic", "Eu sou a Descrição", "300") self.cams.show() self.close()
class ProjectStatusDialog(QDialog): icons = { 'added': 'images/FA_icons/plus.svg', 'removed': 'images/FA_icons/trash.svg', 'updated': 'images/FA_icons/edit.svg', 'renamed': 'images/FA_icons/edit.svg', 'table': 'images/FA_icons/table.svg' } def __init__(self, pull_changes, push_changes, push_changes_summary, has_write_permissions, parent=None): super(ProjectStatusDialog, self).__init__(parent) self.setWindowTitle("Project status") self.table = QTreeView() self.table.setEditTriggers(QAbstractItemView.NoEditTriggers) self.model = QStandardItemModel() self.model.setHorizontalHeaderLabels(["Status"]) self.table.setModel(self.model) self.add_content(pull_changes, 'Server changes', True) self.add_content(push_changes, 'Local changes', False, push_changes_summary) self.table.expandAll() box = QDialogButtonBox( QDialogButtonBox.Ok, centerButtons=True, ) box.accepted.connect(self.accept) box.rejected.connect(self.reject) lay = QVBoxLayout(self) lay.addWidget(self.table) has_files_to_replace = any([ "diff" not in file and is_versioned_file(file['path']) for file in push_changes["updated"] ]) info_text = self._get_info_text(has_files_to_replace, has_write_permissions) if info_text: text_box = QLabel() text_box.setWordWrap(True) text_box.setText(info_text) lay.addWidget(text_box) lay.addWidget(box, Qt.AlignCenter) self.resize(640, 640) def _get_info_text(self, has_files_to_replace, has_write_permissions): msg = "" if not has_write_permissions: msg += f"WARNING: You don't have writing permissions to this project. Changes won't be synced!\n" if has_files_to_replace: msg += f"\nWARNING: Unable to compare some of the modified files with their server version - " \ f"their history will be lost if uploaded." return msg def add_content(self, changes, root_text, is_server, changes_summary={}): """ Adds rows with changes info :param changes: Dict of added/removed/updated/renamed changes :param root_text: Text for the root item :param is_server: True if changes are related to server file changes :param changes_summary: If given and non empty, extra rows are added from geodiff summary. :return: """ if all(not changes[k] for k in changes): return root_item = QStandardItem(root_text) self.model.appendRow(root_item) for category in changes: for file in changes[category]: path = file['path'] item = self._get_icon_item(category, path) if is_versioned_file(path): if path in changes_summary: for sub_item in self._versioned_file_summary_items( changes_summary[path]['geodiff_summary']): item.appendRow(sub_item) elif not is_server: item.appendRow( QStandardItem("Unable to detect changes")) root_item.appendRow(item) def _versioned_file_summary_items(self, geodiff_summary): items = [] for s in geodiff_summary: table_name_item = self._get_icon_item('table', s['table']) for row in self._table_summary_items(s): table_name_item.appendRow(row) items.append(table_name_item) return items def _table_summary_items(self, summary): return [ QStandardItem("{}: {}".format(k, summary[k])) for k in summary if k != 'table' ] def _get_icon_item(self, key, text): path = os.path.join(os.path.dirname(os.path.realpath(__file__)), self.icons[key]) item = QStandardItem(text) item.setIcon(QIcon(path)) return item
class DataViewer(QWidget): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.setMinimumSize(500, 400) main_layout = QHBoxLayout() main_layout.setSpacing(5) self.setLayout(main_layout) control_column = QVBoxLayout() main_layout.addLayout(control_column, stretch=1) refresh_button = QPushButton('🔄 Refresh') refresh_button.setFont(cn.EMOJI_FONT) refresh_button.clicked.connect(self.refresh_list) control_column.addWidget(refresh_button) self.gesture_tree_view = QTreeView() self.gesture_tree_view.setMinimumWidth(250) self.gesture_tree_view.header().hide() self.gesture_tree_view.setEditTriggers( QAbstractItemView.NoEditTriggers) self.gesture_tree_view.clicked.connect(self.show_selected) self.gesture_tree_view.setContextMenuPolicy(Qt.CustomContextMenu) self.gesture_tree_view.customContextMenuRequested.connect( self.gesture_context_menu) self.gesture_model = QStandardItemModel() self.gesture_tree_view.setModel(self.gesture_model) self.gesture_tree_view.setAnimated(True) control_column.addWidget(self.gesture_tree_view) self.displayed_gestures_layout = QVBoxLayout() display_column = QVBoxLayout() close_all_button = QPushButton('❌ Close all opened') close_all_button.setFont(cn.EMOJI_FONT) def close_all_displayed_gestures(): for i in range(self.displayed_gestures_layout.count()): self.displayed_gestures_layout.itemAt(i).widget().close() close_all_button.clicked.connect(close_all_displayed_gestures) control_column.addWidget(close_all_button) display_column.addLayout( VerticalScrollableExtension(self.displayed_gestures_layout)) main_layout.addLayout(display_column, stretch=2) self.refresh_list() def refresh_list(self): gestures = sorted(os.listdir(cn.DATA_FOLDER)) gesture_tree = {} for gesture in gestures: parts = gesture.split(cn.FILE_NAME_SEPARATOR) if parts[0] == cn.SESSION_PREFIX: continue if len(parts) < 3 or parts[0] != cn.GESTURE_PREFIX: logger.debug(f'Skipping file {gesture}, unknown naming.') continue index = int(parts[1]) if index < 0 or index >= len(cn.GESTURES): logger.debug(f'Invalid index on {gesture}, skipping.') continue gesture = cn.GESTURES[index] parts[1] = str(gesture) current_node = gesture_tree for part in parts[1:]: current_node = current_node.setdefault(part, {}) self.gesture_model.clear() root = self.gesture_model.invisibleRootItem() def add_tree(tree: dict, node: QStandardItem): for item in tree: child_node = QStandardItem(item) node.appendRow(child_node) add_tree(tree[item], child_node) add_tree(gesture_tree, root) @staticmethod def get_filename(model_index): name = [] node = model_index while node.isValid(): name.append(node.data()) node = node.parent() name.append(cn.GESTURE_PREFIX) # TODO this could be nicer for i, gesture_spec in enumerate(cn.GESTURES): if str(gesture_spec) == name[-2]: name[-2] = str(i) return cn.FILE_NAME_SEPARATOR.join(name[::-1]) def show_selected(self, model_index): is_leaf = not model_index.child(0, 0).isValid() if not is_leaf: self.gesture_tree_view.setExpanded( model_index, not self.gesture_tree_view.isExpanded(model_index)) return filename = DataViewer.get_filename(model_index) selected_file = cn.DATA_FOLDER / filename data = np.load(selected_file) signal_widget = StaticSignalWidget() signal_widget.plot_data(data) widget = NamedExtension(filename, signal_widget) widget = BlinkExtension(widget) widget = ClosableExtension(widget) widget.setMinimumWidth(600) widget.setFixedHeight(200) self.displayed_gestures_layout.addWidget(widget) def gesture_context_menu(self, point): model_index = self.gesture_tree_view.indexAt(point) is_leaf = not model_index.child(0, 0).isValid() if not is_leaf: self.gesture_tree_view.setExpanded( model_index, not self.gesture_tree_view.isExpanded(model_index)) return menu = QMenu() def move_dialog(): Renamer(DataViewer.get_filename(model_index)).exec() menu.addAction('Move', move_dialog) def trash_and_remove_from_tree(): if Renamer.trash_gesture(DataViewer.get_filename(model_index)): self.gesture_model.removeRow(model_index.row(), model_index.parent()) menu.addAction('Trash', trash_and_remove_from_tree) menu.exec(QCursor.pos())
class _PlatformGui(QWidget): """ The platform-specific GUI. """ def __init__(self, platform_name): """ Initialise the object. """ super().__init__() self._project = None self._platform_name = platform_name self._ignore_extlib_changes = False layout = QVBoxLayout() self._pyshlib_cb = QCheckBox( "Use standard Python shared library", whatsThis="Use the standard Python shared library rather than " "a statically compiled library.", stateChanged=self._pyshlib_changed) layout.addWidget(self._pyshlib_cb) self._extlib_edit = QTreeView( whatsThis="This is the list of external libraries that must " "be linked with the application for this platform. A " "library will only be enabled if a module in the " "standard library uses it. Double-click in the " "<b>DEFINES</b>, <b>INCLUDEPATH</b> and <b>LIBS</b> " "columns to modify the corresponding <tt>qmake</tt> " "variable as required.") self._extlib_edit.setRootIsDecorated(False) self._extlib_edit.setEditTriggers(QTreeView.DoubleClicked | QTreeView.SelectedClicked | QTreeView.EditKeyPressed) model = QStandardItemModel(self._extlib_edit) model.setHorizontalHeaderLabels( ("External Library", 'DEFINES', 'INCLUDEPATH', 'LIBS')) model.itemChanged.connect(self._extlib_changed) model._items = {} for extlib in external_libraries_metadata: name_itm = QStandardItem(extlib.user_name) items = (name_itm, QStandardItem(), QStandardItem(), QStandardItem()) model.appendRow(items) model._items[extlib.name] = items self._extlib_edit.setModel(model) for col in range(3): self._extlib_edit.resizeColumnToContents(col) layout.addWidget(self._extlib_edit) self.setLayout(layout) def update_from_project(self, project): """ Update the GUI to reflect the current state of the project. """ self._project = project platform_name = self._platform_name # Update the shared library state. blocked = self._pyshlib_cb.blockSignals(True) self._pyshlib_cb.setCheckState(Qt.Checked if platform_name in project. python_use_platform else Qt.Unchecked) self._pyshlib_cb.blockSignals(blocked) # Update the external libraries. model = self._extlib_edit.model() blocked = model.blockSignals(True) external_libs = project.external_libraries.get(platform_name, []) for extlib in external_libraries_metadata: _, defs, incp, libs = model._items[extlib.name] for prj_extlib in external_libs: if prj_extlib.name == extlib.name: defs.setText(prj_extlib.defines) incp.setText(prj_extlib.includepath) libs.setText(prj_extlib.libs) break else: defs.setText(extlib.defines) incp.setText(extlib.includepath) libs.setText(extlib.get_libs(platform_name)) model.blockSignals(blocked) def update_from_required_libraries(self, required_libraries): """ Update the GUI as the required external libraries changes. """ items = self._extlib_edit.model()._items # Note that we can't simply block the model's signals as this would # interfere with the model/view interactions. self._ignore_extlib_changes = True for extlib in external_libraries_metadata: if extlib.name in required_libraries: for idx, itm in enumerate(items[extlib.name]): itm.setFlags( Qt.ItemIsEnabled | Qt.ItemIsEditable if idx != 0 else Qt.ItemIsEnabled) else: for itm in items[extlib.name]: itm.setFlags(Qt.NoItemFlags) self._ignore_extlib_changes = False def _pyshlib_changed(self, state): """ Invoked when the shared library state changes. """ project = self._project platform_name = self._platform_name if state == Qt.Checked: project.python_use_platform.append(platform_name) else: project.python_use_platform.remove(platform_name) project.modified = True def _extlib_changed(self, itm): """ Invoked when an external library has changed. """ if self._ignore_extlib_changes: return self._ignore_extlib_changes = True project = self._project platform_name = self._platform_name idx = self._extlib_edit.model().indexFromItem(itm) extlib = external_libraries_metadata[idx.row()] col = idx.column() # Get the project entry, creating it if necessary. external_libs = project.external_libraries.get(platform_name, []) for prj_extlib in external_libs: if prj_extlib.name == extlib.name: break else: prj_extlib = ExternalLibrary(extlib.name, '', '', extlib.get_libs(platform_name)) external_libs.append(prj_extlib) project.external_libraries[platform_name] = external_libs # Update the project. text = itm.text().strip() if col == 1: prj_extlib.defines = text elif col == 2: prj_extlib.includepath = text elif col == 3: prj_extlib.libs = text # If the project entry corresponds to the default then remove it. if prj_extlib.defines == extlib.defines and prj_extlib.includepath == extlib.includepath and prj_extlib.libs == extlib.get_libs( platform_name): external_libs.remove(prj_extlib) if len(external_libs) == 0: del project.external_libraries[platform_name] project.modified = True self._ignore_extlib_changes = False
class OpenedFileExplorer(DockWidget): """Opened File Explorer is list widget with list of opened files. It implements switching current file, files sorting. Uses _OpenedFileModel internally. Class instance created by Workspace. """ def __init__(self, workspace): DockWidget.__init__(self, workspace, "&Opened Files", QIcon(":/enkiicons/filtered.png"), "Alt+O") self._workspace = workspace self.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea) self.tvFiles = QTreeView(self) self.tvFiles.setHeaderHidden(True) self.tvFiles.setEditTriggers(QAbstractItemView.SelectedClicked) self.tvFiles.setContextMenuPolicy(Qt.CustomContextMenu) self.tvFiles.setDragEnabled(True) self.tvFiles.setDragDropMode(QAbstractItemView.InternalMove) self.tvFiles.setRootIsDecorated(False) self.tvFiles.setTextElideMode(Qt.ElideMiddle) self.tvFiles.setUniformRowHeights(True) self.tvFiles.customContextMenuRequested.connect(self._onTvFilesCustomContextMenuRequested) self.setWidget(self.tvFiles) self.setFocusProxy(self.tvFiles) self.model = _OpenedFileModel(self) # Not protected, because used by Configurator self.tvFiles.setModel(self.model) self.tvFiles.setAttribute(Qt.WA_MacShowFocusRect, False) self.tvFiles.setAttribute(Qt.WA_MacSmallSize) self._workspace.currentDocumentChanged.connect(self._onCurrentDocumentChanged) # disconnected by startModifyModel() self.tvFiles.selectionModel().selectionChanged.connect(self._onSelectionModelSelectionChanged) self.tvFiles.activated.connect(self._workspace.focusCurrentDocument) core.actionManager().addAction("mView/aOpenedFiles", self.showAction()) # Add auto-hide capability. self._waitForCtrlRelease = False core.actionManager().action("mNavigation/aNext").triggered.connect( self._setWaitForCtrlRelease) core.actionManager().action("mNavigation/aPrevious").triggered.connect( self._setWaitForCtrlRelease) QApplication.instance().installEventFilter(self) def terminate(self): """Explicitly called destructor """ core.actionManager().removeAction("mView/aOpenedFiles") QApplication.instance().removeEventFilter(self) def startModifyModel(self): """Blocks signals from model while it is modified by code """ self.tvFiles.selectionModel().selectionChanged.disconnect(self._onSelectionModelSelectionChanged) def finishModifyModel(self): """Unblocks signals from model """ self.tvFiles.selectionModel().selectionChanged.connect(self._onSelectionModelSelectionChanged) @pyqtSlot(Document, Document) def _onCurrentDocumentChanged(self, oldDocument, currentDocument): # pylint: disable=W0613 """ Current document has been changed on workspace """ if currentDocument is not None: index = self.model.documentIndex(currentDocument) self.startModifyModel() self.tvFiles.setCurrentIndex(index) # scroll the view self.tvFiles.scrollTo(index) self.finishModifyModel() @pyqtSlot(QItemSelection, QItemSelection) def _onSelectionModelSelectionChanged(self, selected, deselected): # pylint: disable=W0613 """ Item selected in the list. Switch current document """ if not selected.indexes(): # empty list, last file closed return index = selected.indexes()[0] # backup/restore current focused widget as setting active mdi window will steal it focusWidget = self.window().focusWidget() # set current document document = self._workspace.sortedDocuments[index.row()] self._workspace.setCurrentDocument(document) # restore focus widget if focusWidget: focusWidget.setFocus() @pyqtSlot(QPoint) def _onTvFilesCustomContextMenuRequested(self, pos): """Connected automatically by uic """ menu = QMenu() menu.addAction(core.actionManager().action("mFile/mClose/aCurrent")) menu.addAction(core.actionManager().action("mFile/mSave/aCurrent")) menu.addAction(core.actionManager().action("mFile/mReload/aCurrent")) menu.addSeparator() menu.addAction(core.actionManager().action("mFile/mFileSystem/aRename")) toggleExecutableAction = core.actionManager().action("mFile/mFileSystem/aToggleExecutable") if toggleExecutableAction: # not available on Windows menu.addAction(toggleExecutableAction) core.actionManager().action("mFile/mFileSystem").menu().aboutToShow.emit() # to update aToggleExecutable menu.exec_(self.tvFiles.mapToGlobal(pos)) def _setWaitForCtrlRelease(self): # We can't see actual Ctrl+PgUp/PgDn keypresses, since these get eaten # by the QAction and don't even show up in the event filter below. We # want to avoid waiting for a Ctrl release if the menu item brought us # here. As a workaround, check that Ctrl is pressed. If so, it's # unlikely to be the menu item. if QApplication.instance().keyboardModifiers() & Qt.ControlModifier: self._waitForCtrlRelease = True self.show() else: # If this was a menu selection, then update the MRU list. We can't # do this now, since the current document hasn't been changed yet. QTimer.singleShot(0, self.model.sortDocuments) def eventFilter(self, obj, event): """An event filter that looks for ctrl key releases and focus out events.""" # Wait for the user to release the Ctrl key. if ( self._waitForCtrlRelease and event.type() == QEvent.KeyRelease and event.key() == Qt.Key_Control and event.modifiers() == Qt.NoModifier): self.model.sortDocuments() self._waitForCtrlRelease = False if not self.isPinned(): self.hide() # Look for a focus out event sent by the containing widget's focus # proxy. if event.type() == QEvent.FocusOut and obj == self.focusProxy(): self.model.sortDocuments() return QObject.eventFilter(self, obj, event)
class DirectoriesDialog(QMainWindow): def __init__(self, app, **kwargs): super().__init__(None, **kwargs) self.app = app self.specific_actions = set() self.lastAddedFolder = platform.INITIAL_FOLDER_IN_DIALOGS self.recentFolders = Recent(self.app, "recentFolders") self._setupUi() self._updateScanTypeList() self.directoriesModel = DirectoriesModel(self.app.model.directory_tree, view=self.treeView) self.directoriesDelegate = DirectoriesDelegate() self.treeView.setItemDelegate(self.directoriesDelegate) self._setupColumns() self.app.recentResults.addMenu(self.menuLoadRecent) self.app.recentResults.addMenu(self.menuRecentResults) self.recentFolders.addMenu(self.menuRecentFolders) self._updateAddButton() self._updateRemoveButton() self._updateLoadResultsButton() self._updateActionsState() self._setupBindings() def _setupBindings(self): self.appModeRadioBox.itemSelected.connect(self.appModeButtonSelected) self.showPreferencesButton.clicked.connect( self.app.actionPreferences.trigger) self.scanButton.clicked.connect(self.scanButtonClicked) self.loadResultsButton.clicked.connect(self.actionLoadResults.trigger) self.addFolderButton.clicked.connect(self.actionAddFolder.trigger) self.removeFolderButton.clicked.connect(self.removeFolderButtonClicked) self.treeView.selectionModel().selectionChanged.connect( self.selectionChanged) self.app.recentResults.itemsChanged.connect( self._updateLoadResultsButton) self.recentFolders.itemsChanged.connect(self._updateAddButton) self.recentFolders.mustOpenItem.connect(self.app.model.add_directory) self.directoriesModel.foldersAdded.connect( self.directoriesModelAddedFolders) self.app.willSavePrefs.connect(self.appWillSavePrefs) def _setupActions(self): # (name, shortcut, icon, desc, func) ACTIONS = [ ( "actionLoadResults", "Ctrl+L", "", tr("Load Results..."), self.loadResultsTriggered, ), ( "actionShowResultsWindow", "", "", tr("Scan Results"), self.app.showResultsWindow, ), ("actionAddFolder", "", "", tr("Add Folder..."), self.addFolderTriggered), ("actionLoadDirectories", "", "", tr("Load Directories..."), self.loadDirectoriesTriggered), ("actionSaveDirectories", "", "", tr("Save Directories..."), self.saveDirectoriesTriggered), ] createActions(ACTIONS, self) if self.app.use_tabs: # Keep track of actions which should only be accessible from this window self.specific_actions.add(self.actionLoadDirectories) self.specific_actions.add(self.actionSaveDirectories) def _setupMenu(self): if not self.app.use_tabs: # we are our own QMainWindow, we need our own menu bar self.menubar = QMenuBar(self) self.menubar.setGeometry(QRect(0, 0, 42, 22)) self.menuFile = QMenu(self.menubar) self.menuFile.setTitle(tr("File")) self.menuView = QMenu(self.menubar) self.menuView.setTitle(tr("View")) self.menuHelp = QMenu(self.menubar) self.menuHelp.setTitle(tr("Help")) self.setMenuBar(self.menubar) menubar = self.menubar else: # we are part of a tab widget, we populate its window's menubar instead self.menuFile = self.app.main_window.menuFile self.menuView = self.app.main_window.menuView self.menuHelp = self.app.main_window.menuHelp menubar = self.app.main_window.menubar self.menuLoadRecent = QMenu(self.menuFile) self.menuLoadRecent.setTitle(tr("Load Recent Results")) self.menuFile.addAction(self.actionLoadResults) self.menuFile.addAction(self.menuLoadRecent.menuAction()) self.menuFile.addSeparator() self.menuFile.addAction(self.app.actionClearPictureCache) self.menuFile.addSeparator() self.menuFile.addAction(self.actionLoadDirectories) self.menuFile.addAction(self.actionSaveDirectories) self.menuFile.addSeparator() self.menuFile.addAction(self.app.actionQuit) self.menuView.addAction(self.app.actionDirectoriesWindow) self.menuView.addAction(self.actionShowResultsWindow) self.menuView.addAction(self.app.actionIgnoreList) self.menuView.addSeparator() self.menuView.addAction(self.app.actionPreferences) self.menuHelp.addAction(self.app.actionShowHelp) self.menuHelp.addAction(self.app.actionOpenDebugLog) self.menuHelp.addAction(self.app.actionAbout) menubar.addAction(self.menuFile.menuAction()) menubar.addAction(self.menuView.menuAction()) menubar.addAction(self.menuHelp.menuAction()) # Recent folders menu self.menuRecentFolders = QMenu() self.menuRecentFolders.addAction(self.actionAddFolder) self.menuRecentFolders.addSeparator() # Recent results menu self.menuRecentResults = QMenu() self.menuRecentResults.addAction(self.actionLoadResults) self.menuRecentResults.addSeparator() def _setupUi(self): self.setWindowTitle(self.app.NAME) self.resize(420, 338) self.centralwidget = QWidget(self) self.verticalLayout = QVBoxLayout(self.centralwidget) self.verticalLayout.setContentsMargins(4, 0, 4, 0) self.verticalLayout.setSpacing(0) hl = QHBoxLayout() label = QLabel(tr("Application Mode:"), self) label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) hl.addWidget(label) self.appModeRadioBox = RadioBox( self, items=[tr("Standard"), tr("Music"), tr("Picture")], spread=False) hl.addWidget(self.appModeRadioBox) self.verticalLayout.addLayout(hl) hl = QHBoxLayout() hl.setAlignment(Qt.AlignLeft) label = QLabel(tr("Scan Type:"), self) label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) hl.addWidget(label) self.scanTypeComboBox = QComboBox(self) self.scanTypeComboBox.setSizePolicy( QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)) self.scanTypeComboBox.setMaximumWidth(400) hl.addWidget(self.scanTypeComboBox) self.showPreferencesButton = QPushButton(tr("More Options"), self.centralwidget) self.showPreferencesButton.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) hl.addWidget(self.showPreferencesButton) self.verticalLayout.addLayout(hl) self.promptLabel = QLabel( tr('Select folders to scan and press "Scan".'), self.centralwidget) self.verticalLayout.addWidget(self.promptLabel) self.treeView = QTreeView(self.centralwidget) self.treeView.setSelectionMode(QAbstractItemView.ExtendedSelection) self.treeView.setSelectionBehavior(QAbstractItemView.SelectRows) self.treeView.setAcceptDrops(True) triggers = (QAbstractItemView.DoubleClicked | QAbstractItemView.EditKeyPressed | QAbstractItemView.SelectedClicked) self.treeView.setEditTriggers(triggers) self.treeView.setDragDropOverwriteMode(True) self.treeView.setDragDropMode(QAbstractItemView.DropOnly) self.treeView.setUniformRowHeights(True) self.verticalLayout.addWidget(self.treeView) self.horizontalLayout = QHBoxLayout() self.removeFolderButton = QPushButton(self.centralwidget) self.removeFolderButton.setIcon(QIcon(QPixmap(":/minus"))) self.removeFolderButton.setShortcut("Del") self.horizontalLayout.addWidget(self.removeFolderButton) self.addFolderButton = QPushButton(self.centralwidget) self.addFolderButton.setIcon(QIcon(QPixmap(":/plus"))) self.horizontalLayout.addWidget(self.addFolderButton) spacerItem1 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.horizontalLayout.addItem(spacerItem1) self.loadResultsButton = QPushButton(self.centralwidget) self.loadResultsButton.setText(tr("Load Results")) self.horizontalLayout.addWidget(self.loadResultsButton) self.scanButton = QPushButton(self.centralwidget) self.scanButton.setText(tr("Scan")) self.scanButton.setDefault(True) self.horizontalLayout.addWidget(self.scanButton) self.verticalLayout.addLayout(self.horizontalLayout) self.setCentralWidget(self.centralwidget) self._setupActions() self._setupMenu() if self.app.prefs.directoriesWindowRect is not None: self.setGeometry(self.app.prefs.directoriesWindowRect) else: moveToScreenCenter(self) def _setupColumns(self): header = self.treeView.header() header.setStretchLastSection(False) header.setSectionResizeMode(0, QHeaderView.Stretch) header.setSectionResizeMode(1, QHeaderView.Fixed) header.resizeSection(1, 100) def _updateActionsState(self): self.actionShowResultsWindow.setEnabled( self.app.resultWindow is not None) def _updateAddButton(self): if self.recentFolders.isEmpty(): self.addFolderButton.setMenu(None) else: self.addFolderButton.setMenu(self.menuRecentFolders) def _updateRemoveButton(self): indexes = self.treeView.selectedIndexes() if not indexes: self.removeFolderButton.setEnabled(False) return self.removeFolderButton.setEnabled(True) def _updateLoadResultsButton(self): if self.app.recentResults.isEmpty(): self.loadResultsButton.setMenu(None) else: self.loadResultsButton.setMenu(self.menuRecentResults) def _updateScanTypeList(self): try: self.scanTypeComboBox.currentIndexChanged[int].disconnect( self.scanTypeChanged) except TypeError: # Not connected, ignore pass self.scanTypeComboBox.clear() scan_options = self.app.model.SCANNER_CLASS.get_scan_options() for scan_option in scan_options: self.scanTypeComboBox.addItem(scan_option.label) SCAN_TYPE_ORDER = [so.scan_type for so in scan_options] selected_scan_type = self.app.prefs.get_scan_type( self.app.model.app_mode) scan_type_index = SCAN_TYPE_ORDER.index(selected_scan_type) self.scanTypeComboBox.setCurrentIndex(scan_type_index) self.scanTypeComboBox.currentIndexChanged[int].connect( self.scanTypeChanged) self.app._update_options() # --- QWidget overrides def closeEvent(self, event): event.accept() if self.app.model.results.is_modified: title = tr("Unsaved results") msg = tr("You have unsaved results, do you really want to quit?") if not self.app.confirm(title, msg): event.ignore() if event.isAccepted(): self.app.shutdown() # --- Events def addFolderTriggered(self): title = tr("Select a folder to add to the scanning list") flags = QFileDialog.ShowDirsOnly dirpath = str( QFileDialog.getExistingDirectory(self, title, self.lastAddedFolder, flags)) if not dirpath: return self.lastAddedFolder = dirpath self.app.model.add_directory(dirpath) self.recentFolders.insertItem(dirpath) def appModeButtonSelected(self, index): if index == 2: mode = AppMode.Picture elif index == 1: mode = AppMode.Music else: mode = AppMode.Standard self.app.model.app_mode = mode self._updateScanTypeList() def appWillSavePrefs(self): self.app.prefs.directoriesWindowRect = self.geometry() def directoriesModelAddedFolders(self, folders): for folder in folders: self.recentFolders.insertItem(folder) def loadResultsTriggered(self): title = tr("Select a results file to load") files = ";;".join( [tr("dupeGuru Results (*.dupeguru)"), tr("All Files (*.*)")]) destination = QFileDialog.getOpenFileName(self, title, "", files)[0] if destination: self.app.model.load_from(destination) self.app.recentResults.insertItem(destination) def loadDirectoriesTriggered(self): title = tr("Select a directories file to load") files = ";;".join( [tr("dupeGuru Results (*.dupegurudirs)"), tr("All Files (*.*)")]) destination = QFileDialog.getOpenFileName(self, title, "", files)[0] if destination: self.app.model.load_directories(destination) def removeFolderButtonClicked(self): self.directoriesModel.model.remove_selected() def saveDirectoriesTriggered(self): title = tr("Select a file to save your directories to") files = tr("dupeGuru Directories (*.dupegurudirs)") destination, chosen_filter = QFileDialog.getSaveFileName( self, title, "", files) if destination: if not destination.endswith(".dupegurudirs"): destination = "{}.dupegurudirs".format(destination) self.app.model.save_directories_as(destination) def scanButtonClicked(self): if self.app.model.results.is_modified: title = tr("Start a new scan") msg = tr( "You have unsaved results, do you really want to continue?") if not self.app.confirm(title, msg): return self.app.model.start_scanning() def scanTypeChanged(self, index): scan_options = self.app.model.SCANNER_CLASS.get_scan_options() self.app.prefs.set_scan_type(self.app.model.app_mode, scan_options[index].scan_type) self.app._update_options() def selectionChanged(self, selected, deselected): self._updateRemoveButton()
class ChessClaimView(QMainWindow): """ The main window of the application. Attributes: rowCount(int): The number of the row the TreeView Table has. iconsSize(int): The recommended size of the icons. mac_notification: Notification for macOS win_notification: Notification for windows OS """ def __init__(self): super().__init__() self.resize(720, 275) self.iconsSize = 16 self.setWindowTitle('Chess Claim Tool') self.center() self.rowCount = 0 if (platform.system() == "Darwin"): from MacNotification import Notification self.mac_notification = Notification() elif (platform.system() == "Windows"): from win10toast import ToastNotifier self.win_notification = ToastNotifier() def center(self): """ Centers the window on the screen """ screen = QDesktopWidget().screenGeometry() size = self.geometry() self.move((screen.width()-size.width())/2, (screen.height()-size.height())/2) def set_GUI(self): """ Initialize GUI components. """ # Create the Menu self.livePgnOption = QAction('Live PGN',self) self.livePgnOption.setCheckable(True) aboutAction = QAction('About',self) menubar = self.menuBar() optionsMenu = menubar.addMenu('&Options') optionsMenu.addAction(self.livePgnOption) aboutMenu = menubar.addMenu('&Help') aboutMenu.addAction(aboutAction) aboutAction.triggered.connect(self.slots.on_about_clicked) # Create the Claims Table (TreeView) self.claimsTable = QTreeView() self.claimsTable.setFocusPolicy(Qt.NoFocus) self.claimsTable.setEditTriggers(QAbstractItemView.NoEditTriggers) self.claimsTable.header().setDefaultAlignment(Qt.AlignCenter) self.claimsTable.setSortingEnabled(True) self.claimsTable.doubleClicked.connect(self.open_game) # Create the Claims Model self.claimsTableModel = QStandardItemModel() labels = ["#","Timestamp","Type","Board","Players","Move"] self.claimsTableModel.setHorizontalHeaderLabels(labels) self.claimsTable.setModel(self.claimsTableModel) # Create the Scan & Stop Button Box self.buttonBox = ButtonBox(self) # Create the Sources Button sourcesButton = QPushButton("Add Sources") sourcesButton.setObjectName("sources") sourcesButton.clicked.connect(self.slots.on_sourcesButton_clicked) # Create the Status Bar self.pixmapCheck = QPixmap(resource_path("check_icon.png")) self.pixmapError = QPixmap(resource_path("error_icon.png")) self.sourceLabel = QLabel() self.sourceLabel.setObjectName("source-label") self.sourceImage = QLabel() self.sourceImage.setObjectName("source-image") self.downloadLabel = QLabel() self.downloadLabel.setObjectName("download-label") self.downloadImage = QLabel() self.downloadImage.setObjectName("download-image") self.scanningLabel = QLabel() self.scanningLabel.setObjectName("scanning") self.spinnerLabel = QLabel() self.spinnerLabel.setVisible(False) self.spinnerLabel.setObjectName("spinner") self.spinner = QMovie(resource_path("spinner.gif")) self.spinner.setScaledSize(QSize(self.iconsSize, self.iconsSize)) self.spinnerLabel.setMovie(self.spinner) self.spinner.start() self.statusBar = QStatusBar() self.statusBar.setSizeGripEnabled(False) self.statusBar.addWidget(self.sourceLabel) self.statusBar.addWidget(self.sourceImage) self.statusBar.addWidget(self.downloadLabel) self.statusBar.addWidget(self.downloadImage) self.statusBar.addWidget(self.scanningLabel) self.statusBar.addWidget(self.spinnerLabel) self.statusBar.addPermanentWidget(sourcesButton) self.statusBar.setContentsMargins(10,5,9,5) # Container Layout for the Central Widget containerLayout = QVBoxLayout() containerLayout.setSpacing(0) containerLayout.addWidget(self.claimsTable) containerLayout.addWidget(self.buttonBox) # Central Widget containerWidget = QWidget() containerWidget.setLayout(containerLayout) self.setCentralWidget(containerWidget) self.setStatusBar(self.statusBar) def open_game(self): """ TODO: Double click should open a window to replay the game.""" pass def resize_claimsTable(self): """ Resize the table (if needed) after the insertion of a new element""" for index in range(0,6): self.claimsTable.resizeColumnToContents(index) def set_slots(self, slots): """ Connect the Slots """ self.slots = slots def add_to_table(self,type,bo_number,players,move): """ Add new row to the claimsTable Args: type: The type of the draw (3 Fold Repetition, 5 Fold Repetition, 50 Moves Rule, 75 Moves Rule). bo_number: The number of the boards, if this information is available. players: The name of the players. move: With which move the draw is valid. """ # Before insertion, remove rows as descripted in the remove_rows function self.remove_rows(type,players) timestamp = str(datetime.now().strftime('%H:%M:%S')) row = [] items = [str(self.rowCount+1),timestamp,type,bo_number,players,move] """ Convert each item(str) to QStandardItem, make the necessary stylistic additions and append it to row.""" for index in range(len(items)): standardItem = QStandardItem(items[index]) standardItem.setTextAlignment(Qt.AlignCenter) if(index == 2): font = standardItem.font() font.setBold(True) standardItem.setFont(font) if (items[index] == "5 Fold Repetition" or items[index] == "75 Moves Rule"): standardItem.setData(QColor(255,0,0), Qt.ForegroundRole) row.append(standardItem) self.claimsTableModel.appendRow(row) self.rowCount = self.rowCount+1 # After the insertion resize the table self.resize_claimsTable() # Always the last row(the bottom of the table) should be visible. self.claimsTable.scrollToBottom() #Send Notification self.notify(type,players,move) def notify(self,type,players,move): """ Send notification depending on the OS. Args: type: The type of the draw (3 Fold Repetition, 5 Fold Repetition, 50 Moves Rule, 75 Moves Rule). players: The names of the players. move: With which move the draw is valid. """ if (platform.system() == "Darwin"): self.mac_notification.clearNotifications() self.mac_notification.notify(type,players,move) elif(platform.system() == "Windows"): self.win_notification.show_toast(type, players+"\n"+move, icon_path=resource_path("logo.ico"), duration=5, threaded=True) def remove_from_table(self,index): """ Remove element from the claimsTable. Args: index: The index of the row we want to remove. First row has index=0. """ self.claimsTableModel.removeRow(index) def remove_rows(self,type,players): """ Removes a existing row from the Claims Table when same players made the same type of draw with a new move - or they made 5 Fold Repetition over the 3 Fold or 75 Moves Rule over 50 moves Rule. Args: type: The type of the draw (3 Fold Repetition, 5 Fold Repetition, 50 Moves Rule, 75 Moves Rule). players: The names of the players. """ for index in range(self.rowCount): try: modelType = self.claimsTableModel.item(index,2).text() modelPlayers = self.claimsTableModel.item(index,4).text() except AttributeError: modelType = "" modelPlayers = "" if (modelType == type and modelPlayers == players): self.remove_from_table(index) self.rowCount = self.rowCount - 1 break elif (type == "5 Fold Repetition" and modelType == "3 Fold Repetition" and modelPlayers == players) : self.remove_from_table(index) self.rowCount = self.rowCount - 1 break elif (type == "75 Moves Rule" and modelType == "50 Moves Rule" and modelPlayers == players): self.remove_from_table(index) self.rowCount = self.rowCount - 1 break def clear_table(self): """ Clear all the elements off the Claims Table and resets the rowCount. """ for index in range(self.rowCount): self.claimsTableModel.removeRow(0) self.rowCount = 0 def set_sources_status(self,status,validSources=None): """ Adds the sourcess in the statusBar. Args: status(str): The status of the validity of the sources. "ok": At least one source is valid. "error": None of the sources are valid. validSources(list): The list of valid sources, if there is any. This list is used here to display the ToolTip. """ self.sourceLabel.setText("Sources:") # Set the ToolTip if there are sources. try: text = "" for index in range(len(validSources)): if (index == len(validSources) - 1): number = str(index+1) text = text+number+") "+validSources[index].get_value() else: number = str(index+1) text = text+number+") "+validSources[index].get_value()+"\n" self.sourceLabel.setToolTip(text) except TypeError: pass if (status == "ok"): self.sourceImage.setPixmap(self.pixmapCheck.scaled(self.iconsSize,self.iconsSize,transformMode=Qt.SmoothTransformation)) else: self.sourceImage.setPixmap(self.pixmapError.scaled(self.iconsSize,self.iconsSize,transformMode=Qt.SmoothTransformation)) def set_download_status(self,status): """ Adds download status in the statusBar. Args: status(str): The status of the download(s). "ok": The download of the sources is successful. "error": The download of the sources failed. "stop": The download process stopped. """ timestamp = str(datetime.now().strftime('%H:%M:%S')) self.downloadLabel.setText(timestamp+" Download:") if (status == "ok"): self.downloadImage.setPixmap(self.pixmapCheck.scaled(self.iconsSize,self.iconsSize,transformMode=Qt.SmoothTransformation)) elif (status == "error"): self.downloadImage.setPixmap(self.pixmapError.scaled(self.iconsSize,self.iconsSize,transformMode=Qt.SmoothTransformation)) elif (status == "stop"): self.downloadImage.clear() self.downloadLabel.clear() def set_scan_status(self,status): """ Adds the scan status in the statusBar. Args: status(str): The status of the scan process. "active": The scan process is active. "error": The scan process waits for a new file. "stop": The scan process stopped. """ if (status == "wait"): self.scanningLabel.setText("Scan: Waiting") self.spinnerLabel.setVisible(False) elif (status == "active"): self.scanningLabel.setText("Scanning...") self.spinnerLabel.setVisible(True) elif (status == "stop"): self.scanningLabel.clear() self.spinnerLabel.setVisible(False) def change_scanButton_text(self,status): """ Changes the text of the scanButton depending on the status of the application. Args: status(str): The status of the scan process. "active": The scan process is active. "wait": The scan process is being terminated "stop": The scan process stopped. """ if (status == "active"): self.buttonBox.scanButton.setText("Scanning PGN...") elif (status == "stop"): self.buttonBox.scanButton.setText("Start Scan") elif(status == "wait"): self.buttonBox.scanButton.setText("Please Wait") def enable_buttons(self): self.buttonBox.scanButton.setEnabled(True) self.buttonBox.stopButton.setEnabled(True) def disable_buttons(self): self.buttonBox.scanButton.setEnabled(False) self.buttonBox.stopButton.setEnabled(False) def enable_statusBar(self): """ Show download and scan status messages - if they were previously hidden (by disable_statusBar) - from the statusBar.""" self.downloadLabel.setVisible(True) self.scanningLabel.setVisible(True) self.downloadImage.setVisible(True) def disable_statusBar(self): """ Hide download and scan status messages from the statusBar. """ self.downloadLabel.setVisible(False) self.downloadImage.setVisible(False) self.scanningLabel.setVisible(False) self.spinnerLabel.setVisible(False) def closeEvent(self,event): """ Reimplement the close button If the program is actively scanning a pgn a warning dialog shall be raised in order to make sure that the user didn't clicked the close Button accidentally. Args: event: The exit QEvent. """ try: if (self.slots.scanWorker.isRunning): exitDialog = QMessageBox() exitDialog.setWindowTitle("Warning") exitDialog.setText("Scanning in Progress") exitDialog.setInformativeText("Do you want to quit?") exitDialog.setIcon(exitDialog.Warning) exitDialog.setStandardButtons(QMessageBox.Yes | QMessageBox.Cancel) exitDialog.setDefaultButton(QMessageBox.Cancel) replay = exitDialog.exec() if replay == QMessageBox.Yes: event.accept() else: event.ignore() except: event.accept() def load_warning(self): """ Displays a Warning Dialog. trigger: User clicked the "Start Scanning" Button without any valid pgn source. """ warningDialog = QMessageBox() warningDialog.setIcon(warningDialog.Warning) warningDialog.setWindowTitle("Warning") warningDialog.setText("PGN File(s) Not Found") warningDialog.setInformativeText("Please enter at least one valid PGN source.") warningDialog.exec() def load_about_dialog(self): """ Displays the About Dialog.""" self.aboutDialog = AboutDialog() self.aboutDialog.set_GUI() self.aboutDialog.show()
class LeftSideBar(QWidget): treeViewSelectionChanged = pyqtSignal(QModelIndex, QModelIndex) treeViewDoubleClicked = pyqtSignal(QModelIndex) addPlaylistRequested = pyqtSignal() removePlaylistRequested = pyqtSignal(UUID) addToPlaylistRequested = pyqtSignal(UUID) playlistAdded = pyqtSignal(UUID) playlistRenamed = pyqtSignal(UUID, str) def __init__(self, tree_items, parent=None): super(LeftSideBar, self).__init__(parent) self._tree_items = tree_items self._restoreSettings() self._setTreeView() self._setAlbumCoverBox() self._renderUI() self.setMinimumWidth(FRONT_COVER_MIN_WIDTH) self.setMaximumWidth(FRONT_COVER_MAX_WIDTH) def _restoreSettings(self): pass def _setTreeView(self): self.treeModel = TreeModel() self.treeModel.addTopLevelItems(self._tree_items.keys()) self.leftBarView = QTreeView() self.leftBarView.setModel(self.treeModel) self.leftBarView.setHeaderHidden(True) self.leftBarView.setRootIsDecorated(False) self.leftBarView.setItemsExpandable(False) self.leftBarView.setMouseTracking(True) self.leftBarView.expandAll() self.leftBarView.setFocusPolicy(Qt.NoFocus) self.leftBarView.setSelectionBehavior(QAbstractItemView.SelectRows) self.leftBarView.setSelectionMode(QAbstractItemView.SingleSelection) self.leftBarView.setEditTriggers(QAbstractItemView.SelectedClicked) self.leftBarView.selectionModel().currentRowChanged.connect( lambda c, p: self.treeViewSelectionChanged.emit(c, p)) self.leftBarView.selectionModel().setCurrentIndex( self.leftBarView.model().index( 0, 0, self.leftBarView.model().index(0, 0)), QItemSelectionModel.Select) self.leftBarView.doubleClicked.connect( lambda i: self.treeViewDoubleClicked.emit(i)) delegate = LeftSideBarDelegate(self.leftBarView) self.leftBarView.setItemDelegate(delegate) delegate.addPlaylistRequested.connect( lambda: self.addPlaylistRequested.emit()) delegate.removePlaylistRequested.connect( lambda i: self.removePlaylistRequested.emit( self.__getUuidFromIndex(i))) delegate.addToPlaylistRequested.connect( lambda i: self.addToPlaylistRequested.emit(self.__getUuidFromIndex(i))) delegate.editingFinished.connect(self._onRenamed) def _onRenamed(self, index, text): self.playlistRenamed.emit( self.__getUuidFromIndex(index), text) @property def model(self): return self.treeModel def _setAlbumCoverBox(self): self.albumCoverBox = CoverArtBox() def _renderUI(self): self.layout = QVBoxLayout() self.layout.setContentsMargins(0, 0, 0, 0) self.layout.setSpacing(0) self.layout.addWidget(self.leftBarView) self.layout.addWidget(self.albumCoverBox) self.setLayout(self.layout) @QtCore.pyqtSlot(str, str, bytes) def changeCoverArtBoxInformation(self, title, artist, cover): self.albumCoverBox.setCoverArtBox(title, artist, cover) @QtCore.pyqtSlot(UUID, str, int, int) def addPlaylistEntry(self, uuid, name, row=0, column=0): model = self.model parent = model.getTopLevelIndex('PLAYLISTS') if model.insertPlaylistEntry(row, name, uuid, parent): self.playlistAdded.emit(uuid) child = model.index(row, column, parent) self.leftBarView.selectionModel().setCurrentIndex( child, QItemSelectionModel.SelectCurrent) self.leftBarView.edit(child) @QtCore.pyqtSlot(UUID, int, int) def createDefaults(self, uuid, row=0, column=0): model = self.model parent = model.getTopLevelIndex('LIBRARY') model.insertPlaylistEntry(row, 'Songs', uuid, parent) def __getUuidFromIndex(self, index): model = self.model uuid = model.getItemUuid(index) return uuid def __getIndexFromUuid(self, uuid): model = self.model row = model.getIndexFromUuid(uuid) return row @QtCore.pyqtSlot(UUID) def removePlaylistEntry(self, uuid): model = self.model row = self.__getIndexFromUuid(uuid) parent = model.getTopLevelIndex('PLAYLISTS') if not model.removeRow(row, parent): return None
class ProjectStatusDialog(QDialog): icons = { "added": "images/FA_icons/plus.svg", "removed": "images/FA_icons/trash.svg", "updated": "images/FA_icons/edit.svg", "renamed": "images/FA_icons/edit.svg", "table": "images/FA_icons/table.svg", } def __init__( self, pull_changes, push_changes, push_changes_summary, has_write_permissions, validation_results, mergin_project=None, parent=None ): super(ProjectStatusDialog, self).__init__(parent) self.validation_results = validation_results self.setWindowTitle("Project status") self.table = QTreeView() self.table.setEditTriggers(QAbstractItemView.NoEditTriggers) self.model = QStandardItemModel() self.model.setHorizontalHeaderLabels(["Status"]) self.table.setModel(self.model) self.mp = mergin_project self.check_any_changes(pull_changes, push_changes) self.add_content(pull_changes, "Server changes", True) self.add_content(push_changes, "Local changes", False, push_changes_summary) self.table.expandAll() main_lout = QVBoxLayout(self) self.tabs = QTabWidget() main_lout.addWidget(self.tabs) self.status_tab = QWidget() self.valid_tab = QWidget() self.tabs.addTab(self.status_tab, "Status") self.tabs.addTab(self.valid_tab, "Validation results") status_lay = QVBoxLayout(self.status_tab) status_lay.addWidget(self.table) has_files_to_replace = any( ["diff" not in file and is_versioned_file(file["path"]) for file in push_changes["updated"]] ) info_text = self._get_info_text(has_files_to_replace, has_write_permissions) if info_text: text_box = QLabel() text_box.setWordWrap(True) text_box.setText(info_text) status_lay.addWidget(text_box) box = QDialogButtonBox(QDialogButtonBox.Ok, centerButtons=True,) box.accepted.connect(self.accept) box.rejected.connect(self.reject) main_lout.addWidget(box, Qt.AlignCenter) self.valid_view = QTreeView() self.valid_view.setStyleSheet("QTreeView::item { padding: 5px }") self.valid_model = QStandardItemModel() self.show_validation_results() self.resize(640, 640) def _get_info_text(self, has_files_to_replace, has_write_permissions): msg = "" if not has_write_permissions: msg += f"WARNING: You don't have writing permissions to this project. Changes won't be synced!\n" if has_files_to_replace: msg += ( f"\nWARNING: Unable to compare some of the modified files with their server version - " f"their history will be lost if uploaded." ) return msg def check_any_changes(self, pull_changes, push_changes): if not sum(len(v) for v in list(pull_changes.values()) + list(push_changes.values())): root_item = QStandardItem("No changes") self.model.appendRow(root_item) def add_content(self, changes, root_text, is_server, changes_summary={}): """ Adds rows with changes info :param changes: Dict of added/removed/updated/renamed changes :param root_text: Text for the root item :param is_server: True if changes are related to server file changes :param changes_summary: If given and non empty, extra rows are added from geodiff summary. :return: """ if all(not changes[k] for k in changes): return root_item = QStandardItem(root_text) self.model.appendRow(root_item) for category in changes: for file in changes[category]: path = file["path"] item = self._get_icon_item(category, path) if is_versioned_file(path): if path in changes_summary: for sub_item in self._versioned_file_summary_items(changes_summary[path]["geodiff_summary"]): item.appendRow(sub_item) elif not is_server and category != "added": item.appendRow(QStandardItem("Unable to detect changes")) msg = f"Mergin plugin: Unable to detect changes for {path}" QgsApplication.messageLog().logMessage(msg) if self.mp is not None: self.mp.log.warning(msg) root_item.appendRow(item) def _versioned_file_summary_items(self, geodiff_summary): items = [] for s in geodiff_summary: table_name_item = self._get_icon_item("table", s["table"]) for row in self._table_summary_items(s): table_name_item.appendRow(row) items.append(table_name_item) return items def _table_summary_items(self, summary): return [QStandardItem("{}: {}".format(k, summary[k])) for k in summary if k != "table"] def _get_icon_item(self, key, text): path = os.path.join(os.path.dirname(os.path.realpath(__file__)), self.icons[key]) item = QStandardItem(text) item.setIcon(QIcon(path)) return item def show_validation_results(self): lout = QVBoxLayout(self.valid_tab) lout.addWidget(self.valid_view) self.valid_view.setEditTriggers(QAbstractItemView.NoEditTriggers) self.valid_model.setHorizontalHeaderLabels(["Validation results"]) self.valid_view.setModel(self.valid_model) map_layers = QgsProject.instance().mapLayers() for issues_data in sorted(self.validation_results): level, issue = issues_data layer_ids = self.validation_results[issues_data] issue_item = QStandardItem(issue) for lid in sorted(layer_ids, key=lambda x: map_layers[x].name()): layer = map_layers[lid] lyr_item = QStandardItem(f"- {layer.name()}") lyr_item.setToolTip(layer.publicSource()) issue_item.appendRow(lyr_item) self.valid_model.appendRow(issue_item) self.valid_view.expandAll()