class RequestsOperations(QWidget): def __init__(self, parent): super().__init__(parent) self.initUI() self.__ops = [] def initUI(self): self.layout = QFormLayout() self.layout.setFieldGrowthPolicy(QFormLayout.ExpandingFieldsGrow) self.layout.setLabelAlignment(Qt.AlignLeft) self.layout.setFormAlignment(Qt.AlignLeft | Qt.AlignTop) twoListsOfSets = QWidget() twoListsOfSets.setLayout(QHBoxLayout()) twoListsOfSets.layout().setContentsMargins(5, 10, 5, 5) twoListsOfSets.layout().setSpacing(0) effect = QGraphicsDropShadowEffect() effect.setBlurRadius(10) effect.setColor(QColor(0, 0, 0, 160)) effect.setOffset(0.0) self.requestList = QListView() self.requestList.setSpacing(3) self.requestList.setAutoFillBackground(True) self.requestList.setGraphicsEffect(effect) self.requestList.setFrameStyle(QFrame.NoFrame) self.requestList.viewport().setAutoFillBackground(False) self.requestList.setFlow(QListView.LeftToRight) self.requestList.setWrapping(True) self.requestList.setResizeMode(QListView.Adjust) self.requestList.setUniformItemSizes(True) self.requestsModel = QStandardItemModel() self.requestList.setModel(self.requestsModel) effect = QGraphicsDropShadowEffect() effect.setBlurRadius(10) effect.setColor(QColor(0, 0, 0, 160)) effect.setOffset(0.0) self.requestList2 = QListView() self.requestList2.setSpacing(3) self.requestList2.setAutoFillBackground(True) self.requestList2.setGraphicsEffect(effect) self.requestList2.setFrameStyle(QFrame.NoFrame) self.requestList2.viewport().setAutoFillBackground(False) self.requestList2.setFlow(QListView.LeftToRight) self.requestList2.setWrapping(True) self.requestList2.setResizeMode(QListView.Adjust) self.requestList2.setUniformItemSizes(True) self.requestsModel2 = QStandardItemModel() self.requestList2.setModel(self.requestsModel2) twoListsOfSets.layout().addWidget(self.requestList) twoListsOfSets.layout().addWidget(self.requestList2) self.layout.addRow("SETS", twoListsOfSets) self.layout.addRow(HorizontalLine(self)) self.operationSelection = QGroupBox() self.operationSelection.setFlat(True) self.operationSelection.setLayout(QVBoxLayout()) self.buttonIntersection = QRadioButton("Intersection") self.operationSelection.layout().addWidget(self.buttonIntersection) self.buttonIntersection.clicked.connect( self.__disableSecondRequestList) self.buttonIntersection.click() self.buttonUnion = QRadioButton("Union") self.operationSelection.layout().addWidget(self.buttonUnion) self.buttonUnion.clicked.connect(self.__disableSecondRequestList) self.buttonDiff = QRadioButton("Difference") self.operationSelection.layout().addWidget(self.buttonDiff) self.buttonDiff.clicked.connect(self.__enableSecondRequestList) self.layout.addRow("OPERATION", self.operationSelection) self.buttonApplyWidget = QWidget() self.buttonApplyWidget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) self.buttonApplyLayout = QHBoxLayout() self.buttonApplyLayout.setContentsMargins(0, 0, 0, 0) self.buttonApplyWidget.setLayout(self.buttonApplyLayout) self.buttonApply = QPushButton("Apply") self.buttonApply.clicked.connect(self.__applyOp) self.operationSelection.layout().addWidget(self.buttonApply) self.buttonApplyLayout.addWidget(self.buttonApply, alignment=Qt.AlignRight) self.layout.addRow("", self.buttonApplyWidget) self.layout.addRow(HorizontalLine(self)) self.layout.addRow("RESULTS", None) self.resultingSets = QTableView() self.resultingSets.horizontalHeader().setSectionResizeMode( QHeaderView.Stretch) self.resultingSets.verticalHeader().setSectionResizeMode( QHeaderView.ResizeToContents) self.resultingSets.setModel(OperationsTableModel()) self.layout.addRow(self.resultingSets) self.layout.addRow(HorizontalLine(self)) self.outputSetSelection = QComboBox() self.outputSetSelection.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Fixed) self.layout.addRow("OUTPUT SET", self.outputSetSelection) self.setLayout(self.layout) def outputSet(self): return self.outputSetSelection.currentText() def setOutputSet(self, outputSetName): self.outputSetSelection.setCurrentText(outputSetName) @property def ops(self): return copy.deepcopy(self.__ops) def __applyOp(self): includedSets = [ self.requestsModel.item(i).text() for i in range(self.requestsModel.rowCount()) if self.requestsModel.item(i).data(Qt.CheckStateRole) == QVariant( Qt.Checked) ] if self.buttonUnion.isChecked(): if len(includedSets) > 1: opName = SetNameManagement.getUniqueSetName() self.addOp(OverpassUnion(opName), includedSets) logging.info("Union created.") else: logging.error("The union must have at least two sets.") elif self.buttonIntersection.isChecked(): if len(includedSets) > 1: opName = SetNameManagement.getUniqueSetName() self.addOp(OverpassIntersection(opName), includedSets) logging.info("Intersection created.") else: logging.error("The intersection must have at least two sets.") elif self.buttonDiff.isChecked(): excludedSets = [ self.requestsModel2.item(i).text() for i in range(self.requestsModel2.rowCount()) if self.requestsModel2.item(i).data(Qt.CheckStateRole) == QVariant(Qt.Checked) ] if len(includedSets) == 1 and len(excludedSets) > 0: opName = SetNameManagement.getUniqueSetName() self.addOp(OverpassDiff(includedSets[0], opName), excludedSets) logging.info("Difference created.") else: logging.error( "The difference must have only one set selected in the first list and at least one in the other." ) logging.debug("LINE") def addOp(self, op, sets=None): SetNameManagement.assign(op.name) self.__ops.append(op) if sets is not None: op.addSets(sets) self.resultingSets.model().addOp(op.name, op) self.addRequest(op.name) self.cleanRequestList() def __enableSecondRequestList(self): self.requestList2.show() def __disableSecondRequestList(self): self.requestList2.hide() def addRequest(self, name): self.requestsModel.beginInsertRows(QModelIndex(), self.requestsModel.rowCount(), self.requestsModel.rowCount()) item = QStandardItem(name) item.setFlags(Qt.ItemIsUserCheckable | Qt.ItemIsEnabled) item.setData(QVariant(Qt.Unchecked), Qt.CheckStateRole) self.requestsModel.appendRow(item) self.requestsModel.endInsertRows() self.requestsModel2.beginInsertRows(QModelIndex(), self.requestsModel2.rowCount(), self.requestsModel2.rowCount()) item = QStandardItem(name) item.setFlags(Qt.ItemIsUserCheckable | Qt.ItemIsEnabled) item.setData(QVariant(Qt.Unchecked), Qt.CheckStateRole) self.requestsModel2.appendRow(item) self.requestsModel2.endInsertRows() self.outputSetSelection.addItem(name) def removeSetAndDependencies(self, setName): removeList = [setName] for set in removeList: logging.info("Removing set '{}'.".format(set)) removeList.extend( [i for i in self.__removeSet(set) if i not in removeList]) logging.debug("LINE") def __removeSet(self, setName): dependencies = [] for op in self.__ops: op.removeSet(setName) if not op.isValid(): dependencies.append(op.name) for i in range(self.requestsModel.rowCount()): if self.requestsModel.item(i).text() == setName: self.requestsModel.beginRemoveRows(QModelIndex(), i, i) self.requestsModel.removeRow(i) self.requestsModel.endInsertRows() self.requestsModel2.beginRemoveRows(QModelIndex(), i, i) self.requestsModel2.removeRow(i) self.requestsModel2.endInsertRows() self.outputSetSelection.removeItem(i) break for op in self.__ops: if op.name == setName: self.resultingSets.model().removeOp(setName) self.__ops.remove(op) break SetNameManagement.releaseName(setName) return dependencies def reset(self): while len(self.ops) > 0: self.removeSetAndDependencies(self.ops[0].name) def cleanRequestList(self): for i in range(self.requestsModel.rowCount()): self.requestsModel.item(i).setData(QVariant(Qt.Unchecked), Qt.CheckStateRole) self.requestsModel2.item(i).setData(QVariant(Qt.Unchecked), Qt.CheckStateRole) def keyPressEvent(self, event): if event.key() == Qt.Key_Backspace and self.resultingSets.hasFocus(): advice = "Are you sure?\nAll sets containing this one will be deleted if they are no longer valid" reply = QMessageBox.question(self, "Remove request operation", advice) if reply == QMessageBox.Yes: select = self.resultingSets.selectionModel() while len(select.selectedRows()) > 0: self.removeSetAndDependencies( self.resultingSets.model().getOpByIndex( select.selectedRows()[0].row())) event.accept()
class MainForm(QDialog): def __init__(self, parent=None): super(MainForm, self).__init__(parent) self.model = ships.ShipTableModel("ships.dat") tableLabel1 = QLabel("Table &1") self.tableView1 = QTableView() tableLabel1.setBuddy(self.tableView1) self.tableView1.setModel(self.model) self.tableView1.setItemDelegate(ships.ShipDelegate(self)) # 使用自己定义的委托 tableLabel2 = QLabel("Table &2") self.tableView2 = QTableView() tableLabel2.setBuddy(self.tableView2) self.tableView2.setModel(self.model) self.tableView2.setItemDelegate(ships.ShipDelegate(self)) addShipButton = QPushButton("&Add Ship") removeShipButton = QPushButton("&Remove Ship") exportButton = QPushButton("E&xport...") quitButton = QPushButton("&Quit") if not MAC: addShipButton.setFocusPolicy(Qt.NoFocus) removeShipButton.setFocusPolicy(Qt.NoFocus) exportButton.setFocusPolicy(Qt.NoFocus) quitButton.setFocusPolicy(Qt.NoFocus) buttonLayout = QHBoxLayout() buttonLayout.addWidget(addShipButton) buttonLayout.addWidget(removeShipButton) buttonLayout.addWidget(exportButton) buttonLayout.addStretch() buttonLayout.addWidget(quitButton) splitter = QSplitter(Qt.Horizontal) vbox = QVBoxLayout() vbox.addWidget(tableLabel1) vbox.addWidget(self.tableView1) widget = QWidget() widget.setLayout(vbox) splitter.addWidget(widget) vbox = QVBoxLayout() vbox.addWidget(tableLabel2) vbox.addWidget(self.tableView2) widget = QWidget() widget.setLayout(vbox) splitter.addWidget(widget) layout = QVBoxLayout() layout.addWidget(splitter) layout.addLayout(buttonLayout) self.setLayout(layout) for tableView in (self.tableView1, self.tableView2): header = tableView.horizontalHeader() header.sectionClicked[int].connect(self.sortTable) addShipButton.clicked.connect(self.addShip) removeShipButton.clicked.connect(self.removeShip) exportButton.clicked.connect(self.export) quitButton.clicked.connect(self.accept) self.setWindowTitle("Ships (delegate)") QTimer.singleShot(0, self.initialLoad) def initialLoad(self): if not QFile.exists(self.model.filename): self.model.beginResetModel() for ship in ships.generateFakeShips(): self.model.ships.append(ship) self.model.owners.add(str(ship.owner)) self.model.countries.add(str(ship.country)) self.model.endResetModel() self.model.dirty = False else: try: self.model.load() except IOError as e: QMessageBox.warning(self, "Ships - Error", "Failed to load: {0}".format(e)) self.model.sortByName() self.resizeColumns() def resizeColumns(self): self.tableView1.resizeColumnsToContents() self.tableView2.resizeColumnsToContents() def reject(self): self.accept() def accept(self): if (self.model.dirty and QMessageBox.question( self, "Ships - Save?", "Save unsaved changes?", QMessageBox.Yes | QMessageBox.No) == QMessageBox.Yes): try: self.model.save() except IOError as e: QMessageBox.warning(self, "Ships - Error", "Failed to save: {0}".format(e)) QDialog.accept(self) def sortTable(self, section): if section in (ships.OWNER, ships.COUNTRY): self.model.sortByCountryOwner() elif section == ships.TEU: self.model.sortByTEU() else: self.model.sortByName() self.resizeColumns() def addShip(self): row = self.model.rowCount() self.model.insertRows(row) index = self.model.index(row, 0) tableView = self.tableView1 if self.tableView2.hasFocus(): tableView = self.tableView2 tableView.setFocus() tableView.setCurrentIndex(index) tableView.edit(index) def removeShip(self): tableView = self.tableView1 if self.tableView2.hasFocus(): tableView = self.tableView2 index = tableView.currentIndex() if not index.isValid(): return row = index.row() name = self.model.data(self.model.index(row, ships.NAME)) owner = self.model.data(self.model.index(row, ships.OWNER)) country = self.model.data(self.model.index(row, ships.COUNTRY)) if (QMessageBox.question( self, "Ships - Remove", "Remove {0} of {1}/{2}?".format(name, owner, country), QMessageBox.Yes | QMessageBox.No) == QMessageBox.No): return self.model.removeRows(row) self.resizeColumns() def export(self): filename = str( QFileDialog.getSaveFileName(self, "Ships - Choose Export File", ".", "Export files (*.txt)")[0]) if not filename: return #htmlTags = QRegExp(r"<[^>]+>") htmlTags = "<[^>]+>" #htmlTags.setMinimal(True) nonDigits = "[., ]" self.model.sortByCountryOwner() fh = None try: fh = QFile(filename) if not fh.open(QIODevice.WriteOnly): raise IOError(str(fh.errorString())) stream = QTextStream(fh) stream.setCodec("UTF-8") for row in range(self.model.rowCount()): name = self.model.data(self.model.index(row, ships.NAME)) owner = self.model.data(self.model.index(row, ships.OWNER)) country = self.model.data(self.model.index(row, ships.COUNTRY)) teu = self.model.data(self.model.index(row, ships.TEU)) teu = re.sub(nonDigits, "", teu) description = self.model.data( (self.model.index(row, ships.DESCRIPTION))) description = re.sub(htmlTags, "", description) stream << name << "|" << owner << "|" << country \ << "|" << str(teu) << "|" << description << "\n" except EnvironmentError as e: QMessageBox.warning(self, "Ships - Error", "Failed to export: {0}".format(e)) finally: if fh: fh.close() QMessageBox.warning( self, "Ships - Export", "Successfully exported ship to {0}".format(filename))
class ProwlerTableWidget(ProwlerSqlWidget): """Base class for all table-based SQL widgets. Takes care of selection management and context menu.""" # Notifier for selection changes in the main table. Use the selected_ids property to access the selection. selection_changed = pyqtSignal() double_clicked = pyqtSignal() def __init__(self, parent=None): super(ProwlerTableWidget, self).__init__(parent=parent) self.context_menu_actions = [] # Set up the model and table sort_model = QSortFilterProxyModel(self) sort_model.setSourceModel(self.model) self.table = QTableView(self) self.table.setModel(sort_model) self.table.setSortingEnabled(True) # Extended row selection by default self.table.setSelectionBehavior(QAbstractItemView.SelectRows) self.table.setSelectionMode(QAbstractItemView.ExtendedSelection) self.table.selectionModel().selectionChanged.connect( self.selection_changed) # Editing disabled by default self.table.setEditTriggers(QAbstractItemView.NoEditTriggers) # Enable custom context menu self.table.setContextMenuPolicy(Qt.CustomContextMenu) self.table.customContextMenuRequested.connect(self.context_menu) # Set default header behaviour self.table.horizontalHeader().setSectionResizeMode( QHeaderView.ResizeToContents) # Add the table to a layout, add the layout to the widget layout = QVBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) layout.addWidget(self.table) self.setLayout(layout) # Connect the double_clicked signal self.table.doubleClicked.connect(self.double_clicked) @property def selected_ids(self): """Return the values in the 'id' column for each selected row.""" id_idx_list = self.table.selectionModel().selectedRows( self.model.fieldIndex('id')) return [idx.data() for idx in id_idx_list] def get_selected_id(self): """Return the last id in self.selected_ids, or None.""" return self.selected_ids[-1] if self.selected_ids else None def context_menu(self, point): """Generate a popup menu from the actions in self.context_menu_actions.""" menu = QMenu(self) menu.addActions(self.context_menu_actions) point = self.table.viewport().mapToGlobal(point) menu.popup(point) def add_context_action(self, action): """Add an action to the context menu.""" self.context_menu_actions.append(action) def add_context_actions(self, actions): """Adds all actions in an iterable.""" self.context_menu_actions.extend(actions) def remove_context_action(self, action): """Remove an action from the context menu.""" self.context_menu_actions.remove(action) def hasFocus(self): return self.table.hasFocus()
class MainForm(QDialog): def __init__(self, parent=None): super(MainForm, self).__init__(parent) self.model = ships.ShipTableModel("ships.dat") #创建一个shipmodel tableLabel1 = QLabel("Table &1") self.tableView1 = QTableView() tableLabel1.setBuddy(self.tableView1) self.tableView1.setModel(self.model) tableLabel2 = QLabel("Table &2") self.tableView2 = QTableView() tableLabel2.setBuddy(self.tableView2) self.tableView2.setModel(self.model) addShipButton = QPushButton("&Add Ship") removeShipButton = QPushButton("&Remove Ship") quitButton = QPushButton("&Quit") if not MAC: addShipButton.setFocusPolicy( Qt.NoFocus) #设置按钮接受键盘焦点方式,NoFocus不接受焦点 removeShipButton.setFocusPolicy(Qt.NoFocus) quitButton.setFocusPolicy(Qt.NoFocus) buttonLayout = QHBoxLayout() buttonLayout.addWidget(addShipButton) buttonLayout.addWidget(removeShipButton) buttonLayout.addStretch() buttonLayout.addWidget(quitButton) splitter = QSplitter(Qt.Horizontal) vbox = QVBoxLayout() vbox.addWidget(tableLabel1) vbox.addWidget(self.tableView1) widget = QWidget() widget.setLayout(vbox) splitter.addWidget(widget) vbox = QVBoxLayout() vbox.addWidget(tableLabel2) vbox.addWidget(self.tableView2) widget = QWidget() widget.setLayout(vbox) splitter.addWidget(widget) layout = QVBoxLayout() layout.addWidget(splitter) layout.addLayout(buttonLayout) self.setLayout(layout) # 为每个水平标题连接到sortTable方法上 for tableView in (self.tableView1, self.tableView2): header = tableView.horizontalHeader() # 返回水平标题,QHeaderView header.sectionClicked[int].connect( self.sortTable ) # 点击标题绑定信号,点击某个部分时会发出此信号。该部分的逻辑索引由logicalIndex指定 addShipButton.clicked.connect(self.addShip) removeShipButton.clicked.connect(self.removeShip) quitButton.clicked.connect(self.accept) self.setWindowTitle("Ships (model)") QTimer.singleShot(0, self.initialLoad) # 设置定时器,启动时间0,调用initialLoad方法初始化数据 def initialLoad(self): if not QFile.exists(self.model.filename): self.model.beginResetModel() for ship in ships.generateFakeShips( ): # generateFakeShips构造Ship对象列表 self.model.ships.append(ship) self.model.owners.add(str(ship.owner)) self.model.countries.add(str(ship.country)) self.model.endResetModel() self.model.dirty = False else: try: self.model.load() except IOError as e: QMessageBox.warning(self, "Ships - Error", "Failed to load: {0}".format(e)) self.model.sortByName() self.resizeColumns() # 对两个表格视图NAME,OWNER,COUNTRY,TEU列,按照内容改变列大小 def resizeColumns(self): for tableView in (self.tableView1, self.tableView2): for column in (ships.NAME, ships.OWNER, ships.COUNTRY, ships.TEU): tableView.resizeColumnToContents(column) # QDialog.reject() 隐藏模式对话框并将result code 设置为Rejected 拒绝 def reject(self): print() self.accept() # QDialog.accept() 隐藏模式对话框并将result code 设置为Accepted 接受 def accept(self): if (self.model.dirty and QMessageBox.question( self, "Ships - Save?", "Save unsaved changes?", QMessageBox.Yes | QMessageBox.No) == QMessageBox.Yes): try: self.model.save() except IOError as e: QMessageBox.warning(self, "Ships - Error", "Failed to save: {0}".format(e)) QDialog.accept(self) #table视图排序方法 def sortTable(self, section): if section in (ships.OWNER, ships.COUNTRY): self.model.sortByCountryOwner() else: self.model.sortByName() self.resizeColumns() # 槽方法,添加一个船 def addShip(self): row = self.model.rowCount() # 获取模型数据条数 self.model.insertRows(row) # 在最后插入一条 index = self.model.index(row, 0) # 获得插入的index # 判断键盘焦点在哪个表格视图上 tableView = self.tableView1 if self.tableView2.hasFocus(): tableView = self.tableView2 tableView.setFocus() # 设置焦点 tableView.setCurrentIndex(index) # 设置当前行 tableView.edit(index) # 编辑当前行 # 槽方法,删除一个船 def removeShip(self): # 判断键盘焦点在哪个表格视图上 tableView = self.tableView1 if self.tableView2.hasFocus(): tableView = self.tableView2 index = tableView.currentIndex() if not index.isValid( ): # PyQt5.QtCore.QModelIndex.isValid() 此模型索引有效返回true,无效返回false return row = index.row() # 返回此模型索引引用的行 name = self.model.data(self.model.index( row, ships.NAME)) # QModelIndex QAbstractTableModel.index() 返回指定行,列的索引 owner = self.model.data( # QVariant QAbstractItemModel.data() 返回索引所引用项目的给定角色下存储的数据 self.model.index(row, ships.OWNER)) country = self.model.data(self.model.index(row, ships.COUNTRY)) if (QMessageBox.question( self, "Ships - Remove", "Remove {0} of {1}/{2}?".format(name, owner, country), QMessageBox.Yes | QMessageBox.No) == QMessageBox.No): return self.model.removeRows(row) self.resizeColumns()