class MyMainWindow(QMainWindow): def __init__(self, parent=None): QMainWindow.__init__(self, parent) self.config_window() self.create_widgets() self.config_widgets() self.create_menubar() self.bind_widgets() self.show_widgets() def config_window(self): self.setWindowTitle('DirectoPy') self.setMinimumHeight(600) self.setMinimumWidth(1000) def create_widgets(self): self.central_widget = QWidget() self.main_layout = QGridLayout() self.moveup_button = QPushButton('Collapse all', self) self.goto_lineedit = QLineEdit('', self) self.goto_button = QPushButton('Go', self) self.folder_view = QTreeView(self) self.file_view = QTreeView(self) self.folder_model = QFileSystemModel(self) self.file_model = QFileSystemModel(self) def config_widgets(self): self.main_layout.addWidget(self.moveup_button, 0, 0) self.main_layout.addWidget(self.goto_lineedit, 0, 1, 1, 2) self.main_layout.addWidget(self.goto_button, 0, 3) self.main_layout.addWidget(self.folder_view, 1, 0, 1, 2) self.main_layout.addWidget(self.file_view, 1, 2, 1, 2) self.central_widget.setLayout(self.main_layout) # Кнопка "вверх" self.moveup_button.setMaximumWidth(100) # Кнопка "перейти" self.goto_button.setMaximumWidth(70) self.setCentralWidget(self.central_widget) # панели self.folder_model.setRootPath(None) self.folder_model.setFilter(QDir.AllDirs | QDir.NoDotAndDotDot) self.folder_view.setModel(self.folder_model) self.folder_view.setRootIndex(self.folder_model.index(None)) self.folder_view.clicked[QModelIndex].connect(self.clicked_onfolder) self.folder_view.hideColumn(1) self.folder_view.hideColumn(2) self.folder_view.hideColumn(3) self.file_model.setFilter(QDir.Files) self.file_view.setModel(self.file_model) self.file_model.setReadOnly(False) self.file_view.setColumnWidth(0, 200) self.file_view.setSelectionMode(QAbstractItemView.ExtendedSelection) # открытие папки при нажати на неё в окне папок def clicked_onfolder(self, index): selection_model = self.folder_view.selectionModel() index = selection_model.currentIndex() dir_path = self.folder_model.filePath(index) self.file_model.setRootPath(dir_path) self.file_view.setRootIndex(self.file_model.index(dir_path)) # ф-я открытия нового файла def open_file(self): index = self.file_view.selectedIndexes() if not index: return else: index = index[0] file_path = self.file_model.filePath(index).replace('/', '\\') print(file_path) self.file_view.update() # ф-я создания нового файла def new_file(self): global file_name index = self.folder_view.selectedIndexes() if len(index) > 0: path = self.folder_model.filePath(index[0]) for i in range(1, 9999999999999999): if not os.path.isfile(os.path.join(path, "newfile{}.txt".format(i))): file_name = os.path.join(path, "newfile{}.txt".format(i)) break file_name = os.path.abspath(file_name) open(file_name, 'w').close() else: print("Please, select folder") # ф-я удаления файла def delete_file(self): indexes = self.file_view.selectedIndexes() for i in indexes: self.file_model.remove(i) # ф-я переименования файла def rename_file(self): index = self.file_view.selectedIndexes() if not index: return else: index = index[0] self.file_view.edit(index) # ф-я копирования файла def copy_file(self): print("COPY") ask = QFileDialog.getExistingDirectory(self, "Open Directory", "C:\\", QFileDialog.ShowDirsOnly | QFileDialog.DontResolveSymlinks) new_path = ask.replace('\\', '/') indexes = self.file_view.selectedIndexes()[::4] for i in indexes: new_filename = new_path + '/' + self.file_model.fileName(i) copy2(self.file_model.filePath(i), new_filename) # ф-я возвращения к корню пути def colapse(self): self.folder_view.collapseAll() # ф-я перемещения в заданную директорию def go_to(self): dir_path = self.goto_lineedit.text().replace('\\', '/') print(dir_path) self.file_model.setRootPath(dir_path) self.file_view.setRootIndex(self.file_model.index(dir_path)) #self.file_model.setRootPath() # ф-я перемещения файла def move_file(self): print("MOVE") ask = QFileDialog.getExistingDirectory(self, "Open Directory", "C:\\", QFileDialog.ShowDirsOnly | QFileDialog.DontResolveSymlinks) if ask == '': return new_path = ask.replace('\\', '/') indexes = self.file_view.selectedIndexes()[::4] for i in indexes: new_filename = new_path + '/' + self.file_model.fileName(i) move(self.file_model.filePath(i), new_filename) # ф-я создания новой папки def new_folder(self): global file_name index = self.folder_view.selectedIndexes() if len(index) > 0: path = self.folder_model.filePath(index[0]) for i in range(1, 9999999999999999): if not os.path.isdir(os.path.join(path, "newfolder{}".format(i))): file_name = os.path.join(path, "newfolder{}".format(i)) break file_name = os.path.abspath(file_name) os.mkdir(file_name) else: print("Please, select folder") # ф-я удаления папки def delete_folder(self): indexes = self.folder_view.selectedIndexes() for i in indexes: self.folder_model.remove(i) # ф-я переименования папки def rename_folder(self): index = self.folder_view.selectedIndexes() if not index: return else: index = index[0] self.folder_view.edit(index) # ф-я закрытия окна файлового менеджера def exit_application(self): print("EXIT") self.close() # задавание функции каждой кнопке def bind_widgets(self): self.open_file_action.triggered.connect(self.open_file) self.new_file_action.triggered.connect(self.new_file) self.delete_file_action.triggered.connect(self.delete_file) self.rename_file_action.triggered.connect(self.rename_file) self.copy_file_action.triggered.connect(self.copy_file) self.move_file_action.triggered.connect(self.move_file) self.exit_action.triggered.connect(self.exit_application) self.new_folder_action.triggered.connect(self.new_folder) self.delete_folder_action.triggered.connect(self.delete_folder) self.rename_folder_action.triggered.connect(self.rename_folder) self.goto_button.clicked.connect(partial(self.go_to)) self.moveup_button.clicked.connect(partial(self.colapse)) # создание меню def create_menubar(self): self.exit_action = QAction('Exit', self) self.exit_action.setShortcut('Ctrl+Q') self.new_file_action = QAction('New file', self) self.new_file_action.setShortcut('F4') self.open_file_action = QAction('Open file', self) self.open_file_action.setShortcut('F3') self.rename_file_action = QAction('Rename file', self) self.rename_file_action.setShortcut('F2') self.delete_file_action = QAction('Remove file', self) self.delete_file_action.setShortcut(QKeySequence.Delete) self.copy_file_action = QAction('Copy folder...', self) self.copy_file_action.setShortcut(QKeySequence.Copy) self.move_file_action = QAction('Move folder...', self) self.move_file_action.setShortcut(QKeySequence.Cut) self.new_folder_action = QAction('New folder', self) self.new_folder_action.setShortcut('Ctrl+Shift+N') self.delete_folder_action = QAction('Delete folder', self) self.delete_folder_action.setShortcut('Ctrl+Shift+Del') self.rename_folder_action = QAction('Rename folder', self) self.rename_folder_action.setShortcut('Ctrl+Shift+R') self.menubar = self.menuBar() self.file_menu = self.menubar.addMenu('File') self.file_menu.addAction(self.new_file_action) self.file_menu.addAction(self.open_file_action) self.file_menu.addAction(self.rename_file_action) self.file_menu.addAction(self.delete_file_action) self.file_menu.addAction(self.copy_file_action) self.file_menu.addAction(self.move_file_action) self.file_menu.addSeparator() self.file_menu.addAction(self.exit_action) self.folder_menu = self.menubar.addMenu('Folder') self.folder_menu.addAction(self.new_folder_action) self.folder_menu.addAction(self.delete_folder_action) self.folder_menu.addAction(self.rename_folder_action) def show_widgets(self): self.setLayout(self.main_layout)
class StatsGui(QWidget): ''' This class accepts a glue data collection object, and builds an interactive window to display basic statistics (e.g. mean, median, mode) about each dataset ''' released = QtCore.pyqtSignal(object) def __init__(self, dc): # Initialize the object as a QWidget QWidget.__init__(self) #Save the datacollection object as an attribute of class StatsGui self.dc = dc #Set the title of the main GUI window self.setWindowTitle('Statistics') # Set up dicts for row indices self.subset_dict = dict() self.component_dict = dict() self.selected_dict = dict() self.selected_indices = [] #Set up tree view and fix it to the top half of the window self.treeview = QTreeView(self) # Set the default clicking behavior to be row selection self.treeview.setSelectionBehavior(QAbstractItemView.SelectRows) # Set up expand all, collapse all, select all and deselect all buttons # Layout for expand/collapse/select/deselect layout_left_options = QHBoxLayout() self.expand_data = QToolButton(self) self.expand_data.setText("Expand all data and subsets") self.expand_data.clicked.connect(self.expandClicked) layout_left_options.addWidget(self.expand_data) self.all = QToolButton(self) self.all.setText('Select all') self.all.clicked.connect(self.allClicked) layout_left_options.addWidget(self.all) self.none = QToolButton(self) self.none.setText('Deselect all') self.none.clicked.connect(self.noneClicked) layout_left_options.addWidget(self.none) # Set default significant figures to 5 getcontext().prec = 5 # Set up past selected items self.past_selected = [] # Sort by subsets as a default self.sortBySubsets() # Set up the combo box for users to choose the number of significant figures in the table # Set up bottom options layout layout_bottom_options = QHBoxLayout() self.siglabel = QLabel(self) self.siglabel.setText('Number of significant figures:') layout_bottom_options.addWidget(self.siglabel) self.sigfig = QSpinBox(self) self.sigfig.setRange(1, 10) self.sigfig.setValue(5) self.sigfig.valueChanged.connect(self.sigchange) layout_bottom_options.addWidget(self.sigfig) # Export to file button self.export = QPushButton(self) self.export.setText('Export to file') self.export.clicked.connect(self.exportToFile) layout_bottom_options.addWidget(self.export) # Set up the toggle button to switch tree sorting modes self.switch_mode = QToolButton(self) self.switch_mode.setText('Sort tree by components') self.switch_mode.clicked.connect(self.switchMode) layout_left_options.addWidget(self.switch_mode) # Add instructions to sort the table self.how = QLabel(self) self.how.setText('Click each header to sort table') layout_left_options.addWidget(self.how) #################Set up the QTableView Widget############################# self.table = QTableView(self) self.table.setSortingEnabled(True) self.table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) self.table.verticalHeader().setVisible(False) #Set the table headings self.headings = ('Subset', 'Dataset', 'Component', 'Mean', 'Median', 'Minimum', 'Maximum', 'Sum') self.data_frame = pd.DataFrame(columns=self.headings) self.data_accurate = pd.DataFrame(columns=self.headings) self.model = pandasModel(self.data_frame, self.dc) self.table.setModel(self.model) layout_table = QHBoxLayout() layout_table.addWidget(self.table) layout_table.stretch(10) # Finish nesting all the layouts main_layout = QVBoxLayout() main_layout.addWidget(self.treeview) main_layout.addLayout(layout_left_options) main_layout.addLayout(layout_table) main_layout.addLayout(layout_bottom_options) self.setLayout(main_layout) # Set up dict for caching self.cache_stash = dict() def myPressedEvent(self, currentQModelIndex): ''' Every time a row (or rows) in the tree view is clicked: if it is selected, add it to the table if it is deselected, remove it from the table ''' # Get the indexes of all the selected components self.selected_indices = self.treeview.selectionModel().selectedRows() newly_selected = np.setdiff1d(self.selected_indices, self.past_selected) for index in range(0, len(newly_selected)): # Check which view mode the tree is in to get the correct indices if self.switch_mode.text() == 'Sort tree by components': data_i = newly_selected[index].parent().row() comp_i = newly_selected[index].row() subset_i = newly_selected[index].parent().parent().row() else: data_i = newly_selected[index].parent().parent().row() comp_i = newly_selected[index].parent().row() subset_i = newly_selected[index].row() - 1 is_subset = newly_selected[index].parent().parent().parent().row( ) == 1 or (self.switch_mode.text() == 'Sort tree by subsets' and subset_i != -1) # Check if its a subset and if so run subset stats if is_subset: self.runSubsetStats(subset_i, data_i, comp_i) else: # Run standard data stats self.runDataStats(data_i, comp_i) newly_dropped = np.setdiff1d(self.past_selected, self.selected_indices) for index in range(0, len(newly_dropped)): # Check which view mode the tree is in to get the correct indices if self.switch_mode.text() == 'Sort tree by components': data_i = newly_dropped[index].parent().row() comp_i = newly_dropped[index].row() subset_i = newly_dropped[index].parent().parent().row() else: data_i = newly_dropped[index].parent().parent().row() comp_i = newly_dropped[index].parent().row() subset_i = newly_dropped[index].row() - 1 is_subset = newly_dropped[index].parent().parent().parent().row( ) == 1 or (self.switch_mode.text() == 'Sort tree by subsets' and subset_i != -1) if is_subset: try: # Get the indices that match the component, dataset, and subset requirements idx_c = np.where(self.data_frame['Component'] == self.dc[data_i].components[comp_i].label) idx_d = np.where( self.data_frame['Dataset'] == self.dc[data_i].label) idx_s = np.where(self.data_frame['Subset'] == self.dc[data_i].subsets[subset_i].label) idx1 = np.intersect1d(idx_c, idx_d) idx2 = np.intersect1d(idx1, idx_s) self.data_frame = self.data_frame.drop(idx2) except: pass else: try: # Find the index in the table of the unchecked element, if it's in the table # Find the matching component and dataset indices and intersect them to get the unique index idx_c = np.where(self.data_frame['Component'] == self.dc[data_i].components[comp_i].label) idx_d = np.where( self.data_frame['Dataset'] == self.dc[data_i].label) idx_s = np.where(self.data_frame['Subset'] == '--') idx1 = np.intersect1d(idx_c, idx_d) idx2 = np.intersect1d(idx1, idx_s) self.data_frame = self.data_frame.drop(idx2) except: pass # Update the past selected indices self.past_selected = self.selected_indices model = pandasModel(self.data_frame, self.dc) self.table.setModel(model) self.table.setSortingEnabled(True) self.table.setShowGrid(False) def runDataStats(self, data_i, comp_i): ''' Runs statistics for the component comp_i of data set data_i ''' subset_label = "--" data_label = dc[data_i].label comp_label = self.dc[data_i].components[ comp_i].label # add to the name array to build the table # Build the cache key cache_key = subset_label + data_label + comp_label # See if the values have already been cached try: column_data = self.cache_stash[cache_key] except: # Find the stat values # Save the data in the cache mean_val = self.dc[data_i].compute_statistic( 'mean', self.dc[data_i].components[comp_i]) median_val = self.dc[data_i].compute_statistic( 'median', self.dc[data_i].components[comp_i]) min_val = self.dc[data_i].compute_statistic( 'minimum', self.dc[data_i].components[comp_i]) max_val = self.dc[data_i].compute_statistic( 'maximum', self.dc[data_i].components[comp_i]) sum_val = self.dc[data_i].compute_statistic( 'sum', self.dc[data_i].components[comp_i]) column_data = np.asarray([[subset_label], [data_label], [comp_label], [mean_val], [median_val], [min_val], [max_val], [sum_val]]).transpose() self.cache_stash[cache_key] = column_data # Save the accurate data in self.data_accurate column_df = pd.DataFrame(column_data, columns=self.headings) self.data_accurate = self.data_accurate.append(column_df, ignore_index=True) # Round the values according to the number of significant figures set by the user mean_val = Decimal(float(column_data[0][3])) * Decimal(1) median_val = Decimal(float(column_data[0][4])) * Decimal(1) min_val = Decimal(float(column_data[0][5])) * Decimal(1) max_val = Decimal(float(column_data[0][6])) * Decimal(1) sum_val = Decimal(float(column_data[0][7])) * Decimal(1) # Create the column data array and append it to the data frame column_data = np.asarray([[subset_label], [data_label], [comp_label], [mean_val], [median_val], [min_val], [max_val], [sum_val]]).transpose() column_df = pd.DataFrame(column_data, columns=self.headings) self.data_frame = self.data_frame.append(column_df, ignore_index=True) def runSubsetStats(self, subset_i, data_i, comp_i): ''' Runs statistics for the subset subset_i with respect to the component comp_i of data set data_i ''' subset_label = dc[data_i].subsets[subset_i].label data_label = dc[data_i].label comp_label = self.dc[data_i].components[ comp_i].label # add to the name array to build the table # Build the cache key cache_key = subset_label + data_label + comp_label # See if the statistics are already in the cache try: column_data = self.cache_stash[cache_key] # Find the stats if not in the cache # Save in the cache except: mean_val = self.dc[data_i].compute_statistic( 'mean', self.dc[data_i].subsets[subset_i].components[comp_i], subset_state=self.dc[data_i].subsets[subset_i].subset_state) median_val = self.dc[data_i].compute_statistic( 'median', self.dc[data_i].subsets[subset_i].components[comp_i], subset_state=self.dc.subset_groups[subset_i].subset_state) min_val = self.dc[data_i].compute_statistic( 'minimum', self.dc[data_i].subsets[subset_i].components[comp_i], subset_state=self.dc.subset_groups[subset_i].subset_state) max_val = self.dc[data_i].compute_statistic( 'maximum', self.dc[data_i].subsets[subset_i].components[comp_i], subset_state=self.dc.subset_groups[subset_i].subset_state) sum_val = self.dc[data_i].compute_statistic( 'sum', self.dc[data_i].subsets[subset_i].components[comp_i], subset_state=self.dc.subset_groups[subset_i].subset_state) column_data = np.asarray([[subset_label], [data_label], [comp_label], [mean_val], [median_val], [min_val], [max_val], [sum_val]]).transpose() self.cache_stash[cache_key] = column_data # Save the data in self.data_accurate column_df = pd.DataFrame(column_data, columns=self.headings) self.data_accurate = self.data_accurate.append(column_df, ignore_index=True) # Round the values according to the number of significant figures set by the user mean_val = Decimal(float(column_data[0][3])) * Decimal(1) median_val = Decimal(float(column_data[0][4])) * Decimal(1) min_val = Decimal(float(column_data[0][5])) * Decimal(1) max_val = Decimal(float(column_data[0][6])) * Decimal(1) sum_val = Decimal(float(column_data[0][7])) * Decimal(1) # Create the column data array and append it to the data frame column_data = np.asarray([[subset_label], [data_label], [comp_label], [mean_val], [median_val], [min_val], [max_val], [sum_val]]).transpose() column_df = pd.DataFrame(column_data, columns=self.headings) self.data_frame = self.data_frame.append(column_df, ignore_index=True) def sigchange(self, i): # Set the number of significant figures according to what the user selects getcontext().prec = i # Retrospectively change the number of significant figures in the table data_labels = self.data_frame['Dataset'] comp_labels = self.data_frame['Component'] subset_labels = self.data_frame['Subset'] mean_vals = [] median_vals = [] min_vals = [] max_vals = [] sum_vals = [] # Get the values from the self.data_accurate array and append them for i in range(0, len(self.data_frame)): mean_vals.append( Decimal(self.data_accurate['Mean'][i]) * Decimal(1)) median_vals.append( Decimal(self.data_accurate['Median'][i]) * Decimal(1)) min_vals.append( Decimal(self.data_accurate['Minimum'][i]) * Decimal(1)) max_vals.append( Decimal(self.data_accurate['Maximum'][i]) * Decimal(1)) sum_vals.append(Decimal(self.data_accurate['Sum'][i]) * Decimal(1)) column_data = np.asarray([ subset_labels, data_labels, comp_labels, mean_vals, median_vals, min_vals, max_vals, sum_vals ]).transpose() self.data_frame = pd.DataFrame(column_data, columns=self.headings) model = pandasModel(self.data_frame, self.dc) self.table.setModel(model) self.table.setSortingEnabled(True) self.table.setShowGrid(False) def expandClicked(self): if self.expand_data.text() == "Expand all data and subsets": self.treeview.expandAll() self.expand_data.setText("Collapse all data and subsets") else: self.treeview.collapseAll() self.expand_data.setText("Expand all data and subsets") def allClicked(self): # Select all components of the treeview if checked and fill the table with newly checked items # Does not deselect if user unclicks it original_idx = self.treeview.selectionModel().selectedRows() self.treeview.selectAll() end_idx = self.treeview.selectionModel().selectedRows() for index in end_idx: if index not in original_idx: # Check to see if the clicked item is a subset component or a data component if index.parent().parent().parent().row() != 1: self.runDataStats(index.parent().row(), index.row()) else: self.runSubsetStats(index.parent().parent().row(), index.parent().row(), index.row()) # Set the table to display the correct data frame model = pandasModel(self.data_frame, self.dc) self.table.setModel(model) self.table.setSortingEnabled(True) self.table.setShowGrid(False) def noneClicked(self): self.treeview.clearSelection() self.data_frame = pd.DataFrame(columns=self.headings) model = pandasModel(self.data_frame, self.dc) self.table.setModel(model) self.table.setSortingEnabled(True) self.table.setShowGrid(False) def exportToFile(self): file_name, fltr = compat.getsavefilename( caption="Choose an output filename") try: self.data_frame.to_csv(str(file_name), index=False) except: print("passed") pass def switchMode(self): # if the user clicks to sort by components, change the text to "sort by subsets" and sort tree by components if self.switch_mode.text() == 'Sort tree by components': self.sortByComponents() self.switch_mode.setText('Sort tree by subsets') # otherwise the user wants to sort by subsets, change text to "sort by components" and sort tree by subsets else: self.sortBySubsets() self.switch_mode.setText('Sort tree by components') def sizeHint(self): return QSize(600, 800) def sortBySubsets(self): ''' Sorts the treeview by subsets- Dataset then subset then component. What we originally had as the default ''' # Save the selected rows from the component view try: selected = dict() for i in range(0, len(self.selected_indices)): item = self.model_components.itemFromIndex( self.selected_indices[i]) if item.row() != 0: key = item.text() + " (" + item.parent().parent().text( ) + ")" + item.parent().text() selected[key] = item.index() else: key = item.text() + item.parent().text() selected[key] = item.index() except: pass # Set Expand/collapse button to "expand all" self.expand_data.setText("Expand all data and subsets") #Allow the user to select multiple rows at a time self.selection_model = QAbstractItemView.MultiSelection self.treeview.setSelectionMode(self.selection_model) # See if the model already exists instead of regenerating try: self.treeview.setModel(self.model_subsets) except: self.model_subsets = QStandardItemModel() self.model_subsets.setHorizontalHeaderLabels(['']) self.treeview.setModel(self.model_subsets) self.treeview.setUniformRowHeights(True) # populate the tree # Make all the datasets be parents, and make it so they are not selectable parent_data = QStandardItem('{}'.format('Data')) parent_data.setEditable(False) parent_data.setSelectable(False) for i in range(0, len(self.dc)): parent = QStandardItem('{}'.format(self.dc.labels[i])) parent.setIcon(helpers.layer_icon(self.dc[i])) parent.setEditable(False) parent.setSelectable(False) # Make all the data components be children, nested under their parent for j in range(0, len(self.dc[i].components)): child = QStandardItem('{}'.format( str(self.dc[i].components[j]))) child.setEditable(False) # Add to the subset_dict key = self.dc[i].label + self.dc[i].components[ j].label + "All data-" + self.dc[i].label self.subset_dict[key] = child.index() parent.appendRow(child) parent_data.appendRow(parent) #Add the parents with their children to the QStandardItemModel self.model_subsets.appendRow(parent_data) parent_subset = QStandardItem('{}'.format('Subsets')) parent_subset.setEditable(False) parent_subset.setSelectable(False) # Set up the subsets as Subsets > choose subset > choose data set > choose component for j in range(0, len(self.dc.subset_groups)): grandparent = QStandardItem('{}'.format( self.dc.subset_groups[j].label)) grandparent.setIcon( helpers.layer_icon(self.dc.subset_groups[j])) grandparent.setEditable(False) grandparent.setSelectable(False) for i in range(0, len(self.dc)): parent = QStandardItem( '{}'.format(self.dc.subset_groups[j].label) + ' (' + '{}'.format(self.dc[i].label) + ')') # Set up the circles parent.setIcon(helpers.layer_icon( self.dc.subset_groups[j])) parent.setEditable(False) parent.setSelectable(False) try: self.dc[i].compute_statistic( 'mean', self.dc[i].subsets[j].components[0], subset_state=self.dc[i].subsets[j].subset_state) except: parent.setForeground(QtGui.QBrush(Qt.gray)) for k in range(0, len(self.dc[i].components)): child = QStandardItem('{}'.format( str(self.dc[i].components[k]))) child.setEditable(False) # Update the dict to keep track of row indices key = self.dc[i].label + self.dc[i].components[ k].label + self.dc[i].subsets[j].label self.subset_dict[key] = child.index() parent.appendRow(child) # Make gray and unselectable components that aren't defined for a subset try: self.dc[i].compute_statistic( 'mean', self.dc[i].subsets[j].components[k], subset_state=self.dc[i].subsets[j].subset_state ) except: # print("Glue has raised an Incompatible Attribute error on this component. Let's do this instead.") child.setEditable(False) child.setSelectable(False) child.setForeground(QtGui.QBrush(Qt.gray)) grandparent.appendRow(parent) parent_subset.appendRow(grandparent) self.model_subsets.appendRow(parent_subset) # Fill out the dict now that the indices are connected to the QStandardItemModel # Full datasets for i in range(0, parent_data.rowCount()): for j in range(0, parent_data.child(i).rowCount()): key = "All data (" + parent_data.child( i).text() + ")" + parent_data.child(i).child(j).text() self.subset_dict[key] = parent_data.child(i).child( j).index() # Subsets for i in range(0, parent_subset.rowCount()): for j in range(0, parent_subset.child(i).rowCount()): for k in range(0, parent_subset.child(i).child(j).rowCount()): key = parent_subset.child(i).child(j).text( ) + parent_subset.child(i).child(j).child(k).text() self.subset_dict[key] = parent_subset.child(i).child( j).child(k).index() self.treeview.setUniformRowHeights(True) selection_model = QItemSelectionModel(self.model_subsets) self.treeview.setSelectionModel(selection_model) selection_model.selectionChanged.connect(self.myPressedEvent) # Select rows that should be selected sel_mod = self.treeview.selectionModel() for i in range(0, len(selected)): # key = list(self.selected_dict.keys())[list(self.selected_dict.values()).index(self.selected_dict[i])] key = list(selected.keys())[i] index = self.subset_dict[key] print(index.parent().row(), index.row()) # print(index, type(index)) # print(type(self.treeview.selectionModel().select(index, QItemSelectionModel.Select))) # sel_mod.select(index, QItemSelectionModel.Select|QItemSelectionModel.Rows) self.treeview.setCurrentIndex(index) self.treeview.setSelectionModel(sel_mod) def sortByComponents(self): ''' Sorts the treeview by components- Dataset then component then subsets ''' # Save the selected rows from the subset view if applicable try: selected = dict() for i in range(0, len(self.selected_indices)): item = self.model_subsets.itemFromIndex( self.selected_indices[i]) if item.parent().parent().text() == "Data": key = "All data (" + item.parent().text( ) + ")" + item.text() selected[key] = item.index() else: key = item.parent().text() + item.text() selected[key] = item.index() except: pass # Set Expand/collapse button to "expand all" self.expand_data.setText("Expand all data and subsets") self.selection_model = QAbstractItemView.MultiSelection self.treeview.setSelectionMode(self.selection_model) # See if the model already exists try: self.treeview.setModel(self.model_components) except: self.model_components = QStandardItemModel() self.model_components.setHorizontalHeaderLabels(['']) self.treeview.setModel(self.model_components) self.treeview.setUniformRowHeights(True) # populate the tree # Make all the datasets be parents, and make it so they are not selectable for i in range(0, len(dc)): grandparent = QStandardItem('{}'.format(self.dc.labels[i])) grandparent.setIcon(helpers.layer_icon(self.dc[i])) grandparent.setEditable(False) grandparent.setSelectable(False) # Make all the data components be children, nested under their parent for k in range(0, len(self.dc[i].components)): parent = QStandardItem('{}'.format( str(self.dc[i].components[k]))) parent.setEditable(False) parent.setSelectable(False) child = QStandardItem('{}'.format('All data (' + self.dc.labels[i] + ')')) child.setIcon(helpers.layer_icon(self.dc[i])) child.setEditable(False) parent.appendRow(child) for j in range(0, len(self.dc.subset_groups)): child = QStandardItem('{}'.format( self.dc.subset_groups[j].label)) child.setEditable(False) child.setIcon( helpers.layer_icon(self.dc.subset_groups[j])) try: self.dc[i].compute_statistic( 'mean', self.dc[i].subsets[j].components[k], subset_state=self.dc[i].subsets[j].subset_state ) except: # print("Glue has raised an Incompatible Attribute error on this component. Let's do this instead.") child.setEditable(False) child.setSelectable(False) child.setForeground(QtGui.QBrush(Qt.gray)) parent.appendRow(child) grandparent.appendRow(parent) self.model_components.appendRow(grandparent) # Fill out the dict now that the indices are connected to the QStandardItemModel for i in range(0, grandparent.rowCount()): for j in range(0, grandparent.child(i).rowCount()): if grandparent.child(i).child(j).row() == 0: key = grandparent.child(i).child( j).text() + grandparent.child(i).text() self.component_dict[key] = grandparent.child( i).child(j).index() else: key = grandparent.child(i).child( j).text() + " (" + grandparent.text( ) + ")" + grandparent.child(i).text() self.component_dict[key] = grandparent.child( i).child(j).index() self.treeview.setUniformRowHeights(True) selection_model = QItemSelectionModel(self.model_components) self.treeview.setSelectionModel(selection_model) selection_model.selectionChanged.connect(self.myPressedEvent) # Select the rows that should be selected sel_mod = self.treeview.selectionModel() for i in range(0, len(selected)): key = list(selected.keys())[i] index = self.component_dict[key] # self.treeview.selectionModel().select(index, QItemSelectionModel.Select) print(index.parent().row(), index.row()) # This causes an error when it runs # sel_mod.select(index, QItemSelectionModel.Select|QItemSelectionModel.Rows) self.treeview.setCurrentIndex(index) self.treeview.setSelectionModel(sel_mod)
class AttributeController(QMainWindow): _visibility_dict = { 'Beginner': EVisibility.Beginner, 'Expert': EVisibility.Expert, 'Guru': EVisibility.Guru, 'All': EVisibility.Invisible, } def __init__(self, node_map, parent=None): # super().__init__(parent=parent) # self.setWindowTitle('Attribute Controller') # self._view = QTreeView() self._view.setFont(get_system_font()) # self._node_map = node_map self._model = FeatureTreeModel(node_map=self._node_map, ) # self._proxy = FilterProxyModel() self._proxy.setSourceModel(self._model) self._proxy.setDynamicSortFilter(False) # self._delegate = FeatureEditDelegate(proxy=self._proxy) self._view.setModel(self._proxy) self._view.setItemDelegate(self._delegate) self._view.setUniformRowHeights(True) # unit = 260 for i in range(2): self._view.setColumnWidth(i, unit) w, h = 700, 600 self._view.setGeometry(100, 100, w, h) self.setCentralWidget(self._view) self.setGeometry(100, 100, unit * 2, 640) self._combo_box_visibility = None self._line_edit_search_box = None # self._setup_toolbars() def _setup_toolbars(self): # group_filter = self.addToolBar('Node Visibility') group_manipulation = self.addToolBar('Node Tree Manipulation') # label_visibility = QLabel() label_visibility.setText('Visibility') label_visibility.setFont(get_system_font()) # self._combo_box_visibility = QComboBox() self._combo_box_visibility.setSizeAdjustPolicy( QComboBox.AdjustToContents) # items = ('Beginner', 'Expert', 'Guru', 'All') for item in items: self._combo_box_visibility.addItem(item) shortcut_key = 'Ctrl+v' shortcut = QShortcut(QKeySequence(shortcut_key), self) def show_popup(): self._combo_box_visibility.showPopup() shortcut.activated.connect(show_popup) self._combo_box_visibility.setToolTip( compose_tooltip('Filter the nodes to show', shortcut_key)) self._combo_box_visibility.setFont(get_system_font()) self._combo_box_visibility.currentIndexChanged.connect( self._invalidate_feature_tree_by_visibility) # button_expand_all = ActionExpandAll(icon='expand.png', title='Expand All', parent=self, action=self.action_on_expand_all) shortcut_key = 'Ctrl+e' button_expand_all.setToolTip( compose_tooltip('Expand the node tree', shortcut_key)) button_expand_all.setShortcut(shortcut_key) button_expand_all.toggle() # button_collapse_all = ActionCollapseAll( icon='collapse.png', title='Collapse All', parent=self, action=self.action_on_collapse_all) shortcut_key = 'Ctrl+c' button_collapse_all.setToolTip( compose_tooltip('Collapse the node tree', shortcut_key)) button_collapse_all.setShortcut(shortcut_key) button_collapse_all.toggle() # label_search = QLabel() label_search.setText('RegEx Search') label_search.setFont(get_system_font()) # self._line_edit_search_box = QLineEdit() self._line_edit_search_box.setFont(get_system_font()) self._line_edit_search_box.textEdited.connect( self._invalidate_feature_tree_by_keyword) # group_filter.addWidget(label_visibility) group_filter.addWidget(self._combo_box_visibility) group_filter.addWidget(label_search) group_filter.addWidget(self._line_edit_search_box) group_filter.setStyleSheet('QToolBar{spacing:6px;}') # group_manipulation.addAction(button_expand_all) group_manipulation.addAction(button_collapse_all) # group_manipulation.actionTriggered[QAction].connect( self.on_button_clicked_action) # self._combo_box_visibility.setCurrentIndex( self._visibility_dict['Expert']) def _invalidate_feature_tree_by_visibility(self): visibility = self._visibility_dict[ self._combo_box_visibility.currentText()] self._proxy.setVisibility(visibility) self._view.expandAll() @pyqtSlot('QString') def _invalidate_feature_tree_by_keyword(self, keyword): self._proxy.setKeyword(keyword) self._view.expandAll() @staticmethod def on_button_clicked_action(action): action.execute() def expand_all(self): self._view.expandAll() def collapse_all(self): self._view.collapseAll() def resize_column_width(self): for i in range(self._model.columnCount()): self._view.resizeColumnToContents(i) def action_on_expand_all(self): self.expand_all() def action_on_collapse_all(self): self.collapse_all()
class DanaBrowseWindow(QMainWindow): def __init__(self, args): super(DanaBrowseWindow, self).__init__() #Leeo la configuracion self.configFile = args.configFile self.secure = args.secure self.cubeFile = args.cubeFile self.sysExclude = args.sysExclude self.maxLevel = 1 #para poder modificarlo luego self.dictionary = DataDict(defFile=args.configFile, secure=args.secure, sysExclude=args.sysExclude) #TODO variables asociadas del diccionario. Reevaluar al limpiar self.baseModel = self.dictionary.baseModel self.configData = self.dictionary.configData self.conn = self.dictionary.conn if self.dictionary.isEmpty: self.newConfigData() #self.dictionary._cargaModelo(self.dictionary.baseModel) self.setupView() self.cubeMgr = None # necesito mas adelante que este definida if config.DEBUG: print('inicializacion completa') #CHANGE here self.queryView = TableBrowse(None) self.dictMenu = self.menuBar().addMenu("&Conexiones") self.dictMenu.addAction("&New ...", self.newConnection, "Ctrl+N") self.dictMenu.addAction("&Modify ...", self.modConnection, "Ctrl+M") self.dictMenu.addAction("&Delete ...", self.delConnection, "Ctrl+D") self.dictMenu.addAction("&Save Config File", self.saveConfigFile, "Ctrl+S") self.dictMenu.addAction("E&xit", self.close, "Ctrl+Q") self.queryMenu = self.menuBar().addMenu('Consulta de &datos') self.queryMenu.addAction("Cerrar", self.hideDatabrowse) self.queryMenu.setEnabled(False) self.cubeMenu = self.menuBar().addMenu("C&ubo") self.cubeMenu.addAction("&Salvar", self.saveCubeFile, "Ctrl+S") #self.cubeMenu.addAction("&Restaurar", self.restoreCubeFile, "Ctrl+M") self.cubeMenu.addAction("S&alir", self.hideCube, "Ctrl+C") self.cubeMenu.addSeparator() self.cubeMenu.addAction("Ver &ejemplo de datos del cubo", self.previewCube, "Ctrl+E") self.cubeMenu.setEnabled(False) #self.queryModel = self.queryView.baseModel self.querySplitter = QSplitter(Qt.Vertical) self.querySplitter.addWidget(self.view) #self.querySplitter.addWidget(self.queryView) self.configSplitter = QSplitter(Qt.Horizontal) self.configSplitter.addWidget(self.querySplitter) self.setCentralWidget(self.configSplitter) self.setWindowTitle("Visualizador de base de datos") """ estas funciones son para soportar para los decoradores keep position y tal """ def model(self): return self.baseModel def isExpanded(self, idx): return self.view.isExpanded(idx) def setExpanded(self, idx, state): return self.view.setExpanded(idx, state) def setupView(self): self.view = QTreeView(self) self.view.setContextMenuPolicy(Qt.CustomContextMenu) self.view.customContextMenuRequested.connect(self.openContextMenu) self.view.doubleClicked.connect(self.test) self.view.setModel(self.baseModel) #self.view.resizeColumnToContents(0) self.view.setEditTriggers(QAbstractItemView.NoEditTriggers) self.view.expandAll() for m in range(self.baseModel.columnCount()): self.view.resizeColumnToContents(m) self.view.collapseAll() self.view.expandToDepth(0) #self.view.setHeaderHidden(True) #self.view.setSortingEnabled(True) #self.view.setRootIsDecorated(False) self.view.setAlternatingRowColors(True) #self.view.sortByColumn(0, Qt.AscendingOrder) def newConfigData(self): self.configData = dict() self.configData['Conexiones'] = dict() self.editConnection(None) if self.configData['Conexiones']: self.saveConfigFile() print(self.configData) self.dictionary._cargaModelo(confData=self.configData['Conexiones'] ) #self.dictionary.baseModel) else: QMessageBox.critical( self, "Error Fatal", "No se ha encontrado una conexión valida.\nFin de proceso") self.close() def saveConfigFile(self): dump_config(self.configData, getConfigFileName(self.configFile), secure=self.secure) #TODO de momento def closeEvent(self, event): self.close() def close(self): if self.cubeMgr: self.saveConfigFile() for conid in self.conn: if self.conn[conid] is None: continue if self.conn[conid].closed: self.conn[conid].close() self.saveConfigFile() sys.exit() def newConnection(self): confName = self.editConnection(None) # esta claro que sobran parametros self.dictionary.appendConnection(confName) def modConnection(self, nombre=None): if nombre is None: selDialog = SelectConnectionDlg(self.configData['Conexiones']) if selDialog.exec_(): confName = selDialog.conexion else: return else: confName = nombre self.editConnection(confName) self.updateModel(confName) @waiting_effects def updateModel(self, nombre=None): self.dictionary.updateModel(nombre) def delConnection(self, nombre=None): if nombre is None: selDialog = SelectConnectionDlg(self.configData['Conexiones']) if selDialog.exec_(): confName = selDialog.conexion else: return else: confName = nombre self.dictionary.dropConnection(confName) def editConnection(self, nombre=None): attr_list = ('driver', 'dbname', 'dbhost', 'dbuser', 'dbpass', 'dbport', 'debug') if nombre is None: datos = [None for k in range(len(attr_list) + 1)] else: datos = [ nombre, ] + dict2row(self.configData['Conexiones'][nombre], attr_list) datos[1] = DRIVERS.index(datos[1]) #contexto context = ( ( 'Nombre', QLineEdit, { 'setReadOnly': True } if nombre is not None else None, None, ), # driver ( "Driver ", QComboBox, None, DRIVERS, ), ( "DataBase Name", QLineEdit, None, None, ), ( "Host", QLineEdit, None, None, ), ( "User", QLineEdit, None, None, ), ( "Password", QLineEdit, { 'setEchoMode': QLineEdit.Password }, None, ), ( "Port", QLineEdit, None, None, ), ( "Debug", QCheckBox, None, None, )) parmDialog = ConnectionSheetDlg('Edite la conexion', context, datos, self) if parmDialog.exec_(): #TODO deberia verificar que se han cambiado los datos #datos[1]=DRIVERS[datos[1]] self.configData['Conexiones'][datos[0]] = row2dict( datos[1:], attr_list) return datos[0] @keep_tree_layout() def openContextMenu(self, position): """ """ item = None indexes = self.view.selectedIndexes() if len(indexes) > 0: index = indexes[0] item = self.baseModel.itemFromIndex(index) menu = QMenu() if item: item.setMenuActions(menu, self) action = menu.exec_(self.view.viewport().mapToGlobal(position)) #getContextMenu(item,action,self) @waiting_effects def databrowse(self, confName, schema, table, iters=0): #print(confName,schema,table,self.dictionary.conn[confName]) self.queryView.reconnect( self.queryView.getConnection(self.dictionary, confName, schema, table, iters)) self.queryView.executeNewScript( self.queryView.generateSQL(confName, schema, table, iters, pFilter=None)) if self.queryView.isHidden(): self.queryView.show() self.queryMenu.setEnabled(True) if self.querySplitter.count( ) == 1: #de momento parece un modo sencillo de no multiplicar en exceso self.querySplitter.addWidget(self.queryView) def hideDatabrowse(self): self.queryView.hide() self.queryMenu.setEnabled(False) def prepareNewCube(self, confName, schema, table): # aqui tiene que venir un dialogo para seleccionar nombre del cubo maxLevel = self.maxLevel parmDlg = GenerationSheetDlg('Parámetros de generación', table, maxLevel) if parmDlg.exec_(): kname = parmDlg.data[0] maxLevel = parmDlg.data[1] infox = info2cube(self.dictionary, confName, schema, table, maxLevel) if kname != table: infox[kname] = infox.pop(table) return infox def cubebrowse(self, confName, schema, table): infox = self.prepareNewCube(confName, schema, table) if self.cubeMgr and not self.cubeMgr.isHidden(): self.hideCube() self.cubeMgr = CubeMgr(self, confName, schema, table, self.dictionary, rawCube=infox, cubeFile=self.cubeFile) self.cubeMgr.expandToDepth(1) #if self.configSplitter.count() == 1: #de momento parece un modo sencillo de no multiplicar en exceso self.configSplitter.addWidget(self.cubeMgr) self.cubeMgr.show() self.cubeMenu.setEnabled(True) def saveCubeFile(self): self.cubeMgr.saveCubeFile() def restoreCubeFile(self): self.cubeMgr.restoreConfigFile() def hideCube(self): self.cubeMgr.saveCubeFile() self.cubeMgr.hide() self.cubeMenu.setEnabled(False) def test(self, index): return print(index.row(), index.column()) item = self.baseModel.itemFromIndex(index) print(item.text(), item.model()) def refreshTable(self): self.baseModel.emitModelReset() def previewCube(self): startItem = self.cubeMgr.model().item(0, 0) conName = self.cubeMgr.defaultConnection self.queryView.reconnect( self.queryView.getConnection(self.dictionary, confName=conName)) query = self.cubeMgr.getPreviewQuery(startItem) self.queryView.executeNewScript(query) if self.queryView.isHidden(): self.queryView.show() self.queryMenu.setEnabled(True) if self.querySplitter.count( ) == 1: #de momento parece un modo sencillo de no multiplicar en exceso self.querySplitter.addWidget(self.queryView)