class TdisMainForm(QMainWindow): def __init__(self, parent=None): super().__init__() self.setWindowTitle("Родословная") self.resize(800, self.height()) self.distance = 1000 self.central_widget = QWidget() self.setCentralWidget(self.central_widget) layout = QGridLayout() self.central_widget.setLayout(layout) self.lb_find = QInvisibleButton('Поиск') self.lb_find.setFont(mainfont) self.te_find = QLineEdit() self.te_find.setFont(mainfont) layout.addWidget(self.te_find, 0, 0, 1, 1) layout.addWidget(self.lb_find, 0, 0, 1, 1) #if mode: # te.setReadOnly(False) self.lb_find.clicked.connect(self.button_pressed) self.te_find.returnPressed.connect(self.line_edit_return_pressed) self.table = QTableView() # Создаём таблицу self.table.doubleClicked.connect(self.viewPerson) layout.addWidget(self.table) self.table.setFocus() timer = QTimer(self) timer.singleShot(0, self.async_init) def async_init(self): self.database = AllTables('database/objects') self.model = TableModel(self.database.get_peoples()) self.table.setModel(self.model) self.table.resizeRowsToContents() self.table.setSelectionBehavior(QAbstractItemView.SelectRows) self.model.dataChanged.connect(self.table.update) # Работаем с выпадающим меню def openMenu(position): menu = QMenu() relAction = menu.addAction('Связи') menu.addSeparator() albAction = menu.addAction('Альбомы') menu.addSeparator() miniatureAction = menu.addAction('Миниатюра') menu.addSeparator() viewAction = menu.addAction('Просмотреть') addAction = menu.addAction('Добавить') editAction = menu.addAction('Редактировать') delAction = menu.addAction('Удалить') menu.addSeparator() drawGraphAction = menu.addAction('Построить дерево') menu.addSeparator() quitAction = menu.addAction('Выход') action = menu.exec_(self.table.mapToGlobal(position)) if action == viewAction: self.viewPerson() if action == miniatureAction: p = self.get_selected_people() print('p = ' + str(p)) if p is not None: photo_ids = self.database.get_all_photo_ids(p['pid']) path = 0 if photo_ids and type(photo_ids) == type( []) and len(photo_ids) > 0: path = self.database.get_photo_path( p['pid'], photo_ids[0]) gm = GalleryMiniature(p['pid'], path) gm.exec() if action == albAction: p = self.get_selected_people() if p is not None: self.albuns = AlbumViewer(p['pid'], self.database, 1) self.albuns.show() if action == addAction: self.personal_card = PersonalCard( self.database.get_people(self.database.add_people({})), self.database, 1) self.personal_card.exec_() self.line_edit_return_pressed() if action == editAction: p = self.get_selected_people() if p is not None: self.personal_card = PersonalCard(p, self.database, 1) self.personal_card.exec_() self.line_edit_return_pressed() if action == delAction: res = QMessageBox.question( self, 'ВНИМАНИЕ!!!', "Вы действительно хотите выполнить удаление?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if res == QMessageBox.Yes: select = self.table.selectionModel() if select.hasSelection(): id_list = [ index.data() for index in self.table.selectionModel().selection().indexes() if index.column() == 0 ] print(id_list) self.database.del_people(id_list) self.database.peoples.save() for pid in id_list: print('remove = ' + str(self.model.removeRow(pid))) self.line_edit_return_pressed() if action == relAction: pid = self.get_selected_pid() backup_relations = copy.deepcopy(self.database.relations) self.relation_card = RelationCard(pid, self.database, 2) if not self.relation_card.exec_(): self.database.relations = backup_relations if action == drawGraphAction: print('draw graph') #dialog = MyDialog() #dialog.exec_() #engine = dialog.engine engine = 'dot' self.gd = GraphDrawer(self.database, engine) if action == quitAction: qApp.quit() self.table.setContextMenuPolicy(Qt.CustomContextMenu) self.table.customContextMenuRequested.connect(openMenu) def get_selected_pid(self): pid_list = [ index.data() for index in self.table.selectionModel().selection().indexes() if index.column() == 0 ] if len(pid_list) == 1: return int(pid_list[0]) return None def get_selected_people(self): pid_list = [ index.data() for index in self.table.selectionModel().selection().indexes() if index.column() == 0 ] if len(pid_list) == 1: return self.database.get_people(int(pid_list[0])) return None def viewPerson(self): p = self.get_selected_people() if p is not None: self.personal_card = PersonalCard(p, self.database, 0) self.personal_card.show() def button_pressed(self): self.lb_find.stackUnder(self.te_find) self.te_find.setFocus() def line_edit_return_pressed(self): print(self.te_find.text()) self.model = TableModel(self.database.get_peoples()) self.proxy_model = QSortFilterProxyModel(self.model) self.proxy_model.setSourceModel(self.model) self.proxy_model.setFilterFixedString(self.te_find.text()) self.proxy_model.setFilterKeyColumn(-1) self.table.setModel(self.proxy_model) self.table.update() self.table.resizeRowsToContents() #sender = self.sender() self.te_find.stackUnder(self.lb_find) self.lb_find.setFocus()
class SydTableWidget(QtWidgets.QWidget, Ui_SydTableWidget): table_reloaded = Signal() def __init__(self, filename, table_name, parent=None): super().__init__(parent) self.setupUi(self) # internal members self._filename = filename self._table_name = table_name self._db = None self._data = None self._model = None self._filter_proxy_model = None self._header = None self._toggle_width_menus = [] self.button_reload.clicked.connect(self.slot_on_reload) self.button_view.hide() self.button_view.clicked.connect(self.slot_on_view) # initial UI # self.setAutoFillBackground(True) self.scrollArea.setVisible(False) self.button_view.setEnabled(False) # pop-up window self.w = None def table_name(self): return self._table_name def model(self): return self._model def set_data(self, data): self._data = data db = self._db table_name = self._table_name # define and set the model self._model = SydTableModel(self._filename, db, table_name, data) # remove previous widget self.table_view.setParent(None) del self.table_view # create new one self.table_view = QTableView(self) self.verticalLayout.addWidget(self.table_view) # define own header (with column filter) self._header = SydColumnFilterHeader(self.table_view) ncol = self._model.columnCount(0) # define and set the filter/sort proxy self._filter_proxy_model = SydTableSortFilterProxyModel(self._header) self._filter_proxy_model.setSourceModel(self._model) self._filter_proxy_model.setSortLocaleAware(True) self._filter_proxy_model.setSortCaseSensitivity(Qt.CaseInsensitive) self.table_view.setModel(self._filter_proxy_model) # selection model for the button_view self.table_view.setSelectionBehavior(QTableView.SelectRows) selection = self.table_view.selectionModel() selection.selectionChanged.connect(self.on_selection_change) # set the columns filters self._header.set_filter_editors(ncol, self._filter_proxy_model) # setup the context menu self._header.setContextMenuPolicy(Qt.CustomContextMenu) self._header.customContextMenuRequested.connect( self.slot_on_column_header_popup) self._header.filterActivated.connect(self.slot_on_col_filter_changed) self.table_view.setHorizontalHeader(self._header) # remove previous col buttons c = self.layout_col_buttons.takeAt(0) while c and c.widget(): c.widget().setParent(None) del c c = self.layout_col_buttons.takeAt(0) # create new col buttons def create_lambda(icol): return lambda: self.slot_on_col_button_clicked(icol) self.col_buttons = [] for i in range(0, ncol): s = self._model._headers[i] b = QPushButton(s) # , parent=self.layout_col_buttons) b.clicked.connect(create_lambda(i)) b.setFlat(True) b.setVisible(False) self.layout_col_buttons.addWidget(b) self.col_buttons.append(b) h = QSpacerItem(878, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.layout_col_buttons.addItem(h) # create menu col width for i in range(0, ncol): b = QAction(self) b.setText('Toggle adjust width') b.setCheckable(True) b.setChecked(False) self._toggle_width_menus.append(b) # special case for first column ('id') if i == 0: b.setChecked(True) self.slot_on_auto_width_column(i) # make the area invisible first self.scrollArea.setVisible(False) # self.scrollArea.setAutoFillBackground(True) # self.table_view.setAutoFillBackground(True) self.table_view.setAlternatingRowColors(True) # initial nb of elements self.slot_on_col_filter_changed() # double click header self.table_view.horizontalHeader().sectionDoubleClicked. \ connect(self.slot_on_toggle_auto_width_column) # global filter self.edit_filter.textChanged.connect(self.slot_on_filter_changed) self.edit_filter.setClearButtonEnabled(True) # allow sorting self.table_view.setSortingEnabled(True) self._filter_proxy_model.sort(0, Qt.AscendingOrder) self._filter_proxy_model.invalidateFilter() self._header.updateGeometries() def slot_on_column_header_popup(self, pos): idx = self.table_view.horizontalHeader().logicalIndexAt(pos) menu = QMenu(self.table_view) a = QAction(self) name = self._model._col_names[idx] a.setText(f'Hide {name}') a.triggered.connect(lambda col_name=idx: self.slot_on_hide_column(idx)) b = self._toggle_width_menus[idx] b.triggered.connect( lambda col_name=idx: self.slot_on_auto_width_column(idx)) menu.addAction(a) menu.addAction(b) menu.exec_(self.table_view.mapToGlobal(pos)) def slot_on_hide_column(self, idx): # hide the column self.table_view.setColumnHidden(idx, True) # show the button self.scrollArea.setVisible(True) self.col_buttons[idx].setVisible(True) self._header.updateGeometries() def slot_on_col_button_clicked(self, idx): # show the column self.table_view.setColumnHidden(idx, False) # hide the button self.col_buttons[idx].setVisible(False) for b in self.col_buttons: if b.isVisible(): return self.scrollArea.setVisible(False) self._header.updateGeometries() def slot_on_toggle_auto_width_column(self, idx): b = self._toggle_width_menus[idx] b.setChecked(not b.isChecked()) self.slot_on_auto_width_column(idx) def slot_on_auto_width_column(self, idx): b = self._toggle_width_menus[idx] if b.isChecked(): self._header.setSectionResizeMode( idx, QtWidgets.QHeaderView.ResizeToContents) else: self._header.setSectionResizeMode( idx, QtWidgets.QHeaderView.Interactive) self._header.updateGeometries() def slot_on_col_filter_changed(self): n = self._filter_proxy_model.rowCount() t = self._model.rowCount(None) self.label_tablename.setText(f'{self._table_name}') self.label_status.setText(f'{n}/{t}') if self._table_name == 'Image' or self._table_name == 'DicomSeries': self.button_view.show() elif self._table_name == "DicomSeries_default" or self._table_name == "Image_default": self.button_view.show() def slot_on_filter_changed(self): n = self._filter_proxy_model.rowCount() t = self._model.rowCount(None) self.label_status.setText(f'{n}/{t}') f = self.edit_filter self._filter_proxy_model.set_global_filter(f.text()) def slot_on_reload(self): # later -> keep filters if the columns are identical. # not clear why I need to reopen the db here # (not needed for tables, needed for view) self._db = syd.open_db(self._filename) t = self._db.load_table(self._table_name) elements = syd.find_all(t) self.set_data(elements) # indicate that the table has been reloaded self.table_reloaded.emit() n = self._filter_proxy_model.rowCount() t = self._model.rowCount(None) self.label_status.setText(f'{n}/{t}') self.button_view.setText("view in vv") self.button_view.setEnabled(False) def slot_on_view(self): data = [] path = [] rows = set(index.row() for index in self.table_view.selectedIndexes()) for row in rows: data.append(self._data[row]) self.w = SydCTWindow(data, self._filename, self._table_name) self.w.button_ct_on.setEnabled(False) db = syd.open_db(self._filename) for d in data: e = self.w.get_ct_path(db, d) if e is not None and len(rows) == 1 and d['modality'] != 'CT': self.w.button_ct_on.setEnabled(True) self.w.show() else: if self._table_name == 'DicomSeries' or self._table_name == 'DicomSeries_default': db = syd.open_db(self._filename) dicom_file = syd.find_one(db['DicomFile'], dicom_series_id=d['id']) file = syd.find_one(db['File'], id=dicom_file['file_id']) tmp = db.absolute_data_folder + '/' + file[ 'folder'] + '/' + file['filename'] path.append(tmp) elif self._table_name == 'Image' or self._table_name == 'Image_default': db = syd.open_db(self._filename) file = syd.find_one(db['File'], id=d['file_mhd_id']) path.append(db.absolute_data_folder + '/' + file['folder'] + '/' + file['filename']) else: print('La table séléctionnée ne correspond pas') if path != []: path = ' '.join(path) cmd = f'vv {path}' os.system(cmd) else: print('Path to image has no corresponding file') def on_selection_change(self): rows = set(index.row() for index in self.table_view.selectedIndexes()) if len(rows) == 0: self.button_view.setEnabled(False) self.button_view.setText("view in vv") else: t = self._model.rowCount(None) self.button_view.setText(f"view in vv {len(rows)}/{t}") self.button_view.setEnabled(True)