class OntoOnlineSearch(QWidget): tagSelected = pyqtSignal(str, str) def __init__(self, parent=None): super().__init__(parent) self.termListTblWdg = QTableView() self.termTableModel = OntoTermsListModel(parent=self) self.termListTblWdg.setSelectionBehavior(QAbstractItemView.SelectRows) self.termListTblWdg.setSelectionMode(QAbstractItemView.SingleSelection) self.termListTblWdg.setModel(self.termTableModel) self.termListTblWdg.setColumnWidth(0, 300) self.termListTblWdg.setColumnWidth(1, 200) # Signals self.termSelectionModel = self.termListTblWdg.selectionModel() self.termSelectionModel.selectionChanged.connect(self.termSelected) #self.termTableModel.layoutChanged.connect(self.termTableLayoutChanged) self.autoCompleteTxt = OntoAutoComplete(self) self.autoCompleteTxt.completionTerminated.connect( self.completionUpdate) grid = QGridLayout(self) grid.addWidget(self.termListTblWdg, 0, 0) grid.addWidget(self.autoCompleteTxt, 1, 0) def termSelected(self, selection, deselected=None): #id = [tagId for tagId, tagName in self.dicData.items() if name == tagName] #assert(len(id)==1) #id = id[0] #self.addTagToAnnotation(id) #self.tagEdit.erase = True #self.tagEdit.clearEditText() if len(selection.indexes()): term = self.termTableModel.getTerm(selection.indexes()[0]) self.tagSelected.emit(term[0], term[1]) # Unselect selected item in the table view. #self.termListTblWdg.setCurrentCell(-1,-1) self.termListTblWdg.clearSelection() self.termTableModel.refresh() @pyqtSlot(dict) def completionUpdate(self, termDic): #print(termDic) self.termTableModel.setTerms(termDic)
class AddTableDetailsView(QSplitter): """ This is a splitter widget that contains a table and a details widget. Used in the Add Show dialog. """ changed = QtCore.pyqtSignal(dict) def __init__(self, parent=None, worker=None): super().__init__(parent) self.table = QTableView() m = AddTableModel() proxy = QtCore.QSortFilterProxyModel() proxy.setSourceModel(m) self.table.setGridStyle(QtCore.Qt.NoPen) self.table.setSelectionMode(QAbstractItemView.SingleSelection) self.table.setSelectionBehavior(QAbstractItemView.SelectRows) self.table.setEditTriggers(QAbstractItemView.NoEditTriggers) self.table.setModel(proxy) # Allow sorting but don't sort by default self.table.horizontalHeader().setSortIndicator( -1, QtCore.Qt.AscendingOrder) self.table.setSortingEnabled(True) if pyqt_version == 5: self.table.horizontalHeader().setSectionResizeMode( 0, QHeaderView.Stretch) else: self.table.horizontalHeader().setResizeMode(0, QHeaderView.Stretch) self.table.selectionModel().currentRowChanged.connect( self.s_show_selected) self.addWidget(self.table) self.details = DetailsWidget(parent, worker) self.addWidget(self.details) self.setSizes([1, 1]) def s_show_selected(self, new, old=None): if not new: return index = self.table.model().mapToSource(new).row() selected_show = self.getModel().results[index] self.details.load(selected_show) self.changed.emit(selected_show) def setResults(self, results): self.getModel().setResults(results) def getModel(self): return self.table.model().sourceModel() def clearSelection(self): return self.table.clearSelection()
class MasternodesWidget(QWidget): """Widget that displays masternodes.""" def __init__(self, manager, parent=None): super(MasternodesWidget, self).__init__(parent) self.manager = manager self.model = MasternodesModel(self.manager) self.proxy_model = QSortFilterProxyModel() self.proxy_model.setSourceModel(self.model) self.view = QTableView() self.view.setModel(self.proxy_model) for header in [self.view.horizontalHeader(), self.view.verticalHeader()]: header.setHighlightSections(False) header = self.view.horizontalHeader() header.setSectionResizeMode(MasternodesModel.ALIAS, QHeaderView.ResizeToContents) header.setSectionResizeMode(MasternodesModel.VIN, QHeaderView.Stretch) header.setSectionResizeMode(MasternodesModel.COLLATERAL, QHeaderView.ResizeToContents) header.setSectionResizeMode(MasternodesModel.DELEGATE, QHeaderView.ResizeToContents) self.view.verticalHeader().setVisible(False) self.view.setSelectionMode(QAbstractItemView.SingleSelection) self.view.setSelectionBehavior(QAbstractItemView.SelectRows) self.view.setSortingEnabled(True) self.view.sortByColumn(self.model.ALIAS, Qt.AscendingOrder) vbox = QVBoxLayout() vbox.setContentsMargins(0, 0, 0, 0) vbox.addWidget(self.view) self.setLayout(vbox) def select_masternode(self, alias): """Select the row that represents alias.""" self.view.clearSelection() for i in range(self.proxy_model.rowCount()): idx = self.proxy_model.index(i, 0) mn_alias = str(self.proxy_model.data(idx)) if mn_alias == alias: self.view.selectRow(i) break def populate_collateral_key(self, row): """Fill in the collateral key for a masternode based on its collateral output. row refers to the desired row in the proxy model, not the actual model. """ mn = self.masternode_for_row(row) self.manager.populate_masternode_output(mn.alias) # Emit dataChanged for the collateral key. index = self.model.index(row, self.model.COLLATERAL) self.model.dataChanged.emit(index, index) def refresh_items(self): self.model.dataChanged.emit(QModelIndex(), QModelIndex()) def add_masternode(self, masternode, save = True): self.model.add_masternode(masternode, save=save) def remove_masternode(self, alias, save = True): self.model.remove_masternode(alias, save=save) def masternode_for_row(self, row): idx = self.proxy_model.mapToSource(self.proxy_model.index(row, 0)) return self.model.masternode_for_row(idx.row()) def import_masternode_conf_lines(self, conf_lines, pw): return self.model.import_masternode_conf_lines(conf_lines, pw)
class ParamModWgt(QWidget): def __init__(self, parent=None): super().__init__(parent) self.main_window = parent self.buildRequiredTagsGB() # Widgets self.newParamBtn = QPushButton("New") self.deleteParamBtn = QPushButton("Delete") self.paramSaveAnnotBtn = QPushButton("Save") buttonWidget = QWidget(self) self.existingParamsGB = QGroupBox("Existing parameters", self) self.paramListTblWdg = QTableView() self.paramListModel = ParameterListModel(parent=self) self.paramListTblWdg.setSelectionBehavior(QAbstractItemView.SelectRows) self.paramListTblWdg.setSelectionMode( QAbstractItemView.SingleSelection) self.paramListTblWdg.setModel(self.paramListModel) self.paramListTblWdg.setColumnWidth(0, 150) self.paramListTblWdg.setColumnWidth(1, 350) self.relationWgt = ParamRelationWgt(parent) self.newParamsGB = QGroupBox("Parameter details", self) self.resultTypeCbo = QComboBox(self) self.isExpProp = QCheckBox("is an experimental property", self) self.resultTypeCbo.addItems( ["point value", "function", "numerical trace"]) self.singleValueParamWgt = ParamValueWgt(parent) self.functionParamWgt = ParamFunctionWgt(parent) self.traceParamWgt = ParamTraceWgt(parent) self.functionParamWgt.mainWgt = self self.paramModStack = QStackedWidget(self) self.paramModStack.addWidget(self.singleValueParamWgt) self.paramModStack.addWidget(self.functionParamWgt) self.paramModStack.addWidget(self.traceParamWgt) # Signals selectionModel = self.paramListTblWdg.selectionModel() selectionModel.selectionChanged.connect(self.selectedParameterChanged) self.newParamBtn.clicked.connect(self.newParameter) self.deleteParamBtn.clicked.connect(self.deleteParameter) self.paramSaveAnnotBtn.clicked.connect(self.saveParameter) self.resultTypeCbo.currentIndexChanged.connect( self.paramModStack.setCurrentIndex) self.singleValueParamWgt.paramTypeSelected.connect( self.newParamTypeSelected) self.functionParamWgt.paramTypeSelected.connect( self.newParamTypeSelected) self.traceParamWgt.paramTypeSelected.connect(self.newParamTypeSelected) # Layout buttonLayout = QVBoxLayout(buttonWidget) buttonLayout.addWidget(self.paramSaveAnnotBtn) buttonLayout.addWidget(self.deleteParamBtn) buttonLayout.addWidget(self.newParamBtn) existGrid = QHBoxLayout(self.existingParamsGB) existGrid.addWidget(buttonWidget) existGrid.addWidget(self.paramListTblWdg) newGrid = QGridLayout(self.newParamsGB) newGrid.addWidget(QLabel("Result type"), 0, 0) newGrid.addWidget(self.resultTypeCbo, 0, 1) newGrid.addWidget(self.isExpProp, 0, 2) newGrid.addWidget(self.paramModStack, 1, 0, 1, 3) newGrid.addWidget(self.relationWgt, 1, 3) layout = QVBoxLayout(self) self.rootLayout = QSplitter(Qt.Vertical, self) self.rootLayout.setOrientation(Qt.Vertical) self.rootLayout.addWidget(self.existingParamsGB) self.rootLayout.addWidget(self.newParamsGB) self.rootLayout.addWidget(self.requireTagGB) layout.addWidget(self.rootLayout) # Initial behavior self.newParamBtn.setEnabled(True) self.deleteParamBtn.setEnabled(False) self.paramSaveAnnotBtn.setEnabled(False) self.additionMode = False self.newParamsGB.setEnabled(False) def setRootLayoutSizes(self, sizes): self.rootLayout.setSizes(sizes) def viewParameter(self, parameter): row = -1 for row, param in enumerate(self.paramListModel.parameterList): if param.id == parameter.id: break assert (row > -1) self.paramListTblWdg.selectRow(row) @pyqtSlot(str) def newParamTypeSelected(self, paramName): self.requiredTagsListModel.clear() self.requiredTagsListModel.refresh() paramType = getParameterTypeFromName(paramName) if paramType is None: raise ValueError("Parameter type with name '" + paramName + "' was not found.") for reqTag in paramType.requiredTags: self.requiredTagsListModel.addTag(reqTag.id, reqTag.name, reqTag.id, reqTag.name) self.requiredTagsListModel.refresh() def buildRequiredTagsGB(self): # Widgets self.requireTagGB = QGroupBox("Required tag categories", self) self.requiredTagsListTblWdg = RequiredTagsTableView() self.requiredTagsListModel = RequiredTagsListModel(parent=self) self.requiredTagsListTblWdg.setSelectionBehavior( QAbstractItemView.SelectRows) self.requiredTagsListTblWdg.setSelectionMode( QAbstractItemView.SingleSelection) self.requiredTagsListTblWdg.setModel(self.requiredTagsListModel) self.requiredTagsListTblWdg.setColumnWidth(0, 200) self.requiredTagsListTblWdg.setColumnWidth(1, 200) # Layout requiredTagLayout = QGridLayout(self.requireTagGB) requiredTagLayout.addWidget(self.requiredTagsListTblWdg, 0, 0, 4, 1) def newParameter(self): self.resultTypeCbo.setCurrentIndex(0) self.paramModStack.currentWidget().newParameter() self.singleValueParamWgt.newParameter() self.functionParamWgt.newParameter() self.traceParamWgt.newParameter() self.newParamsGB.setEnabled(True) self.paramListTblWdg.clearSelection() self.newParamBtn.setEnabled(False) self.deleteParamBtn.setEnabled(False) self.paramSaveAnnotBtn.setEnabled(True) self.isExpProp.setChecked(False) def saveParameter(self): relationship = self.relationWgt.getRelationship() # Get the ID of the modified parameter if we are modifying an existing # parameters if len(self.paramListTblWdg.selectionModel().selectedRows()) != 0: selectedRow = self.paramListTblWdg.selectionModel().currentIndex( ).row() paramId = self.main_window.currentAnnotation.parameters[ selectedRow].id else: paramId = None param = self.paramModStack.currentWidget().saveParameter( relationship, paramId) if not param is None: param.requiredTags = self.requiredTagsListModel.getRequiredTags() param.isExperimentProperty = self.isExpProp.isChecked() selectedRow = self.paramListTblWdg.selectionModel().currentIndex( ).row() # Even when there is no selection, selectedRow can take a zero value. This "if" # controls for that. if len(self.paramListTblWdg.selectionModel().selectedRows()) == 0: selectedRow = -1 if selectedRow >= 0: self.main_window.currentAnnotation.parameters[ selectedRow] = param else: self.main_window.currentAnnotation.parameters.append(param) self.additionMode = False nbParams = len(self.main_window.currentAnnotation.parameters) self.main_window.saveAnnotation() if selectedRow < 0: selectedRow = nbParams - 1 self.paramListTblWdg.selectRow(selectedRow) self.loadRow(selectedRow) def deleteParameter(self): selectedRow = self.paramListTblWdg.selectionModel().currentIndex().row( ) del self.main_window.currentAnnotation.parameters[selectedRow] self.main_window.saveAnnotation() self.refreshModelingParameters() def refreshModelingParameters(self): selectedRow = self.paramListTblWdg.selectionModel().currentIndex().row( ) self.loadModelingParameter(selectedRow) def loadModelingParameter(self, row=None): """ Call when a new annotation has been selected so that all the modeling parameters associated with this annotation are loaded in the parameter list. """ self.requiredTagsListModel.clear() self.requiredTagsListModel.refresh() if self.main_window.currentAnnotation is None: self.paramListModel.parameterList = [] else: self.paramListModel.parameterList = self.main_window.currentAnnotation.parameters aRowIsSelected = not row is None if aRowIsSelected: if row < 0: noRowToLoad = self.paramListTblWdg.model().rowCount() - row else: noRowToLoad = row else: ## No rows are selected noRowToLoad = -1 self.loadRow(noRowToLoad) self.newParamBtn.setEnabled(True) self.deleteParamBtn.setEnabled(aRowIsSelected) self.paramSaveAnnotBtn.setEnabled(aRowIsSelected) self.paramModStack.currentWidget().loadModelingParameter(row) self.relationWgt.loadModelingParameter(row) self.newParamsGB.setEnabled(aRowIsSelected) self.paramListModel.refresh() def loadRow(self, selectedRow=None): """ Called when a row has been selected in the table listing all the modeling parameters. It update the interface with the values associated with this specific parameter. """ def nlxCheck(id): if id in nlx2ks: return nlx2ks[id] return id def clear(): self.requiredTagsListModel.clear() self.paramModStack.currentWidget().loadRow(None) self.relationWgt.clear() self.paramListTblWdg.clearSelection() if selectedRow is None: selectedRow = self.paramListTblWdg.selectionModel().currentIndex( ).row() if self.main_window.currentAnnotation is None: clear() return if selectedRow < 0 or selectedRow >= len( self.main_window.currentAnnotation.parameters): clear() return currentParameter = self.main_window.currentAnnotation.parameters[ selectedRow] self.newParamBtn.setEnabled(True) self.deleteParamBtn.setEnabled(True) self.paramSaveAnnotBtn.setEnabled(True) if currentParameter.description.type == "pointValue": self.resultTypeCbo.setCurrentIndex(0) self.paramModStack.setCurrentIndex(0) elif currentParameter.description.type == "function": self.resultTypeCbo.setCurrentIndex(1) self.paramModStack.setCurrentIndex(1) elif currentParameter.description.type == "numericalTrace": self.resultTypeCbo.setCurrentIndex(2) self.paramModStack.setCurrentIndex(2) else: raise ValueError("Type of parameter description " + currentParameter.description.type + " is invalid.") self.paramModStack.currentWidget().loadRow(currentParameter) self.relationWgt.loadRow(currentParameter) self.isExpProp.setChecked(currentParameter.isExperimentProperty) ## UPDATING REQUIRED TAGS self.requiredTagsListModel.clear() for tag in currentParameter.requiredTags: self.requiredTagsListModel.addTag( tag.rootId, self.main_window.dicData[tag.rootId], tag.id, tag.name) ## Adding new required tags that may have been specified since the ## creation of this parameter instance. parameterType = getParameterTypeFromID(currentParameter.typeId) reqTags = { reqTag.rootId: reqTag for reqTag in parameterType.requiredTags } for reqTagRootId, reqTag in reqTags.items(): #print(nlxCheck(reqTagRootId), [nlxCheck(tag.rootId) for tag in currentParameter.requiredTags]) if not nlxCheck(reqTagRootId) in [ nlxCheck(tag.rootId) for tag in currentParameter.requiredTags ]: self.requiredTagsListModel.addTag( reqTag.rootId, self.main_window.dicData[reqTag.rootId], reqTag.id, reqTag.name) self.requiredTagsListModel.refresh() self.newParamsGB.setEnabled(True) def selectedParameterChanged(self, selected, deselected): if len(selected.indexes()) == 0: return if self.additionMode: msgBox = QMessageBox(self) msgBox.setWindowTitle("Cancellation") msgBox.setText( "Are you sure you want to cancel the addition of the new parameter being edited? If not, say no and then hit 'Save' to save this new parameter." ) msgBox.setStandardButtons(QMessageBox.No | QMessageBox.Yes) msgBox.setDefaultButton(QMessageBox.No) if msgBox.exec_() == QMessageBox.Yes: self.additionMode = False self.loadRow() else: #self.paramListTblWdg.selectRow(-1) self.paramListTblWdg.clearSelection() else: self.loadRow()
class QtComparePanel(QWidget): filterChanged = pyqtSignal(str) def __init__(self, parent=None): super(QtComparePanel, self).__init__(parent) self.visibility_flags = [] self.visibility_buttons = [] self.labels_projects = [] self.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding) self.setMinimumWidth(400) self.setMinimumHeight(100) self.data_table = QTableView() self.data_table.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding) self.data_table.setSelectionMode(QAbstractItemView.MultiSelection) self.data_table.setSelectionMode(QAbstractItemView.ExtendedSelection) self.data_table.setSelectionBehavior(QAbstractItemView.SelectRows) self.model = None self.data = None self.combodelegate1 = ComboBoxItemDelegate(self.data_table) self.combodelegate2 = ComboBoxItemDelegate(self.data_table) lblFilter = QLabel("Filter: ") self.comboboxFilter = QComboBox() self.comboboxFilter.setMinimumWidth(80) self.comboboxFilter.addItem("All") self.comboboxFilter.addItem("Same") self.comboboxFilter.addItem("Born") self.comboboxFilter.addItem("Dead") self.comboboxFilter.addItem("Grow") self.comboboxFilter.addItem("Shrink") filter_layout = QHBoxLayout() filter_layout.addWidget(lblFilter) filter_layout.addWidget(self.comboboxFilter) filter_layout.addStretch() layout = QVBoxLayout() layout.addLayout(filter_layout) layout.addWidget(self.data_table) self.setLayout(layout) self.correspondences = None self.data = None self.comboboxFilter.currentTextChanged.connect(self.changeFilter) def setTable(self, project, img1idx, img2idx): self.correspondences = project.getImagePairCorrespondences( img1idx, img2idx) self.data = self.correspondences.data self.model = TableModel(self.data) self.sortfilter = QSortFilterProxyModel(self) self.sortfilter.setSourceModel(self.model) self.data_table.setModel(self.sortfilter) self.data_table.setVisible(False) self.data_table.verticalHeader().hide() self.data_table.resizeColumnsToContents() self.data_table.setVisible(True) self.data_table.setItemDelegateForColumn(5, self.combodelegate1) self.data_table.setItemDelegateForColumn(6, self.combodelegate2) self.data_table.setEditTriggers(QAbstractItemView.DoubleClicked) self.data_table.update() self.data_table.setStyleSheet( "QHeaderView::section { background-color: rgb(40,40,40) }") def updateData(self, corr): if corr is None: return self.correspondences = corr self.sortfilter.beginResetModel() self.model.beginResetModel() self.model._data = corr.data self.sortfilter.endResetModel() self.model.endResetModel() self.data_table.update() def selectRows(self, rows): self.data_table.clearSelection() indexes = [self.model.index(r, 0) for r in rows] mode = QItemSelectionModel.Select | QItemSelectionModel.Rows [ self.data_table.selectionModel().select(index, mode) for index in indexes ] if len(rows) > 0: value = self.data_table.horizontalScrollBar().value() column = self.data_table.columnAt(value) self.data_table.scrollTo(self.data_table.model().index( rows[0], column)) @pyqtSlot(QModelIndex) def getData(self, index): pass #column = index.column() #row = index.row() #self.data_table.model().index(row, column).data() @pyqtSlot(str) def changeFilter(self, txt): if self.data is None: return if txt == 'All': self.sortfilter.setFilterRegExp(QRegExp()) else: self.sortfilter.setFilterKeyColumn(5) self.sortfilter.setFilterRegExp(txt.lower()) self.sortfilter.setFilterRole(Qt.DisplayRole) self.filterChanged.emit(txt.lower())
class SubtitleEditor(SubTab): def __init__(self, filePath, subtitleData, parent = None): name = os.path.split(filePath)[1] super(SubtitleEditor, self).__init__(name, parent) self.__initWidgets() self.__initContextMenu() self._settings = SubSettings() self._filePath = filePath self._movieFilePath = None self._subtitleData = subtitleData self.refreshSubtitles() # Some signals self._subtitleData.fileChanged.connect(self.fileChanged) self._subtitleData.subtitlesAdded.connect(self._subtitlesAdded) self._subtitleData.subtitlesRemoved.connect(self._subtitlesRemoved) self._subtitleData.subtitlesChanged.connect(self._subtitlesChanged) self._model.itemChanged.connect(self._subtitleEdited) self.customContextMenuRequested.connect(self.showContextMenu) def __initWidgets(self): minimalSizePolicy = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) # List of subtitles subListDelegate = SubListItemDelegate() self._model = QStandardItemModel(0, 3, self) self._model.setHorizontalHeaderLabels([_("Begin"), _("End"), _("Subtitle")]) self._subList = QTableView(self) self._subList.setModel(self._model) self._subList.setItemDelegateForColumn(0, subListDelegate) self._subList.setItemDelegateForColumn(1, subListDelegate) self._subList.horizontalHeader().setSectionResizeMode(2, QHeaderView.Stretch) self._searchBar = SearchBar(self) self._searchBar.hide() # Top toolbar toolbar = QHBoxLayout() toolbar.setAlignment(Qt.AlignLeft) #toolbar.addWidget(someWidget....) toolbar.addStretch(1) # Main layout grid = QGridLayout() grid.setSpacing(10) grid.setContentsMargins(0, 3, 0, 0) grid.addLayout(toolbar, 0, 0, 1, 1) # stretch to the right grid.addWidget(self._subList, 1, 0) grid.addWidget(self._searchBar, 2, 0) self.setLayout(grid) def __initContextMenu(self): self._contextMenu = QMenu(self) self.setContextMenuPolicy(Qt.CustomContextMenu) af = ActionFactory(self) insertSub = af.create(title = _("&Insert subtitle"), icon = "list-add", connection = self.insertNewSubtitle) self._contextMenu.addAction(insertSub) insertSub = af.create(title = _("&Add subtitle"), icon = "list-add", connection = self.addNewSubtitle) self._contextMenu.addAction(insertSub) removeSub = af.create(title = _("&Remove subtitles"), icon = "list-remove", connection = self.removeSelectedSubtitles) self._contextMenu.addAction(removeSub) def _changeRowBackground(self, rowNo, bg): with DisableSignalling(self._model.itemChanged, self._subtitleEdited): for columnNo in range(self._model.columnCount()): item = self._model.item(rowNo, columnNo) item.setBackground(bg) def _changeItemData(self, item, val, role): with DisableSignalling(self._model.itemChanged, self._subtitleEdited): item.setData(val, role) def _handleIncorrectItem(self, item): with DisableSignalling(self._model.itemChanged, self._subtitleEdited): item.setData(False, CustomDataRoles.ErrorFlagRole) bg = item.background() rowNo = item.row() self._changeRowBackground(rowNo, Qt.red) QTimer.singleShot(600, lambda rowNo=rowNo, bg=bg: self._changeRowBackground(rowNo, bg)) def _subtitleEdited(self, item): modelIndex = item.index() column = modelIndex.column() subNo = modelIndex.row() errorFlag = item.data(CustomDataRoles.ErrorFlagRole) if errorFlag is True: self._handleIncorrectItem(item) else: # TODO: timeStart and timeEnd might be displayed in a frame format in a bright future. # Check it and create FrameTime properly in that case. # TODO: Maybe add column numbers to some kind of enum to avoid magic numbers? oldSubtitle = self.subtitles[subNo] newSubtitle = oldSubtitle.clone() if 0 == column: timeStart = item.data(CustomDataRoles.FrameTimeRole) newSubtitle.change(start = timeStart) elif 1 == column: timeEnd = item.data(CustomDataRoles.FrameTimeRole) newSubtitle.change(end = timeEnd) elif 2 == column: newSubtitle.change(text = item.text()) command = ChangeSubtitle(self.filePath, oldSubtitle, newSubtitle, subNo) self._subtitleData.execute(command) def _subtitlesAdded(self, path, subNos): if path != self.filePath: return subtitles = self.subtitles for subNo in subNos: row = createRow(subtitles[subNo]) self._model.insertRow(subNo, row) def _subtitlesRemoved(self, path, subNos): if path != self.filePath: return for subNo in subNos: self._model.removeRow(subNo) def _subtitlesChanged(self, path, subNos): if path != self.filePath: return for subNo in subNos: self.refreshSubtitle(subNo) def _createNewSubtitle(self, data, subNo): fps = data.fps # data is passed to avoid unnecessary copies minFrameTime = FrameTime(fps, frames = 1) # calculate correct minimum subtitle start time if subNo > 0: timeStart = data.subtitles[subNo - 1].end + minFrameTime else: timeStart = FrameTime(fps, frames = 0) # calculate correct maximum subtitle end time if subNo < data.subtitles.size(): try: timeEnd = data.subtitles[subNo].start - minFrameTime except SubException: timeEnd = FrameTime(fps, frames = 0) else: timeEnd = timeStart + FrameTime(fps, frames = 50) # add subtitle to DataModel sub = Subtitle(timeStart, timeEnd, "") command = AddSubtitle(self.filePath, subNo, sub) with DisableSignalling(self._subtitleData.subtitlesAdded, self._subtitlesAdded): self._subtitleData.execute(command) # create subtitle graphical representation in editor sub list row = createRow(sub) self._model.insertRow(subNo, row) index = self._model.index(subNo, 2) self._subList.clearSelection() self._subList.setCurrentIndex(index) self._subList.edit(index) def addNewSubtitle(self): data = self.data subNo = data.subtitles.size() indices = self._subList.selectedIndexes() if len(indices) > 0: rows = [index.row() for index in indices] subNo = max(rows) + 1 self._createNewSubtitle(data, subNo) def insertNewSubtitle(self): data = self.data subNo = 0 indices = self._subList.selectedIndexes() if len(indices) > 0: rows = [index.row() for index in indices] subNo = max(rows) self._createNewSubtitle(data, subNo) def removeSelectedSubtitles(self): indices = self._subList.selectedIndexes() if len(indices) > 0: rows = list(set([index.row() for index in indices])) command = RemoveSubtitles(self.filePath, rows) self._subtitleData.execute(command) if self._model.rowCount() > rows[-1]: self._subList.selectRow(rows[-1]) else: self._subList.selectRow(self._model.rowCount() - 1) def highlight(self): self._searchBar.show() self._searchBar.highlight() def showContextMenu(self): self._contextMenu.exec(QCursor.pos()) def changeInputEncoding(self, encoding): data = self._subtitleData.data(self.filePath) if encoding != data.inputEncoding: try: data.encode(encoding) except UnicodeDecodeError: message = QMessageBox( QMessageBox.Warning, _("Decoding error"), _("Cannot decode subtitles to '%s' encoding.\nPlease try different encoding.") % encoding, QMessageBox.Ok, self ) message.exec() except LookupError: message = QMessageBox(QMessageBox.Warning, _("Unknown encoding"), _("Unknown encoding: '%s'") % encoding, QMessageBox.Ok, self ) message.exec() else: # TODO: outputEncoding command = ChangeData(self.filePath, data, _("Input encoding: %s") % encoding) self._subtitleData.execute(command) def changeOutputEncoding(self, encoding): data = self._subtitleData.data(self.filePath) if encoding != data.outputEncoding: data.outputEncoding = encoding command = ChangeData(self.filePath, data, _("Output encoding: %s") % encoding) self._subtitleData.execute(command) def changeSubFormat(self, fmt): data = self._subtitleData.data(self.filePath) if data.outputFormat != fmt: data.outputFormat = fmt command = ChangeData(self.filePath, data) self._subtitleData.execute(command) def changeFps(self, fps): data = self.data if data.fps != fps: data.subtitles.changeFps(fps) data.fps = fps command = ChangeData(self.filePath, data, _("FPS: %s") % fps) self._subtitleData.execute(command) def changeVideoPath(self, path): data = self.data if data.videoPath != path: data.videoPath = path command = ChangeData(self.filePath, data, _("Video path: %s") % path) self._subtitleData.execute(command) def offset(self, seconds): data = self.data fps = data.subtitles.fps if fps is None: log.error(_("No FPS for '%s' (empty subtitles)." % self.filePath)) return ft = FrameTime(data.subtitles.fps, seconds=seconds) data.subtitles.offset(ft) command = ChangeData(self.filePath, data, _("Offset by: %s") % ft.toStr()) self._subtitleData.execute(command) def detectFps(self): data = self.data if data.videoPath is not None: fpsInfo = File.detectFpsFromMovie(data.videoPath) if data.videoPath != fpsInfo.videoPath or data.fps != fpsInfo.fps: data.videoPath = fpsInfo.videoPath data.subtitles.changeFps(fpsInfo.fps) data.fps = fpsInfo.fps command = ChangeData(self.filePath, data, _("Detected FPS: %s") % data.fps) self._subtitleData.execute(command) def fileChanged(self, filePath): if filePath == self._filePath: self.refreshSubtitles() def refreshSubtitle(self, subNo): sub = self.subtitles[subNo] self._model.removeRow(subNo) self._model.insertRow(subNo, createRow(sub)) def refreshSubtitles(self): self._model.removeRows(0, self._model.rowCount()) for sub in self.subtitles: self._model.appendRow(createRow(sub)) def updateTab(self): self.refreshSubtitles() def selectedSubtitles(self): rows = self.selectedRows() subtitleList = [self.subtitles[row] for row in rows] return subtitleList def selectedRows(self): indices = self._subList.selectedIndexes() # unique list rows = list(set([index.row() for index in indices])) rows.sort() return rows def selectRow(self, row): self._subList.selectRow(row) @property def filePath(self): return self._filePath @property def movieFilePath(self): return self._movieFilePath @property def data(self): return self._subtitleData.data(self.filePath) @property def subtitles(self): return self.data.subtitles @property def history(self): return self._subtitleData.history(self.filePath) @property def inputEncoding(self): return self.data.inputEncoding @property def outputEncoding(self): return self.data.outputEncoding @property def outputFormat(self): return self.data.outputFormat
class ReqTableWidget(QWidget): requestsChanged = pyqtSignal(list) requestsSelected = pyqtSignal(list) def __init__(self, client, repeater_widget=None, macro_widget=None, *args, **kwargs): QWidget.__init__(self, *args, **kwargs) self.allow_save = False self.client = client self.repeater_widget = repeater_widget self.macro_widget = macro_widget self.query = [] self.req_view_widget = None self.setLayout(QStackedLayout()) self.layout().setContentsMargins(0, 0, 0, 0) self.tableModel = ReqListModel(self.client) self.tableView = QTableView() self.tableView.setModel(self.tableModel) self.tableView.verticalHeader().setSectionResizeMode( QHeaderView.ResizeToContents) self.tableView.horizontalHeader().setSectionResizeMode( QHeaderView.ResizeToContents) self.tableView.verticalHeader().hide() self.tableView.setSelectionBehavior(QAbstractItemView.SelectRows) #self.tableView.setSelectionMode(QAbstractItemView.SingleSelection) self.tableView.horizontalHeader().setStretchLastSection(True) self.tableView.selectionModel().selectionChanged.connect( self.on_select_change) self.tableModel.dataChanged.connect(self._paint_view) self.tableModel.rowsInserted.connect(self._on_rows_inserted) self.requestsChanged.connect(self.set_requests) self.requestsSelected.connect(self._updated_selected_request) self.selected_reqs = [] self.layout().addWidget(self.tableView) self.layout().addWidget( QLabel("<b>Loading requests from data file...</b>")) @pyqtSlot(HTTPRequest) def add_request(self, req): with DisableUpdates(self.tableView): if req.db_id != "": reqid = self.client.get_reqid(req) if self.client.check_request(self.query, reqid=reqid): self.tableModel.add_request_head(req) if req.unmangled and req.unmangled.db_id != "" and self.tableModel.has_request( req.unmangled): self.tableModel.delete_request(req.unmangled) else: if self.client.check_request(self.query, req=req): self.tableModel.add_request_head(req) @pyqtSlot() def clear(self): self.tableModel.clear() def get_requests(self): return self.tableModel.get_requests() @pyqtSlot(list) def set_requests(self, reqs, check_filter=False): to_add = [] if not check_filter: to_add = reqs else: for req in reqs: if req.db_id != "": reqid = self.client.get_reqid(req) if self.client.check_request(self.query, reqid=reqid): to_add.append(req) else: if self.client.check_request(self.query, req=req): to_add.append(req) with DisableUpdates(self.tableView): self.clear() self.tableModel.disable_sort() self.tableModel.add_requests(to_add) self.tableModel.enable_sort() self.set_is_not_loading() @pyqtSlot(HTTPRequest) def update_request(self, req): with DisableUpdates(self.tableView): self.tableModel.update_request(req) if req.db_id != "": if req.unmangled and req.unmangled.db_id != "": self.tableModel.delete_request( reqid=self.client.get_reqid(req.unmangled)) @pyqtSlot(str) def delete_request(self, reqid): with DisableUpdates(self.tableView): self.tableModel.delete_request(reqid=reqid) @pyqtSlot(list) def set_filter(self, query): self.query = query self.set_is_loading() self.client.query_storage_async(self.requestsChanged, self.query, headers_only=True) @pyqtSlot(list) def _updated_selected_request(self, reqs): if len(reqs) > 0: self.selected_reqs = reqs else: self.selected_reqs = [] @pyqtSlot(QModelIndex, int, int) def _on_rows_inserted(self, parent, first, last): rows = self.tableView.selectionModel().selectedRows() if len(rows) > 0: row = rows[0].row() idx = self.tableModel.index(row, 0, QModelIndex()) self.tableView.scrollTo(idx) @pyqtSlot(QItemSelection, QItemSelection) def on_select_change(self, newSelection, oldSelection): reqs = [] added = set() for rowidx in self.tableView.selectionModel().selectedRows(): row = rowidx.row() if row not in added: reqs.append(self.tableModel.req_by_ind(row)) added.add(row) self.requestsSelected.emit(reqs) @pyqtSlot() def clear_selection(self): self.tableView.clearSelection() def get_selected_request(self): # load the full request if len(self.selected_reqs) > 0: return self.client.load_by_reqheaders(self.selected_reqs[0]) else: return None def get_selected_requests(self): ret = [] for hreq in self.selected_reqs: ret.append(self.client.load_by_reqheaders(hreq)) return ret def get_all_requests(self): return [ self.client.req_by_id(self.client.get_reqid(req)) for req in self.tableModel.get_requests() ] def contextMenuEvent(self, event): if len(self.selected_reqs) > 1: reqs = self.get_selected_requests() display_multi_req_context(self, self.client, reqs, event, macro_widget=self.macro_widget, save_option=self.allow_save) elif len(self.selected_reqs) == 1: req = self.get_selected_request() display_req_context(self, self.client, req, event, repeater_widget=self.repeater_widget, req_view_widget=self.req_view_widget, macro_widget=self.macro_widget, save_option=self.allow_save) def set_is_loading(self): self.set_loading(True) def set_is_not_loading(self): self.set_loading(False) def set_loading(self, is_loading): with DisableUpdates(self.tableView): if is_loading: self.layout().setCurrentIndex(1) else: self.layout().setCurrentIndex(0) @pyqtSlot(QModelIndex, QModelIndex) def _paint_view(self, indA, indB): self.tableView.repaint() @pyqtSlot() def delete_selected(self): with DisableUpdates(self.tableView): for req in self.selected_reqs: self.tableModel.delete_request(req=req)
class ParamModWgt(QWidget): def __init__(self, parent=None): super().__init__(parent) self.main_window = parent self.buildRequiredTagsGB() # Widgets self.newParamBtn = QPushButton("New") self.deleteParamBtn = QPushButton("Delete") self.paramSaveAnnotBtn = QPushButton("Save") buttonWidget = QWidget(self) self.existingParamsGB = QGroupBox("Existing parameters", self) self.paramListTblWdg = QTableView() self.paramListModel = ParameterListModel(parent=self) self.paramListTblWdg.setSelectionBehavior(QAbstractItemView.SelectRows) self.paramListTblWdg.setSelectionMode(QAbstractItemView.SingleSelection) self.paramListTblWdg.setModel(self.paramListModel) self.paramListTblWdg.setColumnWidth(0, 150) self.paramListTblWdg.setColumnWidth(1, 350) self.relationWgt = ParamRelationWgt(parent) self.newParamsGB = QGroupBox("Parameter details", self) self.resultTypeCbo = QComboBox(self) self.isExpProp = QCheckBox("is an experimental property", self) self.resultTypeCbo.addItems(["point value", "function", "numerical trace"]) self.singleValueParamWgt = ParamValueWgt(parent) self.functionParamWgt = ParamFunctionWgt(parent) self.traceParamWgt = ParamTraceWgt(parent) self.functionParamWgt.mainWgt = self self.paramModStack = QStackedWidget(self) self.paramModStack.addWidget(self.singleValueParamWgt) self.paramModStack.addWidget(self.functionParamWgt) self.paramModStack.addWidget(self.traceParamWgt) # Signals selectionModel = self.paramListTblWdg.selectionModel() selectionModel.selectionChanged.connect(self.selectedParameterChanged) self.newParamBtn.clicked.connect(self.newParameter) self.deleteParamBtn.clicked.connect(self.deleteParameter) self.paramSaveAnnotBtn.clicked.connect(self.saveParameter) self.resultTypeCbo.currentIndexChanged.connect(self.paramModStack.setCurrentIndex) self.singleValueParamWgt.paramTypeSelected.connect(self.newParamTypeSelected) self.functionParamWgt.paramTypeSelected.connect(self.newParamTypeSelected) self.traceParamWgt.paramTypeSelected.connect(self.newParamTypeSelected) # Layout buttonLayout = QVBoxLayout(buttonWidget) buttonLayout.addWidget(self.paramSaveAnnotBtn) buttonLayout.addWidget(self.deleteParamBtn) buttonLayout.addWidget(self.newParamBtn) existGrid = QHBoxLayout(self.existingParamsGB) existGrid.addWidget(buttonWidget) existGrid.addWidget(self.paramListTblWdg) newGrid = QGridLayout(self.newParamsGB) newGrid.addWidget(QLabel("Result type"), 0, 0) newGrid.addWidget(self.resultTypeCbo, 0, 1) newGrid.addWidget(self.isExpProp, 0, 2) newGrid.addWidget(self.paramModStack, 1, 0, 1, 3) newGrid.addWidget(self.relationWgt, 1, 3) layout = QVBoxLayout(self) self.rootLayout = QSplitter(Qt.Vertical, self) self.rootLayout.setOrientation(Qt.Vertical) self.rootLayout.addWidget(self.existingParamsGB) self.rootLayout.addWidget(self.newParamsGB) self.rootLayout.addWidget(self.requireTagGB) layout.addWidget(self.rootLayout) # Initial behavior self.newParamBtn.setEnabled(True) self.deleteParamBtn.setEnabled(False) self.paramSaveAnnotBtn.setEnabled(False) self.additionMode = False self.newParamsGB.setEnabled(False) def setRootLayoutSizes(self, sizes): self.rootLayout.setSizes(sizes) def viewParameter(self, parameter): row = -1 for row, param in enumerate(self.paramListModel.parameterList): if param.id == parameter.id: break assert(row > -1) self.paramListTblWdg.selectRow(row) @pyqtSlot(str) def newParamTypeSelected(self, paramName): self.requiredTagsListModel.clear() self.requiredTagsListModel.refresh() paramType = getParameterTypeFromName(paramName) if paramType is None: raise ValueError("Parameter type with name '" + paramName + "' was not found.") for reqTag in paramType.requiredTags: self.requiredTagsListModel.addTag(reqTag.id, reqTag.name, reqTag.id, reqTag.name) self.requiredTagsListModel.refresh() def buildRequiredTagsGB(self): # Widgets self.requireTagGB = QGroupBox("Required tag categories", self) self.requiredTagsListTblWdg = RequiredTagsTableView() self.requiredTagsListModel = RequiredTagsListModel(parent=self) self.requiredTagsListTblWdg.setSelectionBehavior(QAbstractItemView.SelectRows) self.requiredTagsListTblWdg.setSelectionMode(QAbstractItemView.SingleSelection) self.requiredTagsListTblWdg.setModel(self.requiredTagsListModel) self.requiredTagsListTblWdg.setColumnWidth(0, 200) self.requiredTagsListTblWdg.setColumnWidth(1, 200) # Layout requiredTagLayout = QGridLayout(self.requireTagGB) requiredTagLayout.addWidget(self.requiredTagsListTblWdg, 0, 0, 4, 1) def newParameter(self): self.resultTypeCbo.setCurrentIndex(0) self.paramModStack.currentWidget().newParameter() self.singleValueParamWgt.newParameter() self.functionParamWgt.newParameter() self.traceParamWgt.newParameter() self.newParamsGB.setEnabled(True) self.paramListTblWdg.clearSelection() self.newParamBtn.setEnabled(False) self.deleteParamBtn.setEnabled(False) self.paramSaveAnnotBtn.setEnabled(True) self.isExpProp.setChecked(False) def saveParameter(self): relationship = self.relationWgt.getRelationship() # Get the ID of the modified parameter if we are modifying an existing # parameters if len(self.paramListTblWdg.selectionModel().selectedRows()) != 0: selectedRow = self.paramListTblWdg.selectionModel().currentIndex().row() paramId = self.main_window.currentAnnotation.parameters[selectedRow].id else: paramId = None param = self.paramModStack.currentWidget().saveParameter(relationship, paramId) if not param is None: param.requiredTags = self.requiredTagsListModel.getRequiredTags() param.isExperimentProperty = self.isExpProp.isChecked() selectedRow = self.paramListTblWdg.selectionModel().currentIndex().row() # Even when there is no selection, selectedRow can take a zero value. This "if" # controls for that. if len(self.paramListTblWdg.selectionModel().selectedRows()) == 0: selectedRow = -1 if selectedRow >= 0: self.main_window.currentAnnotation.parameters[selectedRow] = param else: self.main_window.currentAnnotation.parameters.append(param) self.additionMode = False nbParams = len(self.main_window.currentAnnotation.parameters) self.main_window.saveAnnotation() if selectedRow < 0 : selectedRow = nbParams-1 self.paramListTblWdg.selectRow(selectedRow) self.loadRow(selectedRow) def deleteParameter(self): selectedRow = self.paramListTblWdg.selectionModel().currentIndex().row() del self.main_window.currentAnnotation.parameters[selectedRow] self.main_window.saveAnnotation() self.refreshModelingParameters() def refreshModelingParameters(self): selectedRow = self.paramListTblWdg.selectionModel().currentIndex().row() self.loadModelingParameter(selectedRow) def loadModelingParameter(self, row = None): """ Call when a new annotation has been selected so that all the modeling parameters associated with this annotation are loaded in the parameter list. """ self.requiredTagsListModel.clear() self.requiredTagsListModel.refresh() if self.main_window.currentAnnotation is None: self.paramListModel.parameterList = [] else: self.paramListModel.parameterList = self.main_window.currentAnnotation.parameters aRowIsSelected = not row is None if aRowIsSelected: if row < 0: noRowToLoad = self.paramListTblWdg.model().rowCount()-row else: noRowToLoad = row else: ## No rows are selected noRowToLoad = -1 self.loadRow(noRowToLoad) self.newParamBtn.setEnabled(True) self.deleteParamBtn.setEnabled(aRowIsSelected) self.paramSaveAnnotBtn.setEnabled(aRowIsSelected) self.paramModStack.currentWidget().loadModelingParameter(row) self.relationWgt.loadModelingParameter(row) self.newParamsGB.setEnabled(aRowIsSelected) self.paramListModel.refresh() def loadRow(self, selectedRow = None): """ Called when a row has been selected in the table listing all the modeling parameters. It update the interface with the values associated with this specific parameter. """ def nlxCheck(id): if id in nlx2ks: return nlx2ks[id] return id def clear(): self.requiredTagsListModel.clear() self.paramModStack.currentWidget().loadRow(None) self.relationWgt.clear() self.paramListTblWdg.clearSelection() if selectedRow is None: selectedRow = self.paramListTblWdg.selectionModel().currentIndex().row() if self.main_window.currentAnnotation is None: clear() return if selectedRow < 0 or selectedRow >= len(self.main_window.currentAnnotation.parameters) : clear() return currentParameter = self.main_window.currentAnnotation.parameters[selectedRow] self.newParamBtn.setEnabled(True) self.deleteParamBtn.setEnabled(True) self.paramSaveAnnotBtn.setEnabled(True) if currentParameter.description.type == "pointValue": self.resultTypeCbo.setCurrentIndex(0) self.paramModStack.setCurrentIndex(0) elif currentParameter.description.type == "function": self.resultTypeCbo.setCurrentIndex(1) self.paramModStack.setCurrentIndex(1) elif currentParameter.description.type == "numericalTrace": self.resultTypeCbo.setCurrentIndex(2) self.paramModStack.setCurrentIndex(2) else: raise ValueError("Type of parameter description " + currentParameter.description.type + " is invalid.") self.paramModStack.currentWidget().loadRow(currentParameter) self.relationWgt.loadRow(currentParameter) self.isExpProp.setChecked(currentParameter.isExperimentProperty) ## UPDATING REQUIRED TAGS self.requiredTagsListModel.clear() for tag in currentParameter.requiredTags: self.requiredTagsListModel.addTag(tag.rootId, self.main_window.dicData[tag.rootId], tag.id, tag.name) ## Adding new required tags that may have been specified since the ## creation of this parameter instance. parameterType = getParameterTypeFromID(currentParameter.typeId) reqTags = {reqTag.rootId:reqTag for reqTag in parameterType.requiredTags} for reqTagRootId, reqTag in reqTags.items(): #print(nlxCheck(reqTagRootId), [nlxCheck(tag.rootId) for tag in currentParameter.requiredTags]) if not nlxCheck(reqTagRootId) in [nlxCheck(tag.rootId) for tag in currentParameter.requiredTags]: self.requiredTagsListModel.addTag(reqTag.rootId, self.main_window.dicData[reqTag.rootId], reqTag.id, reqTag.name) self.requiredTagsListModel.refresh() self.newParamsGB.setEnabled(True) def selectedParameterChanged(self, selected, deselected): if len(selected.indexes()) == 0: return if self.additionMode: msgBox = QMessageBox(self) msgBox.setWindowTitle("Cancellation") msgBox.setText("Are you sure you want to cancel the addition of the new parameter being edited? If not, say no and then hit 'Save' to save this new parameter.") msgBox.setStandardButtons(QMessageBox.No | QMessageBox.Yes) msgBox.setDefaultButton(QMessageBox.No) if msgBox.exec_() == QMessageBox.Yes: self.additionMode = False self.loadRow() else: #self.paramListTblWdg.selectRow(-1) self.paramListTblWdg.clearSelection() else: self.loadRow()
class DataTab(QScrollArea): def __init__(self, parent=None): super().__init__(parent) def display(self, path: str): self.tab = TabsWidget.QUERY_TAB_INDEX self.initial_query: Query = None self.initial_result_set: DataFrame = None self.rfd: RFD = None self.extended_query: Query = None self.extended_result_set: DataFrame = None self.relaxed_query: Query = None self.relaxed_result_set: DataFrame = None self.setWidgetResizable(True) self.setLayout(QVBoxLayout()) self.csv_parser: CSVParser = CSVParser(path) self.data_frame: DataFrame = self.csv_parser.data_frame self.table = QTableView() pandas_model: QAbstractTableModel = PandasTableModel( self.data_frame, self) self.table.setModel(pandas_model) self.table.setSortingEnabled(False) self.table.horizontalHeader().setSectionResizeMode( QHeaderView.Stretch) # full width table self.table.setSelectionMode(QAbstractItemView.MultiSelection) self.table.setSelectionBehavior(QAbstractItemView.SelectRows) delegate = ItemDelegate(self) self.table.setItemDelegate(delegate) self.table.horizontalHeader().setSectionsClickable(False) self.table.verticalHeader().setSectionsClickable(False) for i in reversed(range(self.layout().count())): self.layout().itemAt(i).widget().deleteLater() self.extent_label: QLabel = QLabel('Extent percentage: 0%') self.layout().addWidget(self.extent_label) self.layout().addWidget(self.table) def set_initial_query_subject(self, query_subject: Subject): self.initial_query_subject: Subject = query_subject self.initial_query_subject.subscribe( on_next=lambda query: (self.__update_initial_query(query))) def __update_initial_query(self, query: Query): print("Update initial query:") self.initial_query: Query = query self.initial_result_set: DataFrame = self.data_frame.query( self.initial_query.to_expression()) self.__highlight_initial_query() def __highlight_initial_query(self): if self.tab == TabsWidget.QUERY_TAB_INDEX: if hasattr(self, "table") and self.table: self.table.clearSelection() if hasattr(self, "initial_result_set" ) and self.initial_result_set is not None: df_indexes = self.initial_result_set.index.values.tolist() for index in df_indexes: self.table.selectRow(index) extent_percentage = self.initial_result_set.shape[ 0] / self.data_frame.shape[0] self.extent_label.setText('Extent percentage: ' + str(round(extent_percentage * 100, 2)) + '%') elif hasattr(self, 'extent_label'): self.extent_label.setText('Extent percentage: 0%') def set_rfd_subject(self, rfd_subject: Subject): self.rfd_subject: Subject = rfd_subject self.rfd_subject.subscribe( on_next=lambda rfd: (self.__update_selected_rfd(rfd))) def __update_selected_rfd(self, rfd: RFD): print("Update selected RFD...") self.rfd: RFD = rfd self.__highlight_rfd() def __highlight_rfd(self): if self.tab == TabsWidget.RFDS_TAB_INDEX: if hasattr(self, "table") and self.table: self.table.clearSelection() if hasattr( self, "data_frame") and self.data_frame is not None and self.rfd: rfd_df_indexes = RFDExtent.extent_indexes( self.data_frame, self.rfd) for index in rfd_df_indexes: self.table.selectRow(index) extent_percentage = len( rfd_df_indexes) / self.data_frame.shape[0] self.extent_label.setText('Extent percentage: ' + str(round(extent_percentage * 100, 2)) + '%') elif hasattr(self, "extent_label"): self.extent_label.setText('Extent percentage: 0%') def set_extended_query_subject(self, query_subject: Subject): self.extended_query_subject: Subject = query_subject self.extended_query_subject.subscribe( on_next=lambda query: (self.__update_extended_query(query))) def __update_extended_query(self, query: Query): print("Update extended query...") self.extended_query: Query = query self.extended_result_set: DataFrame = self.data_frame.query( self.extended_query.to_expression()) self.__highlight_extended_query() def __highlight_extended_query(self): if self.tab == TabsWidget.EXTENSION_TAB_INDEX: if hasattr(self, "table") and self.table: self.table.clearSelection() if hasattr(self, "extended_result_set" ) and self.extended_result_set is not None: df_indexes = self.extended_result_set.index.values.tolist() for index in df_indexes: self.table.selectRow(index) extent_percentage = self.extended_result_set.shape[ 0] / self.data_frame.shape[0] self.extent_label.setText('Extent percentage: ' + str(round(extent_percentage * 100, 2)) + '%') elif hasattr(self, 'extent_label'): self.extent_label.setText('Extent percentage: 0%') def set_relaxed_query_subject(self, query_subject: Subject): self.relaxed_query_subject: Subject = query_subject self.relaxed_query_subject.subscribe( on_next=lambda query: (self.__update_relaxed_query(query))) def __update_relaxed_query(self, query: Query): print("Update relaxed query...") self.relaxed_query: Query = query self.relaxed_result_set: DataFrame = self.data_frame.query( self.relaxed_query.to_expression()) self.__highlight_relaxed_query() def __highlight_relaxed_query(self): if self.tab == TabsWidget.RELAX_TAB_INDEX: if hasattr(self, "table") and self.table: self.table.clearSelection() if hasattr(self, "relaxed_result_set" ) and self.relaxed_result_set is not None: df_indexes = self.relaxed_result_set.index.values.tolist() for index in df_indexes: self.table.selectRow(index) extent_percentage = self.relaxed_result_set.shape[ 0] / self.data_frame.shape[0] self.extent_label.setText('Extent percentage: ' + str(round(extent_percentage * 100, 2)) + '%') elif hasattr(self, 'extent_label'): self.extent_label.setText('Extent percentage: 0%') def onTabChange(self, index): print("DataTab: On Tab Change...") if index == TabsWidget.QUERY_TAB_INDEX: print("Query TAB") self.tab = TabsWidget.QUERY_TAB_INDEX self.__highlight_initial_query() elif index == TabsWidget.RFDS_TAB_INDEX: print("RFD TAB") self.tab = TabsWidget.RFDS_TAB_INDEX self.__highlight_rfd() elif index == TabsWidget.EXTENSION_TAB_INDEX: print("Extension TAB") self.tab = TabsWidget.EXTENSION_TAB_INDEX self.__highlight_extended_query() elif index == TabsWidget.RELAX_TAB_INDEX: print("Relax TAB") self.tab = TabsWidget.RELAX_TAB_INDEX self.__highlight_relaxed_query()
class GDriveTableController(QObject): did_change_path = pyqtSignal() def __init__(self, folder_id="root"): super().__init__() self._file_thread = None self.table_view = QTableView() self.files = [] self._model = gdrive_table_model.GDriveTableModel() self.table_view.setModel(self._model) self._set_table_view_style() self._connect_table_view_signals() self._update_files(folder_id) self.path = [file_handler.get_gdrive_file(folder_id)] def _set_table_view_style(self): self.table_view.verticalHeader().hide() self.table_view.setSelectionBehavior(QAbstractItemView.SelectRows) self.table_view.setSelectionMode(QAbstractItemView.SingleSelection) self.table_view.setShowGrid(False) self.table_view.setUpdatesEnabled(True) self.table_view.setColumnWidth(0, 230) self.table_view.setColumnWidth(1, 150) self.table_view.setColumnWidth(2, 120) vertical_header = self.table_view.verticalHeader() vertical_header.setSectionResizeMode(QHeaderView.Fixed) vertical_header.setDefaultSectionSize(24) horizontal_header = self.table_view.horizontalHeader() horizontal_header.setHighlightSections(False) # horizontal_header.setSectionResizeMode() def _connect_table_view_signals(self): selectionModel = self.table_view.selectionModel() selectionModel.currentRowChanged.connect(self.did_select_row_index) self.table_view.doubleClicked.connect(self.did_double_click_row_index) def _update_files(self, folder_id): def did_get_files(files): self.files = files self._model.load_files(self.files) self._model.refresh() # self.table_view.viewport().update() self._set_loading_mode(False) def error_occurred(error): print(error) if self._file_thread == None: self._file_thread = FileThread() self._file_thread.did_get_files.connect(did_get_files) self._file_thread.error_occurred.connect(error_occurred) self._file_thread.file_id = folder_id self._set_loading_mode(True) self._file_thread.run() def _set_loading_mode(self, is_loading): if is_loading: self.table_view.setEnabled(False) else : self.table_view.setEnabled(True) def did_double_click_row_index(self, index): row = index.row() file = self.files[row] if file.is_folder: self.path.append(file) self._update_files(str(file.id)) self.table_view.clearSelection() self.did_change_path.emit() def did_select_row_index(self, index): row = index.row() # print("row changed to " + str(row)) def go_back(self): if len(self.path) > 1: self.path.pop() self._update_files(str(self.path[-1].id)) self.table_view.clearSelection() self.did_change_path.emit()
class StickyLoad(QDialog): def __init__(self): super().__init__() self.resize(600, 400) # Получение id стикера для загрузки self.setLayout(self.myLayout()) # Словарь горячих клавиш self.hotkeys = { (Qt.Key_W, int(Qt.ControlModifier)): self.close, (Qt.Key_Q, int(Qt.ControlModifier)): qApp.myQuit, (Qt.Key_Enter - 1, int(Qt.NoModifier)): self.getSIDandClose, (Qt.Key_Delete, int(Qt.NoModifier)): self.deleteRow } self.view.doubleClicked.connect(self.getSIDandClose) self.exec_() def myLayout(self): qmodel = QtSql.QSqlQueryModel() qmodel.setQuery("select id, text from sticky order by id desc") self.view = QTableView(self) self.view.setModel(qmodel) self.view.verticalHeader().hide() self.view.resizeColumnToContents(0) self.view.horizontalHeader().setStretchLastSection(True) self.view.setSelectionBehavior(QTableView.SelectRows) self.view.setSelectionMode(QTableView.SingleSelection) self.view.selectRow(0) layout = QVBoxLayout() layout.addWidget(self.view) layout.setContentsMargins(0, 0, 0, 0) return layout def getSID(self): index = self.view.selectedIndexes()[0] self.sid = self.view.model().data(index) def getSIDandClose(self): self.getSID() self.close() def keyPressEvent(self, event): self.hotkeys.get((event.key(), int(event.modifiers())), lambda: None)() def deleteRow(self): self.getSID() qmodel = QtSql.QSqlQueryModel() qmodel.setQuery("delete from sticky where id = {}".format(self.sid)) deletedRow = self.view.selectedIndexes()[0].row() for x in (i for j in (range(deletedRow + 1, self.view.model().rowCount()), range(deletedRow - 1, -1, -1)) for i in j): if self.view.isRowHidden(x): continue rowToSelecet = x break try: self.view.selectRow(rowToSelecet) except: self.view.clearSelection() self.view.hideRow(deletedRow) del self.sid def __repr__(self): return repr(self.sid)
class Viewer(QMainWindow): def __init__(self, parent=None): super(Viewer, self).__init__(parent) self.MaxRecentFiles = 5 self.windowList = [] self.recentFiles = [] self.settings = QSettings('Axel Schneider', 'QTableViewPandas') self.filename = "" self.setGeometry(0, 0, 800, 600) self.lb = QTableView() self.lb.verticalHeader().setVisible(True) self.lb.setGridStyle(1) self.model = PandasModel() self.lb.setModel(self.model) self.lb.setEditTriggers(QAbstractItemView.DoubleClicked) self.lb.setSelectionBehavior(self.lb.SelectRows) self.lb.setSelectionMode(self.lb.SingleSelection) self.setStyleSheet(stylesheet(self)) self.lb.setAcceptDrops(True) self.setCentralWidget(self.lb) self.setContentsMargins(10, 10, 10, 10) self.createToolBar() self.readSettings() self.lb.setFocus() self.statusBar().showMessage("Ready", 0) def readSettings(self): print("reading settings") if self.settings.contains("geometry"): self.setGeometry(self.settings.value('geometry')) if self.settings.contains("recentFiles"): self.recentFiles = self.settings.value('recentFiles') self.lastFiles.addItem("last Files") self.lastFiles.addItems(self.recentFiles[:15]) def saveSettings(self): print("saving settings") self.settings.setValue('geometry', self.geometry()) self.settings.setValue('recentFiles', self.recentFiles) def closeEvent(self, event): print(self.model.setChanged) if self.model.setChanged == True: print("is changed, saving?") quit_msg = "<b>The document was changed.<br>Do you want to save the changes?</ b>" reply = QMessageBox.question(self, 'Save Confirmation', quit_msg, QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) if reply == QMessageBox.Yes: self.writeCSV_update() else: print("not saved, goodbye ...") return else: print("nothing changed. goodbye") self.saveSettings() def createToolBar(self): openAction = QAction(QIcon.fromTheme("document-open"), "Open", self, triggered=self.loadCSV, shortcut=QKeySequence.Open) saveAction = QAction(QIcon.fromTheme("document-save"), "Save", self, triggered=self.writeCSV_update, shortcut=QKeySequence.Save) saveAsAction = QAction(QIcon.fromTheme("document-save-as"), "Save as ...", self, triggered=self.writeCSV, shortcut=QKeySequence.SaveAs) self.tbar = self.addToolBar("File") self.tbar.setContextMenuPolicy(Qt.PreventContextMenu) self.tbar.setIconSize(QSize(16, 16)) self.tbar.setMovable(False) self.tbar.addAction(openAction) self.tbar.addAction(saveAction) self.tbar.addAction(saveAsAction) empty = QWidget() empty.setFixedWidth(10) self.tbar.addWidget(empty) self.lastFiles = QComboBox() self.lastFiles.setFixedWidth(300) self.lastFiles.currentIndexChanged.connect(self.loadRecent) self.tbar.addWidget(self.lastFiles) empty = QWidget() empty.setFixedWidth(10) self.tbar.addWidget(empty) findbyText = QAction(QIcon.fromTheme("edit-find-symbolic"), "find", self, triggered=self.findInTable) self.lineFind = QLineEdit() self.lineFind.addAction(findbyText, 0) self.lineFind.setPlaceholderText("find") self.lineFind.setClearButtonEnabled(True) self.lineFind.setFixedWidth(250) self.lineFind.returnPressed.connect(self.findInTable) self.tbar.addWidget(self.lineFind) self.tbar.addAction(findbyText) empty = QWidget() empty.setFixedWidth(10) self.tbar.addWidget(empty) self.previewAction = QAction(QIcon.fromTheme("document-print-preview"), "print", self, triggered=self.handlePreview) self.tbar.addAction(self.previewAction) self.printAction = QAction(QIcon.fromTheme("document-print"), "print", self, triggered=self.handlePrint) self.tbar.addAction(self.printAction) def loadRecent(self): if self.lastFiles.currentIndex() > 0: print(self.lastFiles.currentText()) print(self.model.setChanged) if self.model.setChanged == True: print("is changed, saving?") quit_msg = "<b>The document was changed.<br>Do you want to save the changes?</ b>" reply = QMessageBox.question(self, 'Save Confirmation', quit_msg, QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) if reply == QMessageBox.Yes: self.openCSV(self.lastFiles.currentText()) else: self.openCSV(self.lastFiles.currentText()) else: self.openCSV(self.lastFiles.currentText()) def openCSV(self, path): f = open(path, 'r+b') with f: df = pd.read_csv(f, delimiter='\t', keep_default_na=False, low_memory=False, header=None) f.close() self.model = PandasModel(df) self.lb.setModel(main.model) self.lb.resizeColumnsToContents() self.lb.selectRow(0) self.statusBar().showMessage("%s %s" % (path, "loaded"), 0) def findInTable(self): self.lb.clearSelection() text = self.lineFind.text() model = self.lb.model() for column in range(self.model.columnCount()): start = model.index(0, column) matches = model.match(start, Qt.DisplayRole, text, -1, Qt.MatchContains) if matches: for index in matches: print(index.row(), index.column()) self.lb.selectionModel().select(index, QItemSelectionModel.Select) def openFile(self, path=None): print(self.model.setChanged) if self.model.setChanged == True: print("is changed, saving?") quit_msg = "<b>The document was changed.<br>Do you want to save the changes?</ b>" reply = QMessageBox.question(self, 'Save Confirmation', quit_msg, QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) if reply == QMessageBox.Yes: self.writeCSV_update() else: print("not saved, loading ...") return path, _ = QFileDialog.getOpenFileName( self, "Open File", QDir.homePath() + "/Dokumente/CSV/", "CSV Files (*.csv)") if path: return path def loadCSV(self): fileName = self.openFile() if fileName: print(fileName + " loaded") f = open(fileName, 'r+b') with f: df = pd.read_csv(f, delimiter='\t', keep_default_na=False, low_memory=False, header=None) f.close() self.model = PandasModel(df) self.lb.setModel(self.model) self.lb.resizeColumnsToContents() self.lb.selectRow(0) self.statusBar().showMessage("%s %s" % (fileName, "loaded"), 0) self.recentFiles.insert(0, fileName) self.lastFiles.insertItem(1, fileName) def writeCSV(self): fileName, _ = QFileDialog.getSaveFileName(self, "Open File", self.filename, "CSV Files (*.csv)") if fileName: print(fileName + " saved") f = open(fileName, 'w') newModel = self.model dataFrame = newModel._df.copy() dataFrame.to_csv(f, sep='\t', index=False, header=False) def writeCSV_update(self): if self.filename: f = open(self.filename, 'w') newModel = self.model dataFrame = newModel._df.copy() dataFrame.to_csv(f, sep='\t', index=False, header=False) self.model.setChanged = False print("%s %s" % (self.filename, "saved")) self.statusBar().showMessage("%s %s" % (self.filename, "saved"), 0) def handlePrint(self): if self.model.rowCount() == 0: self.msg("no rows") else: dialog = QtPrintSupport.QPrintDialog() if dialog.exec_() == QDialog.Accepted: self.handlePaintRequest(dialog.printer()) print("Document printed") def handlePreview(self): if self.model.rowCount() == 0: self.msg("no rows") else: dialog = QtPrintSupport.QPrintPreviewDialog() dialog.setFixedSize(1000, 700) dialog.paintRequested.connect(self.handlePaintRequest) dialog.exec_() print("Print Preview closed") def handlePaintRequest(self, printer): printer.setDocName(self.filename) document = QTextDocument() cursor = QTextCursor(document) model = self.lb.model() tableFormat = QTextTableFormat() tableFormat.setBorder(0.2) tableFormat.setBorderStyle(3) tableFormat.setCellSpacing(0) tableFormat.setTopMargin(0) tableFormat.setCellPadding(4) table = cursor.insertTable(model.rowCount() + 1, model.columnCount(), tableFormat) model = self.lb.model() ### get headers myheaders = [] for i in range(0, model.columnCount()): myheader = model.headerData(i, Qt.Horizontal) cursor.insertText(str(myheader)) cursor.movePosition(QTextCursor.NextCell) ### get cells for row in range(0, model.rowCount()): for col in range(0, model.columnCount()): index = model.index(row, col) cursor.insertText(str(index.data())) cursor.movePosition(QTextCursor.NextCell) document.print_(printer)
class Viewer(QMainWindow): def __init__(self, parent=None): super(Viewer, self).__init__(parent) self.filename = "" self.fname = "" self.csv_file = '' self.isChanged = False self.mychannels_file = 'myradio.txt' self.mychannels_file_backup = 'myradio.txt_backup' self.setGeometry(0, 0, 1000, 600) self.lb = QTableView() self.lb.horizontalHeader().hide() self.model = QStandardItemModel() self.lb.setModel(self.model) self.model.itemChanged.connect(self.finishedEdit) self.lb.setEditTriggers(QAbstractItemView.DoubleClicked) self.lb.setSelectionBehavior(self.lb.SelectRows) self.lb.setSelectionMode(self.lb.SingleSelection) self.lb.setDragDropMode(self.lb.InternalMove) self.setStyleSheet(stylesheet(self)) self.lb.setAcceptDrops(True) self.setCentralWidget(self.lb) self.setContentsMargins(10, 10, 10, 10) self.statusBar().showMessage("Willkommen", 0) self.setWindowTitle("myRadio Listeneditor") self.setWindowIcon(QIcon.fromTheme("multimedia-playlist")) #self.createMenuBar() self.createToolBar() self.create_backup() self.show() print("Hello") self.open_channels(self.mychannels_file) self.lb.setFocus() def finishedEdit(self): self.isChanged = True def setChanged(self): self.isChanged = True def msgbox(self, message): msg = QMessageBox(2, "Information", message, QMessageBox.Ok) msg.exec() def create_backup(self): if shutil.copyfile(self.mychannels_file, self.mychannels_file_backup): self.msgbox('myradio.txt_backup wurde erstellt') def closeEvent(self, event): print(self.isChanged) if self.isChanged == True: quit_msg = "<b>Das Dokument wurde geändert.<br>Wollen Sie die Änderungen speichern?</ b>" reply = QMessageBox.question(self, 'Speichern', quit_msg, QMessageBox.Yes, QMessageBox.No) if reply == QMessageBox.Yes: event.accept() self.save_file(self.filename) else: print("keine Änderungen.") def createMenuBar(self): bar=self.menuBar() self.filemenu=bar.addMenu("Datei") self.separatorAct = self.filemenu.addSeparator() self.filemenu.addAction(QIcon.fromTheme("document-open"), "Datei öffnen", self.openFile, QKeySequence.Open) self.filemenu.addAction(QIcon.fromTheme("document-save-as"), "Speichern als ...", self.writeCSV, QKeySequence.SaveAs) def createToolBar(self): tb = self.addToolBar("Werkzeuge") tb.setIconSize(QSize(16, 16)) self.findfield = QLineEdit(placeholderText = "finden ...") self.findfield.setClearButtonEnabled(True) self.findfield.setFixedWidth(200) tb.addWidget(self.findfield) tb.addSeparator() self.replacefield = QLineEdit(placeholderText = "ersetzen mit ...") self.replacefield.setClearButtonEnabled(True) self.replacefield.setFixedWidth(200) tb.addWidget(self.replacefield) tb.addSeparator() btn = QToolButton() btn.setText("alles ersetzen") btn.setToolTip("alles ersetzen") btn.clicked.connect(self.replace_in_table) tb.addWidget(btn) tb.addSeparator() del_btn = QToolButton() del_btn.setIcon(QIcon.fromTheme("edit-delete")) del_btn.setToolTip("Zeile löschen") del_btn.clicked.connect(self.del_row) tb.addWidget(del_btn) tb.addSeparator() add_btn = QToolButton() add_btn.setIcon(QIcon.fromTheme("add")) add_btn.setToolTip("Zeile hinzufügen") add_btn.clicked.connect(self.add_row) tb.addWidget(add_btn) move_down_btn = QToolButton() move_down_btn.setIcon(QIcon.fromTheme("down")) move_down_btn.setToolTip("nach unten bewegen") move_down_btn.clicked.connect(self.move_down) tb.addWidget(move_down_btn) move_up_up = QToolButton() move_up_up.setIcon(QIcon.fromTheme("up")) move_up_up.setToolTip("nach oben bewegen") move_up_up.clicked.connect(self.move_up) tb.addWidget(move_up_up) spacer = QWidget() spacer.setSizePolicy(QSizePolicy.Expanding, 0) tb.addWidget(spacer) self.filter_field = QLineEdit(placeholderText = "Filtern (Enter drücken)") self.filter_field.setClearButtonEnabled(True) self.filter_field.setToolTip("Suchbegriff einfügen und Enter drücken") self.filter_field.setFixedWidth(200) self.filter_field.returnPressed.connect(self.filter_table) self.filter_field.textChanged.connect(self.update_filter) tb.addWidget(self.filter_field) def move_down(self): if self.model.rowCount() < 1: return row = self.lb.selectionModel().selection().indexes()[0].row() if row > self.model.rowCount() - 2: return nextrow = row + 1 itemList = self.model.takeRow(row) self.model.insertRow(nextrow,itemList) self.isChanged = True self.lb.selectRow(nextrow) def move_up(self): if self.model.rowCount() < 1: return row = self.lb.selectionModel().selection().indexes()[0].row() if row < 1: return nextrow = row - 1 itemList = self.model.takeRow(row) self.model.insertRow(nextrow,itemList) self.isChanged = True self.lb.selectRow(nextrow) def del_row(self): if self.model.rowCount() < 1: return row = self.lb.selectionModel().selection().indexes()[0].row() self.model.takeRow(row) self.isChanged = True self.lb.selectRow(row) def add_row(self): if self.model.rowCount() < 1: return print("Zeile hinzufügen") newrow = ['name', 'group', 'url'] items = [QStandardItem(field) for field in newrow] self.model.appendRow(items) self.isChanged = True self.lb.selectRow(self.model.rowCount() - 1) def load_channels_file(self): if self.isChanged == True: save_msg = "<b>Das Dokument wurde geändert.<br>Wollen Sie die Änderungen speichern?</ b>" reply = QMessageBox.question(self, 'Speichern', save_msg, QMessageBox.Yes, QMessageBox.No) if reply == QMessageBox.Yes: self.writeCSV() self.open_channels() else: self.isChanged = False self.open_channels() else: self.isChanged = False self.open_channels() def open_channels(self, fileName): if fileName: self.convert_to_csv(fileName) print(fileName + " geladen") with open(self.csv_file, 'r') as f: i = 0 reader = csv.reader(f, delimiter = '\t') self.model.clear() for row in reader: items = [QStandardItem(field) for field in row] self.model.appendRow(items) self.model.setHeaderData(i - 1, Qt.Horizontal, str(i)) i = i + 1 self.lb.resizeColumnsToContents() self.lb.selectRow(0) self.statusBar().showMessage(f"{fileName} loaded", 0) self.isChanged = False self.lb.verticalHeader().setMinimumWidth(24) self.filename = fileName def convert_to_csv(self, fileName): #mychannels_file = 'myradio.txt' channels_list = open(fileName, 'r').read().splitlines() csv_content = "" group = "" name = "" url = "" for x in reversed(range(len(channels_list))): line = channels_list[x] if line == "": print(f"empty line {x} removed") del(channels_list[x]) i = 0 for x in range(0, len(channels_list)): line = channels_list[x] while True: if line.startswith("--"): group = line.replace("-- ", "").replace(" --", "") break continue elif not line.startswith("--"): ch_line = line.split(",") name = ch_line[0] url = ch_line[1] i += 1 break if not name == "" and not name == channels_list[x-1].partition(",")[0]: csv_content += (f'{name}\t{group}\t{url}\n') self.csv_file = '/tmp/mychannels.csv' with open(self.csv_file, 'w') as f: f.write(csv_content) def writeCSV(self): fileName, _ = QFileDialog.getSaveFileName(self, "Datei speichern", self.mychannels_file,"TVPlayer2 List (*.txt)") if fileName: self.save_file(fileName) def save_file(self, fileName): # save temporary csv f = open(self.csv_file, 'w') content = "" for row in range(self.lb.model().rowCount()): itemlist = [] for column in range(self.lb.model().columnCount()): itemlist.append(self.model.item(row, column).text()) content += '\t'.join(itemlist) content += '\n' with open(self.csv_file, 'w') as f: f.write(content) # convert to txt channels_list = open(self.csv_file, 'r').read().splitlines() group = "" name = "" url = "" out_list = [] for x in range(len(channels_list)): line = channels_list[x].split('\t') name = line[0] group = line[1] url = line[2] out_list.append(f"-- {group} --") out_list.append(f'{name},{url}') tlist = self.ordered_set(out_list) m3u_content = '\n'.join(tlist) m3u_content += '\n' with open(fileName, 'w') as f: f.write(m3u_content) print(fileName + " gespeichert") self.isChanged = False def ordered_set(self, in_list): out_list = [] added = set() for val in in_list: if not val in added: out_list.append(val) added.add(val) return out_list def replace_in_table(self): if self.model.rowCount() < 1: return searchterm = self.findfield.text() replaceterm = self.replacefield.text() if searchterm == "": return else: for i in range(self.lb.model().columnCount() - 1): indexes = self.lb.model().match( self.lb.model().index(0, i), Qt.DisplayRole, searchterm, -1, Qt.MatchContains ) for ix in indexes: old_item = self.model.item(ix.row(), i) new_item = old_item.text().replace(searchterm, replaceterm) self.model.setItem(ix.row(), i, QStandardItem(new_item)) self.lb.resizeColumnsToContents() self.isChanged = True def filter_table(self): if self.model.rowCount() < 1: return searchterm = self.filter_field.text() row_list = [] self.lb.clearSelection() for i in range(self.lb.model().columnCount()-1): indexes = self.lb.model().match( self.lb.model().index(0, i), Qt.DisplayRole, searchterm, -1, Qt.MatchContains ) for ix in indexes: self.lb.selectRow(ix.row()) row_list.append(ix.row()) for x in range(self.lb.model().rowCount()): if not x in row_list: self.lb.hideRow(x) def update_filter(self): if self.filter_field.text() == "": for x in range(self.lb.model().rowCount()): self.lb.showRow(x)
class Ui_MainWindow(QMainWindow): def __init__(self): super().__init__() self.select_checkbox_num = 0 self.all_widgets = [] self.game_sub_menus = [] self.face_sub_menus = [] self.setupUi(self) self.updateMenu(self) self.retranslateUi(self) def setupUi(self, MainWindow): MainWindow.setObjectName("MainWindow") MainWindow.resize(775, 532) self.menubar = QtWidgets.QMenuBar(MainWindow) self.menubar.setGeometry(QtCore.QRect(0, 0, 775, 23)) self.menubar.setObjectName("menubar") self.menu = QtWidgets.QMenu(self.menubar) self.menu.setObjectName("menu") self.menu_team = QtWidgets.QMenu(self.menubar) #参赛队组建菜单 self.menu_team.setObjectName("menu_team") self.menu_face = QtWidgets.QMenu(self.menubar) #对阵组建 self.menu_face.setObjectName("menu_face") MainWindow.setMenuBar(self.menubar) mgr_player = QAction(QIcon(), '运动员管理', self) mgr_player.triggered.connect( functools.partial(self.edit_player, *self.get_player_parmas())) mgr_playground = QAction(QIcon(), '场地管理', self) mgr_playground.triggered.connect( functools.partial(self.edit_player, *self.get_playground_parmas())) mgr_game = QAction(QIcon(), '竞赛项目', self) mgr_game.triggered.connect( functools.partial(self.edit_player, *self.get_game_parmas())) mgr_team = QAction(QIcon(), '参赛团队', self) mgr_team.triggered.connect(self.edit_team) mgr_group = QAction(QIcon(), '分组管理', self) mgr_group.triggered.connect(self.edit_group) mgr_test = QAction(QIcon(), '我的测试', self) mgr_test.triggered.connect(self.test) # mgr_face = QAction(QIcon(),'对阵设定',self) # mgr_face.triggered.connect(self.edit_face) self.toolbar = self.addToolBar('Mytool') self.toolbar.addAction(mgr_player) self.toolbar.addAction(mgr_playground) self.toolbar.addAction(mgr_game) self.toolbar.addAction(mgr_team) self.toolbar.addAction(mgr_group) self.toolbar.addAction(mgr_test) # self.toolbar.addAction(mgr_face) self.statusbar = QtWidgets.QStatusBar(MainWindow) self.statusbar.setObjectName("statusbar") MainWindow.setStatusBar(self.statusbar) self.action_init = QtWidgets.QAction(MainWindow) self.action_init.setObjectName("action_init") self.action_import_data = QtWidgets.QAction(MainWindow) self.action_import_data.setObjectName("action_import_data") self.action_gamemgr = QtWidgets.QAction(MainWindow) self.action_gamemgr.setObjectName("action_gamemgr") self.menu.addAction(self.action_init) self.menu.addAction(self.action_import_data) self.menu.addSeparator() self.menubar.addAction(self.menu.menuAction()) self.menubar.addAction(self.menu_team.menuAction()) self.menubar.addAction(self.menu_face.menuAction()) self.retranslateUi(MainWindow) QtCore.QMetaObject.connectSlotsByName(MainWindow) self.action_init.triggered.connect(self.clear_data_firm) self.action_import_data.triggered.connect(self.get_data_file) self.add_first_ui() # if has_data(): # self.add_team_ui() # time.sleep(2) # self.centralwidget.hide() # def pr(self): # print('prrrrr') def updateMenu(self, MainWindow): """添加或更新为每个竞赛项目组队的‘参赛队组建’菜单""" self.games = get_games() # 清除‘参赛队组建’原子菜单 for game_sub_menu in self.game_sub_menus: self.menu_team.removeAction(game_sub_menu) for game in self.games: self.game_sub_menus.append(QtWidgets.QAction(MainWindow)) for game_sub_menu, game in zip(self.game_sub_menus, self.games): game_sub_menu.setObjectName(game.name) game_sub_menu.triggered.connect( functools.partial(self.select_player, game.id, game.name, game.team_num, game.sex)) game_sub_menu.setText( QtCore.QCoreApplication.translate("MainWindow", "&{}".format(game.name))) for game_sub_menu in self.game_sub_menus: self.menu_team.addAction(game_sub_menu) """添加或更新为每个竞赛项目组队的‘对阵组建’菜单""" for face_sub_menu in self.face_sub_menus: self.menu_face.removeAction(face_sub_menu) for game in self.games: self.face_sub_menus.append(QtWidgets.QAction(MainWindow)) for game_sub_menu, game in zip(self.face_sub_menus, self.games): game_sub_menu.setObjectName(game.name + '_') game_sub_menu.triggered.connect( functools.partial(self.new_face, game.id)) game_sub_menu.setText( QtCore.QCoreApplication.translate("MainWindow", "&{}".format(game.name))) for game_sub_menu in self.face_sub_menus: self.menu_face.addAction(game_sub_menu) def add_first_ui(self, info="Welcome!"): self.takeCentralWidget() self.main_frame = QScrollArea(self) self.main_frame.setStyleSheet( 'QWidget{background-color:rgb(255,255,255)}') self.wel = QLabel(info) boxlayout = QHBoxLayout() boxlayout.addStretch() boxlayout.addWidget(self.wel) boxlayout.addStretch() self.main_frame.setLayout(boxlayout) self.setCentralWidget(self.main_frame) # def add_team_ui(self): # self.centralwidget = QWidget() # self.form_layout = QHBoxLayout(self.centralwidget) # self.left_layout = MyQVBoxLayout() # self.right_layout = QVBoxLayout() # # qbtn = QPushButton('1') # # qbtn.resize(qbtn.minimumSize()) # self.left_widgts = self.get_left_widgts() # # self.left_layout.addWidget(qbtn) # # self.left_layout.addWidget(QLabel('def')) # for widgt in self.left_widgts: # self.left_layout.addWidget(widgt) # self.left_layout.addStretch() # # self.right_layout.addWidget(QLabel('aaaa'),0,0) # # self.right_layout.addWidget(QLabel('bbbb'),1,0) # self.right_widgts = self.get_right_widgets() # for widgt in self.right_widgts: # self.right_layout.addWidget(widgt) # self.right_layout.addStretch() # self.form_layout.addLayout(self.left_layout) # self.form_layout.addLayout(self.right_layout) # self.setCentralWidget(self.centralwidget) # qbtn.clicked.connect(self.test) # def test(self): # centralwidget = QWidget() # test_layout = QHBoxLayout(centralwidget) # test_layout.addWidget(QLabel('kkkkkkkkkkkkkkkkkkk')) # # self.removeWidget(self.centralwidget) # self.takeCentralWidget() # self.setCentralWidget(centralwidget) # # self.show() # # self.update() # # self.repaint() # def test_tool(self): # print('abccc') # self.wel.setText('akkkkkkkkkkkk!') # self.takeCentralWidget() # self.setCentralWidget(QLabel('kkkdkddaaaaaaaaaaaaaa')) # main_form = QFormLayout() # left_widgt = QWidget(None) # left_widgt_layout = QVBoxLayout() # left_widgt.setLayout(left_widgt_layout) # for i in range(5): # left_widgt_layout.addWidget(QLabel('标签%i' % i)) # for w in self.get_left_widgts(): # left_widgt_layout.addWidget(w) # scroll_area = QScrollArea() # right_widgt = QWidget(None) # self.right_widgt_layout = QVBoxLayout() # right_widgt.setLayout(self.right_widgt_layout) # self.right_widgt_layout.addWidget(QLabel('标签%i' % 5)) # self.right_widgt_layout.addWidget(QLabel('标签%i' % 7)) # scroll_area.setWidget(right_widgt) # main_form.addRow(left_widgt,scroll_area) # main_widgt = QWidget(None) # main_widgt.setLayout(main_form) # self.setCentralWidget(main_widgt) # def get_left_widgts(self): # widgts = [] # self.game_data = get_games() # mylbl = QLabel('运动项目:') # mylbl.setMaximumHeight(20) # widgts.append(mylbl) # self.game_combo = QComboBox(None) # self.game_combo.setMaximumWidth(80) # self.game_combo.setMaximumHeight(20) # for item in self.game_data.keys(): # self.game_combo.addItem(item) # widgts.append(self.game_combo) # self.game_combo.setCurrentIndex(-1) # self.game_combo.activated[str].connect(self.set_cur_game) # return widgts # def set_cur_game(self,game): # self.game = game # print(game,self.game_data[game]) # if self.game_data[game] == 1: # self.show_players(game) # def get_right_widgets(self): # players = get_players() # widgts = [] # for p in players: # data = [p.name,p.idcode,p.sex,p.age,p.work_place,p.tel] # data = ['' if d is None else d for d in data] # info = '{: <8} {: <8} {: <2} {: <2} {: <10} {: <12}'.format(*data) # widgts.append(QLabel(info)) # return widgts # def show_players(self,game): # self.test() # game_sex = get_games_sex() # sex = game_sex[game] # self.players = get_players(sex) # self.p_checkboxes = [QCheckBox(' '.join((p.name,p.idcode))) for p in self.players] # for w in self.right_widgts: # w.hide() # for cb in self.p_checkboxes: # cb.toggle() # print('abc') # self.right_layout.addWidget(cb) # self.right_layout.addStretch() def retranslateUi(self, MainWindow): _translate = QtCore.QCoreApplication.translate MainWindow.setWindowTitle(_translate("MainWindow", "易用竞赛管理系统")) self.menu.setTitle(_translate("MainWindow", "&管理数据")) self.action_init.setText(_translate("MainWindow", "&清理数据")) self.action_import_data.setText(_translate("MainWindow", "&导入数据")) self.menu_team.setTitle(_translate("MainWindow", "&参赛队组建")) self.menu_face.setTitle(_translate("MainWindow", "&对阵组建")) def get_data_file(self): fname = QFileDialog.getOpenFileName(self, '打开文件', '.\\') if fname[0]: info = load_data(fname[0]) if info: QMessageBox.information(self, "数据错误,请修改后重新导入!", info) else: QMessageBox.information(self, "提示:", '数据导入成功!') self.add_first_ui('数据已导入!') # self.add_team_ui() self.updateMenu(self) def clear_data_firm(self): reply = QMessageBox.question(self, '确认', '确定删除数据?', QMessageBox.Yes | QMessageBox.No, QMessageBox.No) self.takeCentralWidget() if reply == QMessageBox.Yes: clear_data() self.add_first_ui('数据已全部清空!') def get_player_parmas(self): head_lst = ['索引号', '姓名', '身份证号', '性别', '年龄', '工作单位', '电话'] keys = ['id', 'name', 'idcode', 'sex', 'age', 'work_place', 'tel'] return get_players, keys, head_lst, Player def get_playground_parmas(self): head_lst = ['索引号', '场地名', '使用状态', '备注'] keys = ['id', 'name', 'using', 'memo'] return get_playgrounds, keys, head_lst, PlayGround def get_game_parmas(self): head_lst = ['索引号', '竞赛项目', '队员人数', '队员性别', '备注'] keys = ['id', 'name', 'team_num', 'sex', 'memo'] return get_games, keys, head_lst, Games def edit_player(self, getfunc, keys, head_lst, obj): model_objs = getfunc() datas = [] for model_obj in model_objs: # data = [p.id,p.name,p.idcode,p.sex,p.age,p.work_place,p.tel] data = [getattr(model_obj, key) for key in keys] data = ['' if d is None else d for d in data] datas.append(data) # head_lst = ['索引号','姓名','身份证号','性别','年龄','工作单位','电话'] if datas: self.takeCentralWidget() main_frame = QScrollArea(self) main_frame.setStyleSheet( 'QWidget{background-color:rgb(255,255,255)}') self.player_tabview = QTableView() r, c = len(datas), len(datas[0]) self.player_model = QStandardItemModel(r, c) self.player_model.setHorizontalHeaderLabels(head_lst) for r, rdata in enumerate(datas): for c, cell in enumerate(rdata): it = QStandardItem(str(cell)) if c == 0: it.setEditable(False) self.player_model.setItem(r, c, it) # keys = ['id','name','idcode','sex','age','work_place','tel'] edit_cell = functools.partial(self.edit_cell, obj, keys) self.player_model.itemChanged.connect(edit_cell) self.player_tabview.setModel(self.player_model) boxlayout = QVBoxLayout() # boxlayout.addStretch(1) boxlayout.addWidget(self.player_tabview, 18) # boxlayout.addStretch(1) del_btn = QPushButton('删除') del_btn.clicked.connect(functools.partial(self.del_row, obj)) boxlayout.addWidget(del_btn) main_frame.setLayout(boxlayout) self.setCentralWidget(main_frame) def edit_cell(self, obj, keys): r = self.player_tabview.currentIndex().row() c = self.player_tabview.currentIndex().column() curr_data = self.player_tabview.currentIndex().data() item = self.player_model.index(r, 0) param = dict() param[keys[c]] = curr_data save_cell(obj, int(item.data()), param) if obj == Games and 'name' in param: self.updateMenu(self) def del_row(self, obj): reply = QMessageBox.question(self, '确认', '确定删除数据?', QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.Yes: r = self.player_tabview.currentIndex().row() item = self.player_model.index(r, 0) # print(int(item.data())) del_rowdb(obj, int(item.data())) self.player_model.removeRow(r) if obj == Games: self.updateMenu(self) def edit_team(self): datas = get_team_datas() head_lst = ['索引号', '队名', '项目', '分组'] if datas: self.takeCentralWidget() main_frame = QScrollArea(self) main_frame.setStyleSheet( 'QWidget{background-color:rgb(255,255,255)}') self.player_tabview = QTableView() r, c = len(datas), len(datas[0]) self.player_model = QStandardItemModel(r, c) self.player_model.setHorizontalHeaderLabels(head_lst) for r, rdata in enumerate(datas): for c, cell in enumerate(rdata): it = QStandardItem(str(cell)) if c == 0: it.setEditable(False) self.player_model.setItem(r, c, it) # keys = ['id','name','idcode','sex','age','work_place','tel'] # edit_cell = functools.partial(self.edit_cell,obj,keys) # self.player_model.itemChanged.connect(edit_cell) self.player_tabview.setModel(self.player_model) boxlayout = QVBoxLayout() # boxlayout.addStretch(1) boxlayout.addWidget(self.player_tabview, 18) # boxlayout.addStretch(1) del_btn = QPushButton('删除') del_btn.clicked.connect(self.del_team) boxlayout.addWidget(del_btn) main_frame.setLayout(boxlayout) self.setCentralWidget(main_frame) def del_team(self): # v = MyDialog() # if v.exec_(): # name,game = v.get_data() # print(name,game) reply = QMessageBox.question(self, '确认', '确定删除数据?', QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.Yes: r = self.player_tabview.currentIndex().row() item = self.player_model.index(r, 0) # print(int(item.data())) del_rowdb(Team, int(item.data())) self.player_model.removeRow(r) def select_player(self, gid, gname, gteam_num, gsex): # 新建团队UI players = get_players(gsex) head_lst = ['索引号', '姓名', '身份证号', '性别', '年龄', '工作单位', '电话'] keys = ['id', 'name', 'idcode', 'sex', 'age', 'work_place', 'tel'] datas = [] for player in players: data = [getattr(player, key) for key in keys] data = ['' if d is None else d for d in data] datas.append(data) if datas: self.takeCentralWidget() main_frame = QScrollArea(self) main_frame.setStyleSheet( 'QWidget{background-color:rgb(255,255,255)}') self.player_tabview = QTableView() r, c = len(datas), len(datas[0]) self.player_model = QStandardItemModel(r, c) print(self.player_tabview.itemDelegate()) self.player_model.setHorizontalHeaderLabels(head_lst) for r, rdata in enumerate(datas): for c, cell in enumerate(rdata): it = QStandardItem(str(cell)) if c == 0: it.setEditable(False) self.player_model.setItem(r, c, it) self.player_tabview.setModel(self.player_model) boxlayout = QVBoxLayout() # boxlayout.addStretch(1) boxlayout.addWidget(self.player_tabview, 18) # boxlayout.addStretch(1) new_btn = QPushButton('新建团队({})'.format(gname)) new_btn.clicked.connect( functools.partial(self.add_team, gid, gteam_num, gsex)) boxlayout.addWidget(new_btn) main_frame.setLayout(boxlayout) self.setCentralWidget(main_frame) def add_team(self, gid, gteam_num, gsex): # 新建团队方法 rows = set() pids = [] for selected_model_index in self.player_tabview.selectedIndexes(): rows.add(selected_model_index.row()) for r in rows: item = self.player_model.index(r, 0) pids.append(item.data()) if gteam_num == 1: info = new_team(gid, pids, flag=1) if info: QMessageBox.warning(self, '完成', info, QMessageBox.Ok) else: QMessageBox.information(self, '完成', '成功建立!', QMessageBox.Ok) if gteam_num > 1: if len(rows) == gteam_num: info = new_team(gid, pids) if info: QMessageBox.warning(self, '完成', info, QMessageBox.Ok) else: QMessageBox.information(self, '完成', '成功建立!', QMessageBox.Ok) else: QMessageBox.warning(self, '错误', '请选中指定的运动员数:{}'.format(gteam_num), QMessageBox.Ok) self.player_tabview.clearSelection() def test(self): from .modeltt import TObjModel, MyDelegate md = MyDelegate() self.takeCentralWidget() main_frame = QScrollArea(self) main_frame.setStyleSheet('QWidget{background-color:rgb(255,255,255)}') self.player_tabview = QTableView() self.player_model = TObjModel() self.player_tabview.setModel(self.player_model) boxlayout = QVBoxLayout() # boxlayout.addStretch(1) boxlayout.addWidget(self.player_tabview, 18) self.player_tabview.setItemDelegateForColumn(2, md) self.player_tabview.hideColumn(0) main_frame.setLayout(boxlayout) self.setCentralWidget(main_frame) def edit_group(self): datas = get_group_datas() head_lst = ['索引号', '组名', '项目', '所含队名', 'gameid'] self.takeCentralWidget() main_frame = QScrollArea(self) main_frame.setStyleSheet('QWidget{background-color:rgb(255,255,255)}') self.player_tabview = QTableView() self.player_model = QStandardItemModel() self.player_model.setHorizontalHeaderLabels(head_lst) if datas: r, c = len(datas), len(datas[0]) # self.player_model = QStandardItemModel(r,c) self.player_model.setHorizontalHeaderLabels(head_lst) for r, rdata in enumerate(datas): for c, cell in enumerate(rdata): it = QStandardItem(str(cell)) # if c == 0: it.setEditable(False) self.player_model.setItem(r, c, it) # keys = ['id','name','idcode','sex','age','work_place','tel'] # edit_cell = functools.partial(self.edit_cell,obj,keys) # self.player_model.itemChanged.connect(edit_cell) self.player_tabview.setModel(self.player_model) boxlayout = QVBoxLayout() # boxlayout.addStretch(1) boxlayout.addWidget(self.player_tabview, 18) # boxlayout.addStretch(1) self.player_tabview.hideColumn(4) add_btn = QPushButton('添加分组') add_btn.clicked.connect(self.add_group) boxlayout.addWidget(add_btn) del_btn = QPushButton('删除分组') del_btn.clicked.connect(functools.partial(self.del_row, Group)) boxlayout.addWidget(del_btn) add_team_btn = QPushButton('分配参赛团队') add_team_btn.clicked.connect(self.add_team2group) boxlayout.addWidget(add_team_btn) main_frame.setLayout(boxlayout) self.setCentralWidget(main_frame) def add_group(self): v = MyDialog() if v.exec_(): name, game = v.get_data() if name and game: info = add_groupdb(name, int(game)) if info: QMessageBox.warning(self, '错误', info, QMessageBox.Ok) else: QMessageBox.information(self, '完成', '成功建立!', QMessageBox.Ok) self.edit_group() def add_team2group(self): rows = set() for selected_model_index in self.player_tabview.selectedIndexes(): rows.add(selected_model_index.row()) if len(rows) != 1: QMessageBox.warning(self, '错误', '请仅选择其中一个小组', QMessageBox.Ok) return row = rows.pop() groupid = self.player_model.index(row, 0).data() gameid = self.player_model.index(row, 4).data() print(groupid, gameid) if get_team_datas(gameid): v = GroupDialog(gameid, "添加团队到小组", get_team_datas) if v.exec_(): tids = v.get_data() print(tids) if tids: add_team2group_db(groupid, tids) QMessageBox.information(self, '提示', '操作完成!', QMessageBox.Ok) else: QMessageBox.information(self, '提示', '无参赛团队可以分组!', QMessageBox.Ok) def get_grp_combo(self, gid): groups = get_group_for_game(gid) cur_index = -1 grp_combo = QComboBox() for index, (ggid, gname, ggname) in enumerate(groups): grp_combo.addItem('-'.join((ggname, gname)), ggid) grp_combo.setCurrentIndex(cur_index) grp_combo.currentIndexChanged.connect( functools.partial(self.disp_face, gid)) grp_combo.setToolTip('请选择一个分组,并为其建立对阵。') return grp_combo def new_face(self, gid): # print(gid) self.takeCentralWidget() main_frame = QScrollArea(self) main_frame.setStyleSheet('QWidget{background-color:rgb(255,255,255)}') boxlayout = QVBoxLayout() self.grp_combo = self.get_grp_combo(gid) boxlayout.addWidget(self.grp_combo) # boxlayout.addStretch(1) self.face_view = QTableView() self.face_model = QStandardItemModel() self.face_model.setHorizontalHeaderLabels(['id', '队A', '队B']) self.face_view.setModel(self.face_model) boxlayout.addWidget(self.face_view) add_btn = QPushButton('添加对阵') add_btn.clicked.connect(functools.partial(self.add_face, gid)) boxlayout.addWidget(add_btn) main_frame.setLayout(boxlayout) self.setCentralWidget(main_frame) def disp_face(self, gid): # print(self.grp_combo.currentIndex(),self.grp_combo.currentText()) self.ggid = int(self.grp_combo.itemData(self.grp_combo.currentIndex())) if self.ggid >= 1: self.face_model.beginResetModel() self.face_model.clear() self.face_model.setHorizontalHeaderLabels(['id', '队A', '队B']) faces = get_faces(gid, self.ggid) for r, rd in enumerate(faces): for c, cd in enumerate(rd): item = QStandardItem(str(cd)) item.setEditable(False) self.face_model.setItem(r, c, item) self.face_model.endResetModel() def add_face(self, gid): v = GroupDialog(self.ggid, "添加对阵团队", get_teams_for_group) if v.exec_(): tids = v.get_data() print(tids) tids = [int(tid) for tid in tids] if len(tids) != 2: QMessageBox.warning(self, '错误', '请仅选择对阵的双方小组', QMessageBox.Ok) else: tids.sort() if add_face2db(*tids): self.disp_face(gid) QMessageBox.information(self, '提示', '操作完成!', QMessageBox.Ok) else: QMessageBox.warning(self, '错误', '对阵不能重复!', QMessageBox.Ok)