class MainWindow(QMainWindow): def __init__(self, labeltool, parent=None): try: QMainWindow.__init__(self, parent) except: print('QMainWindow:bad init') self.idletimer = QTimer() self.loader = None self.labeltool = labeltool self.setupGui() self.loadApplicationSettings() self.onAnnotationsLoaded() # uncommenting crashes on Anaconda # Slots def onPluginLoaded(self, action): self.ui.menuPlugins.addAction(action) def onStatusMessage(self, message=''): self.statusBar().showMessage(message, 5000) def onModelDirtyChanged(self, dirty): postfix = "[+]" if dirty else "" if self.labeltool.getCurrentFilename() is not None: self.setWindowTitle("%s - %s %s" % \ (APP_NAME, QFileInfo(self.labeltool.getCurrentFilename()).fileName(), postfix)) else: self.setWindowTitle("%s - Unnamed %s" % (APP_NAME, postfix)) def onMousePositionChanged(self, x, y): self.posinfo.setText("%d, %d" % (x, y)) def startBackgroundLoading(self): self.stopBackgroundLoading(forced=True) self.loader = BackgroundLoader(self.labeltool.model(), self.statusBar(), self.sb_progress) self.idletimer.timeout.connect(self.loader.load) self.loader.finished.connect(self.stopBackgroundLoading) self.statusBar().addWidget(self.sb_progress) self.sb_progress.show() self.idletimer.start() def stopBackgroundLoading(self, forced=False): if not forced: self.statusBar().showMessage("Background loading finished", 5000) self.idletimer.stop() if self.loader is not None: self.idletimer.timeout.disconnect(self.loader.load) self.statusBar().removeWidget(self.sb_progress) self.loader = None def onAnnotationsLoaded(self): self.labeltool.model().dirtyChanged.connect(self.onModelDirtyChanged) self.onModelDirtyChanged(self.labeltool.model().dirty()) self.treeview.setModel(self.labeltool.model()) self.scene.setModel(self.labeltool.model()) self.selectionmodel = QItemSelectionModel(self.labeltool.model()) self.treeview.setSelectionModel(self.selectionmodel) self.treeview.selectionModel().currentChanged.connect(self.labeltool.setCurrentImage) self.property_editor.onModelChanged(self.labeltool.model()) self.startBackgroundLoading() def onCurrentImageChanged(self): new_image = self.labeltool.currentImage() self.scene.setCurrentImage(new_image) self.onFitToWindowModeChanged() self.treeview.scrollTo(new_image.index()) img = self.labeltool.getImage(new_image) if img is None: self.controls.setFilename("") self.selectionmodel.setCurrentIndex(new_image.index(), QItemSelectionModel.ClearAndSelect|QItemSelectionModel.Rows) return h = img.shape[0] w = img.shape[1] self.image_resolution.setText("%dx%d" % (w, h)) # TODO: This info should be obtained from AnnotationModel or LabelTool if isinstance(new_image, FrameModelItem): self.controls.setFrameNumAndTimestamp(new_image.framenum(), new_image.timestamp()) elif isinstance(new_image, ImageFileModelItem): self.controls.setFilename(os.path.basename(new_image['filename'])) self.selectionmodel.setCurrentIndex(new_image.index(), QItemSelectionModel.ClearAndSelect|QItemSelectionModel.Rows) def onFitToWindowModeChanged(self): if self.options["Fit-to-window mode"].isChecked(): self.view.fitInView() def onEnumerateCornersModeChanged(self): if self.options["Enumerate-corners mode"].isChecked(): self.scene.enumerateCorners() self.onCurrentImageChanged() else: self.scene.removeCorners() self.onCurrentImageChanged() def onCopyAnnotationsModeChanged(self): if self.annotationMenu["Copy from previous"].isChecked(): self.copyAnnotations.copy() self.annotationMenu["Copy from previous"].setChecked(False) def onInterpolateRangeModeChanged(self): if self.annotationMenu["Interpolate range"].isChecked(): self.interpolateRange.interpolateRange() self.annotationMenu["Interpolate range"].setChecked(False) def onScaleChanged(self, scale): self.zoominfo.setText("%.2f%%" % (100 * scale, )) def initShortcuts(self, HOTKEYS): self.shortcuts = [] for hotkey in HOTKEYS: assert len(hotkey) >= 2 key = hotkey[0] fun = hotkey[1] desc = "" if len(hotkey) > 2: desc = hotkey[2] if type(fun) == str: fun = import_callable(fun) hk = QAction(desc, self) hk.setShortcut(QKeySequence(key)) hk.setEnabled(True) if hasattr(fun, '__call__'): hk.triggered.connect(bind(fun, self.labeltool)) else: hk.triggered.connect(compose_noargs([bind(f, self.labeltool) for f in fun])) self.ui.menuShortcuts.addAction(hk) self.shortcuts.append(hk) def initOptions(self): self.options = {} for o in ["Fit-to-window mode"]: action = QAction(o, self) action.setCheckable(True) self.ui.menuOptions.addAction(action) self.options[o] = action for o in ["Enumerate-corners mode"]: action = QAction(o, self) action.setCheckable(True) self.ui.menuOptions.addAction(action) self.options[o] = action def initAnnotationMenu(self): self.annotationMenu = {} for a in ["Copy from previous"]: action = QAction(a, self) action.setCheckable(True) self.ui.menuAnnotation.addAction(action) self.annotationMenu[a] = action for a in ["Interpolate range"]: action = QAction(a, self) action.setCheckable(True) self.ui.menuAnnotation.addAction(action) self.annotationMenu[a] = action ### ### GUI/Application setup ###___________________________________________________________________________________________ def setupGui(self): self.ui = uic.loadUi(os.path.join(GUIDIR, "labeltool.ui"), self) # get inserters and items from labels # FIXME for handling the new-style config correctly inserters = dict([(label['attributes']['class'], label['inserter']) for label in config.LABELS if 'class' in label.get('attributes', {}) and 'inserter' in label]) items = dict([(label['attributes']['class'], label['item']) for label in config.LABELS if 'class' in label.get('attributes', {}) and 'item' in label]) # Property Editor self.property_editor = PropertyEditor(config.LABELS) self.ui.dockProperties.setWidget(self.property_editor) # Scene self.scene = AnnotationScene(self.labeltool, items=items, inserters=inserters) self.property_editor.insertionModeStarted.connect(self.scene.onInsertionModeStarted) self.property_editor.insertionModeEnded.connect(self.scene.onInsertionModeEnded) # SceneView self.view = GraphicsView(self) self.view.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding) self.view.setScene(self.scene) self.central_widget = QWidget() self.central_layout = QVBoxLayout() self.controls = ControlButtonWidget() #give functions as lambdas, or else they will be called with a bool as parameter self.controls.back_button.clicked.connect(lambda lt: self.labeltool.gotoPrevious()) self.controls.forward_button.clicked.connect(lambda lt: self.labeltool.gotoNext()) self.central_layout.addWidget(self.controls) self.central_layout.addWidget(self.view) self.central_widget.setLayout(self.central_layout) self.setCentralWidget(self.central_widget) self.initShortcuts(config.HOTKEYS) self.initOptions() self.initAnnotationMenu() self.treeview = AnnotationTreeView() self.treeview.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Preferred) self.ui.dockAnnotations.setWidget(self.treeview) self.scene.selectionChanged.connect(self.scene.onSelectionChanged) self.treeview.selectedItemsChanged.connect(self.scene.onSelectionChangedInTreeView) self.posinfo = QLabel("-1, -1") self.posinfo.setFrameStyle(QFrame.StyledPanel) self.statusBar().addPermanentWidget(self.posinfo) self.scene.mousePositionChanged.connect(self.onMousePositionChanged) self.image_resolution = QLabel("[no image]") self.image_resolution.setFrameStyle(QFrame.StyledPanel) self.statusBar().addPermanentWidget(self.image_resolution) self.zoominfo = QLabel() self.zoominfo.setFrameStyle(QFrame.StyledPanel) self.statusBar().addPermanentWidget(self.zoominfo) self.view.scaleChanged.connect(self.onScaleChanged) self.onScaleChanged(self.view.getScale()) self.sb_progress = QProgressBar() # View menu self.ui.menu_Views.addAction(self.ui.dockProperties.toggleViewAction()) self.ui.menu_Views.addAction(self.ui.dockAnnotations.toggleViewAction()) # Annotation menu self.copyAnnotations = CopyAnnotations(self.labeltool) self.interpolateRange = InterpolateRange(self.labeltool) # Show the UI. It is important that this comes *after* the above # adding of custom widgets, especially the central widget. Otherwise the # dock widgets would be far to large. self.ui.show() ## connect action signals self.connectActions() def connectActions(self): ## File menu self.ui.actionNew. triggered.connect(self.fileNew) self.ui.actionOpen. triggered.connect(self.fileOpen) self.ui.actionSave. triggered.connect(self.fileSave) self.ui.actionSave_As.triggered.connect(self.fileSaveAs) self.ui.actionExit. triggered.connect(self.close) ## View menu self.ui.actionLocked.toggled.connect(self.onViewsLockedChanged) ## Help menu self.ui.action_About.triggered.connect(self.about) ## Navigation self.ui.action_Add_Image.triggered.connect(self.addMediaFile) self.ui.actionNext. triggered.connect(self.labeltool.gotoNext) self.ui.actionPrevious. triggered.connect(self.labeltool.gotoPrevious) self.ui.actionZoom_In. triggered.connect(functools.partial(self.view.setScaleRelative, 1.2)) self.ui.actionZoom_Out. triggered.connect(functools.partial(self.view.setScaleRelative, 1/1.2)) ## Connections to LabelTool self.labeltool.pluginLoaded. connect(self.onPluginLoaded) self.labeltool.statusMessage. connect(self.onStatusMessage) self.labeltool.annotationsLoaded. connect(self.onAnnotationsLoaded) self.labeltool.currentImageChanged.connect(self.onCurrentImageChanged) ## options menu self.options["Fit-to-window mode"].changed.connect(self.onFitToWindowModeChanged) self.options["Enumerate-corners mode"].changed.connect(self.onEnumerateCornersModeChanged) ## annotation menu self.annotationMenu["Copy from previous"].changed.connect(self.onCopyAnnotationsModeChanged) self.annotationMenu["Interpolate range"].changed.connect(self.onInterpolateRangeModeChanged) def loadApplicationSettings(self): settings = QSettings() size = settings.value("MainWindow/Size", QSize(800, 600)) pos = settings.value("MainWindow/Position", QPoint(10, 10)) state = settings.value("MainWindow/State") locked = settings.value("MainWindow/ViewsLocked", False) if isinstance(size, QVariant): size = size.toSize() if isinstance(pos, QVariant): pos = pos.toPoint() if isinstance(state, QVariant): state = state.toByteArray() if isinstance(locked, QVariant): locked = locked.toBool() self.resize(size) self.move(pos) if state is not None: self.restoreState(state) self.ui.actionLocked.setChecked(bool(locked)) def saveApplicationSettings(self): settings = QSettings() settings.setValue("MainWindow/Size", self.size()) settings.setValue("MainWindow/Position", self.pos()) settings.setValue("MainWindow/State", self.saveState()) settings.setValue("MainWindow/ViewsLocked", self.ui.actionLocked.isChecked()) if self.labeltool.getCurrentFilename() is not None: filename = self.labeltool.getCurrentFilename() else: filename = None settings.setValue("LastFile", filename) def okToContinue(self): if self.labeltool.model().dirty(): reply = QMessageBox.question(self, "%s - Unsaved Changes" % (APP_NAME), "Save unsaved changes?", QMessageBox.Yes|QMessageBox.No|QMessageBox.Cancel) if reply == QMessageBox.Cancel: return False elif reply == QMessageBox.Yes: return self.fileSave() return True def fileNew(self): if self.okToContinue(): self.labeltool.clearAnnotations() def fileOpen(self): if not self.okToContinue(): return path = '.' filename = self.labeltool.getCurrentFilename() if (filename is not None) and (len(filename) > 0): path = QFileInfo(filename).path() format_str = ' '.join(self.labeltool.getAnnotationFilePatterns()) fname = QFileDialog.getOpenFileName(self, "%s - Load Annotations" % APP_NAME, path, "%s annotation files (%s)" % (APP_NAME, format_str))[0] if len(str(fname)) > 0: self.labeltool.loadAnnotations(fname) def fileSave(self): filename = self.labeltool.getCurrentFilename() if filename is None: return self.fileSaveAs() return self.labeltool.saveAnnotations(filename) def fileSaveAs(self): fname = '.' # self.annotations.filename() or '.' format_str = ' '.join(self.labeltool.getAnnotationFilePatterns()) fname = QFileDialog.getSaveFileName(self, "%s - Save Annotations" % APP_NAME, fname, "%s annotation files (%s)" % (APP_NAME, format_str))[0] if len(str(fname)) > 0: return self.labeltool.saveAnnotations(str(fname)) return False def addMediaFile(self): path = '.' filename = self.labeltool.getCurrentFilename() if (filename is not None) and (len(filename) > 0): path = QFileInfo(filename).path() image_types = [ '*.jpg', '*.bmp', '*.png', '*.pgm', '*.ppm', '*.tiff', '*.tif', '*.gif' ] video_types = [ '*.mp4', '*.mpg', '*.mpeg', '*.avi', '*.mov', '*.vob' ] format_str = ' '.join(image_types + video_types) fnames = QFileDialog.getOpenFileNames(self, "%s - Add Media File" % APP_NAME, path, "Media files (%s)" % (format_str, ))[0] item = None numFiles = len(fnames) progress_bar = QProgressDialog('Importing files...', 'Cancel import', 0, numFiles, self) for fname,c in zip(fnames, range(numFiles)): if len(str(fname)) == 0: continue fname = str(fname) if os.path.isabs(fname): fname = os.path.relpath(fname, str(path)) for pattern in image_types: if fnmatch.fnmatch(fname.lower(), pattern): item = self.labeltool.addImageFile(fname) progress_bar.setValue(c) if item is None: return self.labeltool.addVideoFile(fname) progress_bar.close() return item def onViewsLockedChanged(self, checked): features = QDockWidget.AllDockWidgetFeatures if checked: features = QDockWidget.NoDockWidgetFeatures self.ui.dockProperties.setFeatures(features) self.ui.dockAnnotations.setFeatures(features) ### ### global event handling ###______________________________________________________________________________ def closeEvent(self, event): if self.okToContinue(): self.saveApplicationSettings() else: event.ignore() def about(self): QMessageBox.about(self, "About %s" % APP_NAME, """<b>%s</b> version %s <p>This labeling application for computer vision research was developed at the CVHCI research group at KIT. <p>For more details, visit our homepage: <a href="%s">%s</a>""" % (APP_NAME, __version__, ORGANIZATION_DOMAIN, ORGANIZATION_DOMAIN))
class ManagerMainui(QMainWindow, Ui_MainWindow): def __init__(self): super(ManagerMainui, self).__init__() self.setupUi(self) self.opendb() self.show() self.lianjie() self.setCentralWidget(self.splitter) self.table_view.setSelectionBehavior(QAbstractItemView.SelectItems) self.table_view.setSelectionMode( QAbstractItemView.SingleSelection) # 一次选择单行还是多行 self.table_view.setAlternatingRowColors(True) # 隔行自动改变颜色 self.table_view.verticalHeader().setDefaultSectionSize(22) self.table_view.horizontalHeader().setDefaultSectionSize(60) # 为菜单添加动作 self.filemenu.addAction(self.exit_Act) self.Updata_Menu.addAction(self.Updata_Act) # 当前页 self.currentPage = 0 # 总页数 self.totalPage = 0 # 总记录数 self.totalRecord = 0 # 每页数据数 self.pageRecord = 10 def opendb(self): db = QSqlDatabase.addDatabase('QSQLITE') db.setDatabaseName('F:/pycharm项目/图书管理系统NEW/AllDataBase/book.db') if db.open(): self._biaocaozuo() else: QMessageBox.information(self, 'warning', '无法建立与数据库的连接') return False def _biaocaozuo(self): self.model = QtSql.QSqlTableModel() self.model.setTable('BookData') self.model.setEditStrategy(QSqlTableModel.OnManualSubmit) # 设置保存策略 self.table_view.setModel(self.model) self.model.select() self.model.setHeaderData(0, Qt.Horizontal, 'ISBN') self.model.setHeaderData(1, Qt.Horizontal, '书名') self.model.setHeaderData(2, Qt.Horizontal, '作者') self.model.setHeaderData(3, Qt.Horizontal, '出版社') self.model.setHeaderData(4, Qt.Horizontal, '出版日期') self.model.setHeaderData(5, Qt.Horizontal, '评分') self.model.setHeaderData(6, Qt.Horizontal, '照片') self.table_view.setColumnHidden(6, True) self.mapper = QDataWidgetMapper() self.mapper.setModel(self.model) self.mapper.setSubmitPolicy(QDataWidgetMapper.AutoSubmit) self.mapper.addMapping(self.lineEdit_ISBN, 0) self.mapper.addMapping(self.lineEdit_shuming, 1) self.mapper.addMapping(self.lineEdit_zuozhe, 2) self.mapper.addMapping(self.lineEdit_chubanshe, 3) self.mapper.addMapping(self.lineEdit_chubanriqi, 4) self.mapper.addMapping(self.lineEdit_pingfen, 5) self.mapper.toFirst() self.selModel = QItemSelectionModel(self.model) # 选择模型 self.table_view.setSelectionModel(self.selModel) self.selModel.currentChanged.connect( self.do_currentChanged) # 当前项变化时触发 self.selModel.currentRowChanged.connect( self.do_currentRowChanged) # 选择行变化时 # @pyqtSlot() ##保存修改 def on_p_baocun_triggered(self): result = QMessageBox.question(self, 'warning', '你要保存你的修改吗?', QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if result == QMessageBox.Yes: res = self.model.submitAll() if (res == False): QMessageBox.information( self, "消息", "数据保存错误,错误信息\n" + self.model.lastError().text()) else: QMessageBox.information(self, "message", "保存成功\n") self.p_baocun.setEnabled(False) self.p_quxiao.setEnabled(False) else: QMessageBox.information(self, 'message', 'Thanks') # @pyqtSlot() ##取消修改 def on_p_quxiao_triggered(self): # self.model.revertAll() result = QMessageBox.question(self, 'warning', '确认取消之前所有的的操作吗?', QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if result == QMessageBox.Yes: self.model.revertAll() self.p_baocun.setEnabled(False) self.p_quxiao.setEnabled(False) else: self.p_baocun.setEnabled(True) self.p_quxiao.setEnabled(True) # @pyqtSlot() ##添加记录 def on_p_zengjia_triggered(self): #self.model.insertRows(self.model.rowCount(), 1) self.model.insertRow(self.model.rowCount(), QModelIndex()) # 在末尾添加一个记录 curIndex = self.model.index(self.model.rowCount() - 1, 1) # 创建最后一行的ModelIndex self.selModel.clearSelection() # 清空选择项 self.selModel.setCurrentIndex( curIndex, QItemSelectionModel.Select) # 设置刚插入的行为当前选择行 # 删除记录 def on_p_sanchu_triggered(self): # if self.p_baocun.isEnabled(): result = QMessageBox.question(self, 'warning', '确认删除该记录吗?', QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if result == QMessageBox.Yes: self.model.removeRow(self.table_view.currentIndex().row()) self.p_baocun.setEnabled(True) self.p_quxiao.setEnabled(True) else: self.p_baocun.setEnabled(False) self.p_quxiao.setEnabled(False) # 插入记录 def on_p_charu_triggered(self): curIndex = self.table_view.currentIndex() # QModelIndex self.model.insertRow(curIndex.row(), QModelIndex()) self.selModel.clearSelection() # 清除已有选择 self.selModel.setCurrentIndex(curIndex, QItemSelectionModel.Select) # 对当前行设置图片 def on_p_photo_triggered(self): fileName, filt = QFileDialog.getOpenFileName(self, "选择图片文件", "", "照片(*.jpg)") if (fileName == ''): return file = QFile(fileName) # fileName为图片文件名 file.open(QIODevice.ReadOnly) try: data = file.readAll() # QByteArray finally: file.close() curRecNo = self.selModel.currentIndex().row() curRec = self.model.record(curRecNo) # 获取当前记录QSqlRecord curRec.setValue("Photo", data) # 设置字段数据 self.model.setRecord(curRecNo, curRec) pic = QPixmap() pic.loadFromData(data) W = self.dbLabPhoto.width() self.dbLabPhoto.setPixmap(pic.scaledToWidth(W)) # 在界面上显示 def closeEvent(self, event): """ 提示保存 """ if self.p_baocun.isEnabled(): r = QMessageBox.warning(self, "注意", "你还没有保存,现在保存下?", QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) if r == QMessageBox.No: event.accept() else: event.ignore() def chaxun_ISBN(self): text1 = self.lineEdit_chaxun.text() self.model.setFilter(("ISBN='%s'" % (text1))) def chaxun_zuozhe(self): text2 = self.lineEdit_chaxun.text() self.model.setFilter(("author='%s'" % (text2))) def chaxun_shumin(self): text3 = self.lineEdit_chaxun.text() self.model.setFilter(("title='%s'" % (text3))) def chaxun_(self): """ 查找图书 """ searchtext = self.lineEdit_chaxun.text() if searchtext: if self.comboBox_chaxun.currentText() == "ISBN": self.chaxun_ISBN() elif self.comboBox_chaxun.currentText() == "书名": self.chaxun_shumin() elif self.comboBox_chaxun.currentText() == "作者": self.chaxun_zuozhe() else: QMessageBox.information(self, "提示", "请输入搜索关键词!") def on_p_quxiaoCX_triggered(self): self.lineEdit_chaxun.clear() self.model.setFilter("") def do_currentChanged(self, current, previous): # 更新actPost和actCancel 的状态 self.p_baocun.setEnabled(self.model.isDirty()) # 有未保存修改时可用 self.p_quxiao.setEnabled(self.model.isDirty()) def do_currentRowChanged(self, current, previous): # 行切换时的状态控制 self.mapper.setCurrentIndex(current.row()) if (current.isValid() == False): self.dbLabPhoto.clear() # 清除图片显示 return self.mapper.setCurrentIndex(current.row()) # 更新数据映射的行号 curRec = self.model.record(current.row()) # 获取当前记录,QSqlRecord类型 if (curRec.isNull("Photo")): # 图片字段内容为空 self.dbLabPhoto.clear() else: # data=bytearray(curRec.value("Photo")) #可以工作 data = curRec.value("Photo") # 也可以工作 pic = QPixmap() pic.loadFromData(data) W = self.dbLabPhoto.size().width() self.dbLabPhoto.setPixmap(pic.scaledToWidth(W)) def scrapy(self): """启动爬虫,在线更新功能""" # cmdline.execute(" scrapy ".split()) self.hide() try: run = ScrapyRun() finally: pass def lianjie(self): self.p_zengjia.clicked.connect(self.on_p_zengjia_triggered) self.p_baocun.clicked.connect(self.on_p_baocun_triggered) self.p_quxiao.clicked.connect(self.on_p_quxiao_triggered) self.p_sanchu.clicked.connect(self.on_p_sanchu_triggered) self.p_charu.clicked.connect(self.on_p_charu_triggered) self.p_photo.clicked.connect(self.on_p_photo_triggered) self.p_chaxun.clicked.connect(self.chaxun_) self.p_quxiaoCX.clicked.connect(self.on_p_quxiaoCX_triggered) self.p_tuichu.clicked.connect(self.close) self.exit_Act.triggered.connect(self.close) self.Updata_Act.triggered.connect(self.scrapy)
class QmyMainWindow(QMainWindow): def __init__(self, parent=None): super().__init__(parent) #调用父类构造函数,创建窗体 self.ui = Ui_MainWindow() #创建UI对象 self.ui.setupUi(self) #构造UI界面 self.setCentralWidget(self.ui.splitter) # tableView显示属性设置 self.ui.tableView.setSelectionBehavior(QAbstractItemView.SelectRows) self.ui.tableView.setSelectionMode(QAbstractItemView.SingleSelection) self.ui.tableView.setAlternatingRowColors(True) self.ui.tableView.verticalHeader().setDefaultSectionSize(22) self.ui.tableView.horizontalHeader().setDefaultSectionSize(60) ## self.ui.tableView.resizeColumnsToContents() ## ==============自定义功能函数============ def __getFieldNames(self): ##获取所有字段名称 emptyRec = self.qryModel.record() #获取空记录,只有字段名 self.fldNum = {} #字段名与序号的字典 for i in range(emptyRec.count()): fieldName = emptyRec.fieldName(i) self.fldNum.setdefault(fieldName) self.fldNum[fieldName] = i print(self.fldNum) def __openTable(self): ##查询数据 self.qryModel = QSqlQueryModel(self) self.qryModel.setQuery( '''SELECT empNo, Name, Gender, Birthday, Province, Department, Salary FROM employee ORDER BY empNo''' ) if self.qryModel.lastError().isValid(): QMessageBox.critical( self, "错误", "数据表查询错误,错误信息\n" + self.qryModel.lastError().text()) return self.ui.statusBar.showMessage("记录条数:%d" % self.qryModel.rowCount()) self.__getFieldNames() #获取字段名和序号 ##设置字段显示名,直接使用序号 self.qryModel.setHeaderData(0, Qt.Horizontal, "工号") self.qryModel.setHeaderData(1, Qt.Horizontal, "姓名") self.qryModel.setHeaderData(2, Qt.Horizontal, "性别") self.qryModel.setHeaderData(3, Qt.Horizontal, "出生日期") self.qryModel.setHeaderData(4, Qt.Horizontal, "省份") self.qryModel.setHeaderData(5, Qt.Horizontal, "部门") self.qryModel.setHeaderData(6, Qt.Horizontal, "工资") ## self.qryModel.setHeaderData(self.fldNum["empNo"], Qt.Horizontal, "工号") ## self.qryModel.setHeaderData(self.fldNum["Name"], Qt.Horizontal, "姓名") ## self.qryModel.setHeaderData(self.fldNum["Gender"], Qt.Horizontal, "性别") ## self.qryModel.setHeaderData(self.fldNum["Birthday"], Qt.Horizontal, "出生日期") ## self.qryModel.setHeaderData(self.fldNum["Province"], Qt.Horizontal, "省份") ## self.qryModel.setHeaderData(self.fldNum["Department"], Qt.Horizontal, "部门") ## self.qryModel.setHeaderData(self.fldNum["Salary"], Qt.Horizontal, "工资") ##创建界面组件与数据模型的字段之间的数据映射 self.mapper = QDataWidgetMapper() self.mapper.setModel(self.qryModel) #设置数据模型 ## self.mapper.setSubmitPolicy(QDataWidgetMapper.AutoSubmit) ##界面组件与qryModel的具体字段之间的联系 ## self.mapper.addMapping(self.ui.dbSpinEmpNo, self.fldNum["empNo"]) ## self.mapper.addMapping(self.ui.dbEditName, self.fldNum["Name"]) ## self.mapper.addMapping(self.ui.dbComboSex, self.fldNum["Gender"]) ## self.mapper.addMapping(self.ui.dbEditBirth, self.fldNum["Birthday"]) ## self.mapper.addMapping(self.ui.dbComboProvince, self.fldNum["Province"] ) ## self.mapper.addMapping(self.ui.dbComboDep, self.fldNum["Department"] ) ## self.mapper.addMapping(self.ui.dbSpinSalary,self.fldNum["Salary"] ) self.mapper.addMapping(self.ui.dbSpinEmpNo, 0) self.mapper.addMapping(self.ui.dbEditName, 1) self.mapper.addMapping(self.ui.dbComboSex, 2) self.mapper.addMapping(self.ui.dbEditBirth, 3) self.mapper.addMapping(self.ui.dbComboProvince, 4) self.mapper.addMapping(self.ui.dbComboDep, 5) self.mapper.addMapping(self.ui.dbSpinSalary, 6) self.mapper.toFirst() #移动到首记录 self.selModel = QItemSelectionModel(self.qryModel) #关联选择模型 self.selModel.currentRowChanged.connect( self.do_currentRowChanged) #选择行变化时 self.ui.tableView.setModel(self.qryModel) #设置数据模型 self.ui.tableView.setSelectionModel(self.selModel) #设置选择模型 self.ui.actOpenDB.setEnabled(False) def __refreshTableView(self): ##刷新tableView显示 index = self.mapper.currentIndex() curIndex = self.qryModel.index(index, 1) #QModelIndex self.selModel.clearSelection() #清空选择项 self.selModel.setCurrentIndex(curIndex, QItemSelectionModel.Select) ## ==========由connectSlotsByName() 自动连接的槽函数================== @pyqtSlot() ##“打开数据库”按钮 def on_actOpenDB_triggered(self): dbFilename, flt = QFileDialog.getOpenFileName( self, "选择数据库文件", "", "SQL Lite数据库(*.db *.db3)") if (dbFilename == ''): return #打开数据库 self.DB = QSqlDatabase.addDatabase("QSQLITE") #添加 SQL LITE数据库驱动 self.DB.setDatabaseName(dbFilename) #设置数据库名称 ## DB.setHostName() ## DB.setUserName() ## DB.setPassword() if self.DB.open(): #打开数据库 self.__openTable() #打开数据表 else: QMessageBox.warning(self, "错误", "打开数据库失败") @pyqtSlot() ##首记录 def on_actRecFirst_triggered(self): self.mapper.toFirst() self.__refreshTableView() @pyqtSlot() ##前一记录 def on_actRecPrevious_triggered(self): self.mapper.toPrevious() self.__refreshTableView() @pyqtSlot() ##后一条记录 def on_actRecNext_triggered(self): self.mapper.toNext() self.__refreshTableView() @pyqtSlot() ##最后一条记录 def on_actRecLast_triggered(self): self.mapper.toLast() self.__refreshTableView() ## =============自定义槽函数=============================== def do_currentRowChanged(self, current, previous): ##记录移动时触发 if (current.isValid() == False): self.ui.dbLabPhoto.clear() #清除图片显示 return self.mapper.setCurrentIndex(current.row()) #更新数据映射的行号 first = (current.row() == 0) #是否首记录 last = (current.row() == self.qryModel.rowCount() - 1) #是否尾记录 self.ui.actRecFirst.setEnabled(not first) #更新使能状态 self.ui.actRecPrevious.setEnabled(not first) self.ui.actRecNext.setEnabled(not last) self.ui.actRecLast.setEnabled(not last) curRec = self.qryModel.record(current.row()) #获取当前记录,QSqlRecord类型 empNo = curRec.value("EmpNo") #不需要加 toInt()函数 query = QSqlQuery(self.DB) query.prepare( '''SELECT EmpNo, Memo, Photo FROM employee WHERE EmpNo = :ID''') query.bindValue(":ID", empNo) ## if not query.exec_(): #注意,在PyQt5.11.2之前的版本里只能使用exec_()函数 if not query.exec( ): #注意,在PyQt5.11.2添加了遗漏的overload型exec()函数,在PyQt5.11.2里没问题了 QMessageBox.critical(self, "错误", "执行SQL语句错误\n" + query.lastError().text()) return else: query.first() picData = query.value("Photo") if (picData == None): #图片字段内容为空 self.ui.dbLabPhoto.clear() else: #显示照片 pic = QPixmap() pic.loadFromData(picData) W = self.ui.dbLabPhoto.size().width() self.ui.dbLabPhoto.setPixmap(pic.scaledToWidth(W)) memoData = query.value("Memo") #显示备注 self.ui.dbEditMemo.setPlainText(memoData)
class QmyMainWindow(QMainWindow): def __init__(self, parent=None): super().__init__(parent) #调用父类构造函数,创建窗体 self.ui = Ui_MainWindow() #创建Ui对象 self.ui.setupUi(self) #构造UI self.__ColCount = 6 #列数 self.itemModel = QStandardItemModel( 10, self.__ColCount, self) #创建QStandardItemModel类型的数据模型,指定行列值 ''' setSelectionBehavior() 此属性保存视图使用的选择行为。 此属性保存选择是根据单个项目,行还是列进行的 #QItemSelectionModel() 此属性保存视图在哪种选择模式下运行。 #此属性控制用户是否可以选择一个或多个项目,并且在多个项目选择中控制选择是否必须是连续范围的项目 ''' self.selectionModel = QItemSelectionModel(self.itemModel) self.selectionModel.currentChanged.connect( self.do_curChanged) #单元格选择发生变化时会发射此信号 self.__lastColumnTitle = "" #设置最后一列的标题,可以是空 self.__lastColumnFlags = (Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled) #设置tableView属性 self.ui.tableView.setModel(self.itemModel) #数据模型 self.ui.tableView.setSelectionModel(self.selectionModel) #选择模型 oneOrMore = QAbstractItemView.ExtendedSelection #选择模式->多选模式 self.ui.tableView.setSelectionMode(oneOrMore) #多选模式 itemOrRow = QAbstractItemView.SelectItems #项选择模式->单元格选择 self.ui.tableView.setSelectionBehavior(itemOrRow) #单元格选择 self.ui.tableView.verticalHeader().setDefaultSectionSize(22) self.ui.tableView.setAlternatingRowColors(True) #交替行颜色 self.ui.tableView.setEnabled(False) #设置默认禁用tabelView self.ui.actFontBold.setCheckable(False) self.setCentralWidget(self.ui.splitter) #设置中心组件 # self.setCentralWidget(self.ui.tableView) self.__buildStatusBar() self.spinCeshen = QmyFloatSpinDelegate(0, 10000, 0, self) self.spinLength = QmyFloatSpinDelegate(0, 6000, 2, self) self.spinDegree = QmyFloatSpinDelegate(0, 360, 1, self) self.ui.tableView.setItemDelegateForColumn(0, self.spinCeshen) self.ui.tableView.setItemDelegateForColumn(1, self.spinLength) self.ui.tableView.setItemDelegateForColumn(3, self.spinLength) self.ui.tableView.setItemDelegateForColumn(2, self.spinDegree) qualities = ["优", "良", "合格", "不合格"] self.comboDelegate = QmyComboBoxDelegate(self) self.comboDelegate.setItems(qualities, False) self.ui.tableView.setItemDelegateForColumn(4, self.comboDelegate) ##==========自定义功能函数========== def __buildStatusBar(self): ''' 设置状态栏ui组件 :return: ''' self.LabCellPos = QLabel("当前单元格:", self) self.LabCellPos.setMinimumWidth(180) self.ui.statusBar.addWidget(self.LabCellPos) self.LabCellText = QLabel("单元格内容:", self) self.LabCellText.setMinimumWidth(150) self.ui.statusBar.addWidget(self.LabCellText) self.LabCurFile = QLabel("当前文件:", self) self.ui.statusBar.addWidget(self.LabCurFile) def __iniModelFromStringList(self, allLines): rowCnt = len(allLines) #获取总行数 self.itemModel.setRowCount(rowCnt - 1) #除去表头的数据行数 headerText = allLines[0].strip() #表头去除换行符,文件呢的空格使用Tab代替 headerList = headerText.split("\t") #按照制表符转化为列表 self.itemModel.setHorizontalHeaderLabels(headerList) #设置表头标题 # print(headerList) self.__lastColumnTitle = headerList[len(headerList) - 1] #最后一列的标题 lastColNo = self.__ColCount - 1 #最后一列的列号 for i in range(rowCnt - 1): #除去表头的数据行数 # print(i) lineText = allLines[i + 1].strip() #去除换行符,不包括表头 strList = lineText.split("\t") #按制表符生成列表 for j in range(self.__ColCount - 1): #不包括最后一列 ''' QStandardItem是一个数据结构,他可以存储一个cell的各种信息,比如文本、图标、是否可选、字体、别景色、前景色等等。 并且QStandardItem可以有孩子和兄弟,他是为model提供数据存储的节点。 QTableView:作为表格cell时,有一个作为根节点的QStandardItem,其他节点都是QStandardItem节点的孩子节点,并且都是兄弟节点(这里暂时不考虑多列的情况)。 QTreeView:作为树节点cell时,有一个作为根节点的QStandardItem,其他节点都是他的孩子节点,但是其他节点也可以作为父节点存在(这里暂时不考虑多列的情况)。 ''' item = QStandardItem(strList[j]) self.itemModel.setItem(i, j, item) #设置最后一行 # print(self.__lastColumnTitle) item = QStandardItem(self.__lastColumnTitle) #将最后一行的表头 item.setFlags(self.__lastColumnFlags) item.setCheckable(True) # print(strList[lastColNo]) if strList[lastColNo] == '0': #对比文本内的数值进行设定,类型是字符串 item.setCheckState(Qt.Unchecked) else: item.setCheckState(Qt.Checked) self.itemModel.setItem(i, lastColNo, item) def __setCellAlignment(self, align=Qt.AlignHCenter): if not self.selectionModel.hasSelection(): return ''' selectedIndexes()返回一个元素为QModelIndex类型的列表,包括所有被选中的单元格的模型索引 ''' selectdIndex = self.selectionModel.selectedIndexes() # print(selectdIndex) count = len(selectdIndex) for i in range(count): index = selectdIndex[i] ''' itemFromIndex(index)返回的是模型索引为index的QStandardItem对象 ''' item = self.itemModel.itemFromIndex(index) item.setTextAlignment(align) ##==========事件处理函数=========== ##==========由connectSlotsByName()自动关联的槽函数==== @pyqtSlot() def on_actOpenFile_triggered(self): curPath = os.getcwd() #获取当前路径 # print(curPath)? #flt是文件过滤器 filename, flt = QFileDialog.getOpenFileName( self, "打开一个文件", curPath, "井斜数据文件(*.txt);;所有文件(*.*)") if filename == "": return self.LabCurFile.setText(("当前文件: " + filename)) #设置状态栏文本 self.ui.plainTextEdit.clear() aFile = open(filename, "r") allLines = aFile.readlines() aFile.close() for strLine in allLines: self.ui.plainTextEdit.appendPlainText( strLine.strip()) #按照行添加到plainTextEdit中 self.__iniModelFromStringList(allLines) #设置激活状态 self.ui.tableView.setEnabled(True) self.ui.actAppend.setEnabled(True) self.ui.actInsert.setEnabled(True) self.ui.actDel.setEnabled(True) self.ui.actSaveFile.setEnabled(True) self.ui.actModelData.setEnabled(True) self.ui.actFontBold.setCheckable(True) #设置加粗可用 @pyqtSlot() def on_actAppend_triggered(self): itemlist = [] for i in range(self.__ColCount - 1): #循环一行中的各个列,不包括最后一列 item = QStandardItem("0") #添加0到数据结构中 itemlist.append(item) item = QStandardItem(self.__lastColumnTitle) #将最后一行的表头添加入数据结构 item.setCheckable(True) #可选 item.setFlags(self.__lastColumnFlags) itemlist.append(item) #添加到itemlist内(添加到最后一个) self.itemModel.appendRow(itemlist) curIndex = self.itemModel.index(self.itemModel.rowCount() - 1, 0) #获取最后一行第一个单元格的模型索引 self.selectionModel.clearSelection() #清除选择 self.selectionModel.setCurrentIndex( curIndex, QItemSelectionModel.Select) #设置在添加后自动选择添加行的第一个单元格(可从状态栏确认) @pyqtSlot() def on_actInsert_triggered(self): ''' 插入行 :return: ''' itemlist = [] for i in range(self.__ColCount - 1): item = QStandardItem("0") itemlist.append(item) item = QStandardItem(self.__lastColumnTitle) item.setFlags(self.__lastColumnFlags) item.setCheckable(False) #是否可以修改选择 item.setCheckState(Qt.Checked) #是否选中 itemlist.append(item) curIndex = self.selectionModel.currentIndex() #选中项的模型索引,包括行列等其他信息 self.itemModel.insertRow(curIndex.row(), itemlist) self.selectionModel.clearSelection() self.selectionModel.setCurrentIndex(curIndex, QItemSelectionModel.Select) @pyqtSlot() def on_actDel_triggered(self): ''' 删除行 :return: ''' curIndex = self.selectionModel.currentIndex() self.itemModel.removeRow(curIndex.row()) @pyqtSlot() def on_actAlignLeft_triggered(self): self.__setCellAlignment(Qt.AlignLeft | Qt.AlignVCenter) @pyqtSlot() def on_actAlignCenter_triggered(self): self.__setCellAlignment(Qt.AlignHCenter | Qt.AlignVCenter) @pyqtSlot() def on_actAlignRight_triggered(self): self.__setCellAlignment(Qt.AlignRight | Qt.AlignVCenter) @pyqtSlot(bool) def on_actFontBold_triggered(self, checked): print("1,checked", checked) if not self.selectionModel.hasSelection(): return selectIndex = self.selectionModel.selectedIndexes() for i in range(len(selectIndex)): index = selectIndex[i] item = self.itemModel.itemFromIndex(index) font = item.font() font.setBold(checked) item.setFont(font) @pyqtSlot() def on_actModelData_triggered(self): self.ui.plainTextEdit.clear() lineStr = "" for i in range(self.itemModel.columnCount() - 1): #不包括最后一列的表头遍历 item = self.itemModel.horizontalHeaderItem(i) #返回行标题的一个数据项对象,i是列号 lineStr = lineStr + item.text() + "\t" #给每个项对象的文本添加Tab制表符 item = self.itemModel.horizontalHeaderItem(self.__ColCount - 1) #最后一列的表头 lineStr = lineStr + item.text() self.ui.plainTextEdit.appendPlainText(lineStr) for i in range(self.itemModel.rowCount()): #获取行总数,不包括表头行 lineStr = "" for j in range(self.itemModel.columnCount() - 1): #不包括最后一列的遍历 item = self.itemModel.item(i, j) #按照行列号获取工作区内项对象 lineStr = lineStr + item.text() + "\t" #项对象文本添加制表符 item = self.itemModel.item(i, self.__ColCount - 1) #按照行号获取最后一列的项对象 if item.checkState() == Qt.Checked: #匹配状态是否为选中 lineStr = lineStr + "1" else: lineStr = lineStr + "0" self.ui.plainTextEdit.appendPlainText(lineStr) @pyqtSlot() def on_actSaveFile_triggered(self): ''' 保存文件 :return: ''' curPath = os.getcwd() filename, flt = QFileDialog.getSaveFileName( self, "保存文件", curPath, "井斜数据文件(*.txt);;所有文件(*.*)") if filename == "": return self.on_actModelData_triggered() aFile = open(filename, "w") aFile.write(self.ui.plainTextEdit.toPlainText()) aFile.close() ##=========自定义槽函数============ def do_curChanged(self, current, previous): ''' 设置状态栏组件显示内容 :param current: :param previous: :return: ''' if current != None: text = " 当前单元格: %d行,%d列" % (current.row() + 1, current.column() + 1) self.LabCellPos.setText(text) item = self.itemModel.itemFromIndex(current) self.LabCellText.setText("单元格内容:" + item.text()) font = item.font() self.ui.actFontBold.setChecked( font.bold()) #设置按钮按下,当font.Bold的值大于50式font.blod()会为True
class QmyMainWindow(QMainWindow): cellIndexChanged = pyqtSignal(int, int) def __init__(self, parent=None): super().__init__(parent) #调用父类构造函数,创建窗体 self.ui = Ui_MainWindow() #创建UI对象 self.ui.setupUi(self) #构造UI界面 self.__dlgSetHeaders = None self.setCentralWidget(self.ui.tableView) ##构建状态栏 self.LabCellPos = QLabel("当前单元格:", self) self.LabCellPos.setMinimumWidth(180) self.ui.statusBar.addWidget(self.LabCellPos) self.LabCellText = QLabel("单元格内容:", self) self.LabCellText.setMinimumWidth(200) self.ui.statusBar.addWidget(self.LabCellText) ##构建Item Model/View self.itemModel = QStandardItemModel(10, 5, self) #数据模型,10行5列 self.selectionModel = QItemSelectionModel(self.itemModel) #Item选择模型 self.selectionModel.currentChanged.connect(self.do_currentChanged) ##为tableView设置数据模型 self.ui.tableView.setModel(self.itemModel) #设置数据模型 self.ui.tableView.setSelectionModel(self.selectionModel) #设置选择模型 ## self.ui.tableView.setSelectionMode(QAbstractItemView.SingleSelection) #单选 ## self.ui.tableView.setSelectionBehavior(QAbstractItemView.SelectItems) #单元格选择 ## self.ui.tableView.setAlternatingRowColors(True) ## self.ui.tableView.verticalHeader().setDefaultSectionSize(25)#缺省行高 def __del__(self): ## super().__del__(self) print("QmyMainWindow 对象被删除了") ## ==============自定义功能函数============ ## ==========由connectSlotsByName() 自动连接的槽函数================== @pyqtSlot() ##设置行数列数对话框 def on_actTab_SetSize_triggered(self): dlgTableSize = QmyDialogSize() #局部变量,构建时不能传递self dlgTableSize.setIniSize(self.itemModel.rowCount(), self.itemModel.columnCount()) ret = dlgTableSize.exec() #模态方式运行对话框 if (ret == QDialog.Accepted): rows, cols = dlgTableSize.getTableSize() self.itemModel.setRowCount(rows) self.itemModel.setColumnCount(cols) @pyqtSlot() ##设置表头标题 def on_actTab_SetHeader_triggered(self): if (self.__dlgSetHeaders == None): #未创建对话框 self.__dlgSetHeaders = QmyDialogHeaders(self) count = len(self.__dlgSetHeaders.headerList()) if (count != self.itemModel.columnCount()): #列数改变了 strList = [] for i in range(self.itemModel.columnCount()): text = str( self.itemModel.headerData(i, Qt.Horizontal, Qt.DisplayRole)) strList.append(text) #现有表格标题 self.__dlgSetHeaders.setHeaderList(strList) ret = self.__dlgSetHeaders.exec() #以模态方式运行对话框 if (ret == QDialog.Accepted): strList2 = self.__dlgSetHeaders.headerList() self.itemModel.setHorizontalHeaderLabels(strList2) @pyqtSlot() ##"定位单元格" def on_actTab_Locate_triggered(self): dlgLocate = QmyDialogLocate(self) dlgLocate.setSpinRange(self.itemModel.rowCount(), self.itemModel.columnCount()) dlgLocate.changeActionEnable.connect(self.do_setActLocateEnable) dlgLocate.changeCellText.connect(self.do_setACellText) self.cellIndexChanged.connect(dlgLocate.do_setSpinValue) dlgLocate.setAttribute(Qt.WA_DeleteOnClose) #对话框关闭时自动删除 dlgLocate.show() ## =============自定义槽函数=============================== def do_currentChanged(self, current, previous): if (current != None): #当前模型索引有效 self.LabCellPos.setText( "当前单元格:%d行,%d列" % (current.row(), current.column())) #显示模型索引的行和列号 item = self.itemModel.itemFromIndex(current) #从模型索引获得Item self.LabCellText.setText("单元格内容:" + item.text()) #显示item的文字内容 self.cellIndexChanged.emit(current.row(), current.column()) ## @pyqtSlot(bool) def do_setActLocateEnable(self, enable): self.ui.actTab_Locate.setEnabled(enable) ## @pyqtSlot(int,int,str) def do_setACellText(self, row, column, text): index = self.itemModel.index(row, column) #获取模型索引 self.selectionModel.clearSelection() #清除现有选择 self.selectionModel.setCurrentIndex( index, QItemSelectionModel.Select) #定位到单元格 self.itemModel.setData(index, text, Qt.DisplayRole) #设置单元格字符串
class QmyMainWindow(QtWidgets.QMainWindow): def __init__(self, parent=None): super().__init__(parent) self.ui = Ui_MainWindow() self.ui.setupUi(self) self.setCentralWidget(self.ui.tableView) self.ui.tableView.setSelectionBehavior(QAbstractItemView.SelectItems) self.ui.tableView.setSelectionMode(QAbstractItemView.SingleSelection) self.ui.tableView.setAlternatingRowColors(True) self.ui.tableView.verticalHeader().setDefaultSectionSize(22) self.ui.tableView.horizontalHeader().setDefaultSectionSize(100) def __getFieldNames(self): emptyRec = self.tabModel.record() self.fldNum = {} for i in range(emptyRec.count()): fieldName = emptyRec.fieldName(i) self.fldNum.setdefault(fieldName) self.fldNum[fieldName] = i print(self.fldNum) def __openTable(self): self.tabModel = QSqlRelationalTableModel(self, self.DB) self.tabModel.setTable("studInfo") self.tabModel.setEditStrategy(QSqlTableModel.OnManualSubmit) self.tabModel.setSort(self.tabModel.fieldIndex("studID"), Qt.AscendingOrder) if (self.tabModel.select() == False): QMessageBox.critical( self, "错误信息", "打开数据表错误,错误信息\n" + self.tabModel.lastError().text()) return self.__getFieldNames() self.tabModel.setHeaderData(self.fldNum["studID"], Qt.Horizontal, "学号") self.tabModel.setHeaderData(self.fldNum["name"], Qt.Horizontal, "姓名") self.tabModel.setHeaderData(self.fldNum["gender"], Qt.Horizontal, "性别") self.tabModel.setHeaderData(self.fldNum["departID"], Qt.Horizontal, "学院") self.tabModel.setHeaderData(self.fldNum["majorID"], Qt.Horizontal, "专业") self.tabModel.setRelation( self.fldNum["departID"], QSqlRelation("departments", "departID", "department")) self.tabModel.setRelation(self.fldNum["majorID"], QSqlRelation("majors", "majorID", "major")) self.selModel = QItemSelectionModel(self.tabModel) self.selModel.currentChanged.connect(self.do_currentChanged) self.ui.tableView.setModel(self.tabModel) self.ui.tableView.setSelectionModel(self.selModel) delgate = QSqlRelationalDelegate(self.ui.tableView) self.ui.tableView.setItemDelegate(delgate) self.tabModel.select() self.ui.actOpenDB.setEnabled(False) self.ui.actRecAppend.setEnabled(True) self.ui.actRecInsert.setEnabled(True) self.ui.actRecDelete.setEnabled(True) self.ui.actFields.setEnabled(True) @pyqtSlot() def on_actOpenDB_triggered(self): dbFilename, flt = QFileDialog.getOpenFileName( self, "选择数据库文件", "", "SQL Lite数据库(*.db *.db3)") if (dbFilename == ''): return self.DB = QSqlDatabase.addDatabase("QSQLITE") self.DB.setDatabaseName(dbFilename) if self.DB.open(): self.__openTable() else: QMessageBox.warning(self, "错误", "打开数据库失败了") @pyqtSlot() def on_actSubmit_triggered(self): res = self.tabModel.submitAll() if (res == False): QMessageBox.information( self, "消息", "数据保存错误,错误信息\n" + self.tabModel.lastError().text()) else: self.ui.actSubmit.setEnabled(False) self.ui.actRevert.setEnabled(False) @pyqtSlot() def on_actRevert_triggered(self): self.tabModel.revertAll() self.ui.actSubmit.setEnabled(False) self.ui.actRevert.setEnabled(False) @pyqtSlot() def on_actRecInsert_triggered(self): curIndex = self.ui.tableView.currentIndex() self.tabModel.insertRow(curIndex.row(), QModelIndex()) self.selModel.clearSelection() self.selModel.setCurrentIndex(curIndex, QItemSelectionModel.Select) @pyqtSlot() def on_actRecDelete_triggered(self): curIndex = self.selModel.currentIndex() self.tabModel.removeRow(curIndex.row()) @pyqtSlot() def on_actFields_triggered(self): emptyRec = self.tabModel.record() str = '' for i in range(emptyRec.count()): str = str + emptyRec.fieldName(i) + '\n' QMessageBox.information(self, "所有字段名", str) def do_currentChanged(self, current, previous): self.ui.actSubmit.setEnabled(self.tabModel.isDirty()) self.ui.actRevert.setEnabled(self.tabModel.isDirty())
class QmyMainWindow(QMainWindow): def __init__(self, parent=None): super().__init__(parent) #调用父类构造函数,创建窗体 self.ui = Ui_MainWindow() #创建UI对象 self.ui.setupUi(self) #构造UI界面 self.setCentralWidget(self.ui.tableView) # tableView显示属性设置 self.ui.tableView.setSelectionBehavior(QAbstractItemView.SelectItems) self.ui.tableView.setSelectionMode(QAbstractItemView.SingleSelection) self.ui.tableView.setAlternatingRowColors(True) self.ui.tableView.verticalHeader().setDefaultSectionSize(22) self.ui.tableView.horizontalHeader().setDefaultSectionSize(100) ## ==============自定义功能函数============ def __getFieldNames(self): ##获取所有字段名称 emptyRec = self.tabModel.record() #获取空记录,只有字段名 self.fldNum = {} #字段名与序号的字典 for i in range(emptyRec.count()): fieldName = emptyRec.fieldName(i) self.fldNum.setdefault(fieldName) self.fldNum[fieldName] = i print(self.fldNum) def __openTable(self): ##打开数据表 self.tabModel = QSqlRelationalTableModel(self, self.DB) #数据表 self.tabModel.setTable("studInfo") #设置数据表 self.tabModel.setEditStrategy(QSqlTableModel.OnManualSubmit ) #数据保存方式,OnManualSubmit , OnRowChange self.tabModel.setSort(self.tabModel.fieldIndex("studID"), Qt.AscendingOrder) #排序 if (self.tabModel.select() == False): #查询数据失败 QMessageBox.critical( self, "错误信息", "打开数据表错误,错误信息\n" + self.tabModel.lastError().text()) return self.__getFieldNames() #获取字段名和序号 ##字段显示名 self.tabModel.setHeaderData(self.fldNum["studID"], Qt.Horizontal, "学号") self.tabModel.setHeaderData(self.fldNum["name"], Qt.Horizontal, "姓名") self.tabModel.setHeaderData(self.fldNum["gender"], Qt.Horizontal, "性别") self.tabModel.setHeaderData(self.fldNum["departID"], Qt.Horizontal, "学院") self.tabModel.setHeaderData(self.fldNum["majorID"], Qt.Horizontal, "专业") ## 设置代码字段的查询关系数据表 self.tabModel.setRelation(self.fldNum["departID"], QSqlRelation("departments", "departID", "department")) #学院 self.tabModel.setRelation(self.fldNum["majorID"], QSqlRelation("majors", "majorID", "major")) #专业 self.selModel = QItemSelectionModel(self.tabModel) #关联选择模型 ##selModel当前项变化时触发currentChanged信号 self.selModel.currentChanged.connect(self.do_currentChanged) ##选择行变化时 ## self.selModel.currentRowChanged.connect(self.do_currentRowChanged) self.ui.tableView.setModel(self.tabModel) #设置数据模型 self.ui.tableView.setSelectionModel(self.selModel) #设置选择模型 delgate = QSqlRelationalDelegate(self.ui.tableView) self.ui.tableView.setItemDelegate(delgate) #为关系型字段设置缺省代理组件 self.tabModel.select() #必须重新查询数据 ##更新actions和界面组件的使能状态 self.ui.actOpenDB.setEnabled(False) self.ui.actRecAppend.setEnabled(True) self.ui.actRecInsert.setEnabled(True) self.ui.actRecDelete.setEnabled(True) self.ui.actFields.setEnabled(True) ## ==========由connectSlotsByName() 自动连接的槽函数================== @pyqtSlot() def on_actOpenDB_triggered(self): dbFilename, flt = QFileDialog.getOpenFileName( self, "选择数据库文件", "", "SQL Lite数据库(*.db *.db3)") if (dbFilename == ''): return #打开数据库 self.DB = QSqlDatabase.addDatabase("QSQLITE") #添加 SQL LITE数据库驱动 self.DB.setDatabaseName(dbFilename) #设置数据库名称 ## DB.setHostName() ## DB.setUserName() ## DB.setPassword() if self.DB.open(): #打开数据库 self.__openTable() #打开数据表 else: QMessageBox.warning(self, "错误", "打开数据库失败") @pyqtSlot() ##保存修改 def on_actSubmit_triggered(self): res = self.tabModel.submitAll() if (res == False): QMessageBox.information( self, "消息", "数据保存错误,错误信息\n" + self.tabModel.lastError().text()) else: self.ui.actSubmit.setEnabled(False) self.ui.actRevert.setEnabled(False) @pyqtSlot() ##取消修改 def on_actRevert_triggered(self): self.tabModel.revertAll() self.ui.actSubmit.setEnabled(False) self.ui.actRevert.setEnabled(False) @pyqtSlot() ##添加记录 def on_actRecAppend_triggered(self): self.tabModel.insertRow(self.tabModel.rowCount(), QModelIndex()) #在末尾添加一个记录 curIndex = self.tabModel.index(self.tabModel.rowCount() - 1, 1) #创建最后一行的ModelIndex self.selModel.clearSelection() #清空选择项 self.selModel.setCurrentIndex( curIndex, QItemSelectionModel.Select) #设置刚插入的行为当前选择行 @pyqtSlot() ##插入记录 def on_actRecInsert_triggered(self): curIndex = self.ui.tableView.currentIndex() #QModelIndex self.tabModel.insertRow(curIndex.row(), QModelIndex()) self.selModel.clearSelection() #清除已有选择 self.selModel.setCurrentIndex(curIndex, QItemSelectionModel.Select) @pyqtSlot() ##删除记录 def on_actRecDelete_triggered(self): curIndex = self.selModel.currentIndex() #获取当前选择单元格的模型索引 self.tabModel.removeRow(curIndex.row()) #删除最后一行 @pyqtSlot() ##显示字段列表 def on_actFields_triggered(self): emptyRec = self.tabModel.record() #获取空记录,只有字段名 str = '' for i in range(emptyRec.count()): str = str + emptyRec.fieldName(i) + '\n' QMessageBox.information(self, "所有字段名", str) ## =============自定义槽函数=============================== def do_currentChanged(self, current, previous): ##更新actPost和actCancel 的状态 self.ui.actSubmit.setEnabled(self.tabModel.isDirty()) #有未保存修改时可用 self.ui.actRevert.setEnabled(self.tabModel.isDirty())
class QmyMainWindow(QtWidgets.QMainWindow): def __init__(self, parent=None): super().__init__(parent) self.ui = Ui_MainWindow() self.ui.setupUi(self) self.setCentralWidget(self.ui.splitter) self.ui.tableView.setSelectionBehavior(QAbstractItemView.SelectItems) self.ui.tableView.setSelectionMode(QAbstractItemView.SingleSelection) self.ui.tableView.setAlternatingRowColors(True) self.ui.tableView.verticalHeader().setDefaultSectionSize(22) self.ui.tableView.horizontalHeader().setDefaultSectionSize(60) def __openTable(self): self.tabModel = QSqlTableModel(self, self.DB) self.tabModel.setTable("employee") self.tabModel.setEditStrategy(QSqlTableModel.OnManualSubmit) self.tabModel.setSort(self.tabModel.fieldIndex("empNo"), Qt.AscendingOrder) if(self.tabModel.select()==False): QMessageBox.critical(self, "错误信息", "打开数据表错误,错误信息\n"+self.tabModel.lastError().text()) return self.__getFieldNames() self.tabModel.setHeaderData(self.fldNum["empNo"], Qt.Horizontal, "工号") self.tabModel.setHeaderData(self.fldNum["Name"], Qt.Horizontal, "姓名") self.tabModel.setHeaderData(self.fldNum["Gender"], Qt.Horizontal, "性别") self.tabModel.setHeaderData(self.fldNum["Birthday"], Qt.Horizontal, "出生日期") self.tabModel.setHeaderData(self.fldNum["Province"], Qt.Horizontal, "省份") self.tabModel.setHeaderData(self.fldNum["Department"], Qt.Horizontal, "部门") self.tabModel.setHeaderData(self.fldNum["Salary"], Qt.Horizontal, "工资") self.tabModel.setHeaderData(self.fldNum["Memo"], Qt.Horizontal, "备注") self.tabModel.setHeaderData(self.fldNum["Photo"], Qt.Horizontal, "照片") self.mapper = QDataWidgetMapper() self.mapper.setModel(self.tabModel) self.mapper.setSubmitPolicy(QDataWidgetMapper.AutoSubmit) self.mapper.addMapping(self.ui.dbSpinEmpNo, self.fldNum["empNo"]) self.mapper.addMapping(self.ui.dbEditName, self.fldNum["Name"]) self.mapper.addMapping(self.ui.dbComboSex, self.fldNum["Gender"]) self.mapper.addMapping(self.ui.dbEditBirth, self.fldNum["Birthday"]) self.mapper.addMapping(self.ui.dbComboProvince, self.fldNum["Province"]) self.mapper.addMapping(self.ui.dbComboDep, self.fldNum["Department"]) self.mapper.addMapping(self.ui.dbSpinSalary, self.fldNum["Salary"]) self.mapper.addMapping(self.ui.dbEditMemo, self.fldNum["Memo"]) self.mapper.toFirst() self.selModel = QItemSelectionModel(self.tabModel) self.selModel.currentChanged.connect(self.do_currentChanged) self.selModel.currentRowChanged.connect(self.do_currentRowChanged) self.ui.tableView.setModel(self.tabModel) self.ui.tableView.setSelectionModel(self.selModel) self.ui.tableView.setColumnHidden(self.fldNum["Memo"], True) self.ui.tableView.setColumnHidden(self.fldNum["Photo"], True) strList = ("男", "女") self.__delegatesex = QmyComboBoxDelegate() self.__delegatesex.setItems(strList, False) self.ui.tableView.setItemDelegateForColumn(self.fldNum["Gender"], self.__delegatesex) strList = ("销售部", "技术部", "生产部", "行政部") self.__delegateDepart = QmyComboBoxDelegate() self.__delegateDepart.setItems(strList, True) self.ui.tableView.setItemDelegateForColumn(self.fldNum["Department"], self.__delegateDepart) self.ui.actOpenDB.setEnabled(False) self.ui.actOpenDB.setEnabled(False) self.ui.actRecAppend.setEnabled(True) self.ui.actRecInsert.setEnabled(True) self.ui.actRecDelete.setEnabled(True) self.ui.actScan.setEnabled(True) self.ui.groupBoxSort.setEnabled(True) self.ui.groupBoxFilter.setEnabled(True) def __getFieldNames(self): emptyRec = self.tabModel.record() self.fldNum = {} for i in range(emptyRec.count()): fieldName = emptyRec.fieldName(i) self.ui.comboFields.addItem(fieldName) self.fldNum.setdefault(fieldName) self.fldNum[fieldName]=i print(self.fldNum) def do_currentChanged(self, current, previous): self.ui.actSubmit.setEnabled(self.tabModel.isDirty()) self.ui.actRevert.setEnabled(self.tabModel.isDirty()) def do_currentRowChanged(self, current, previous): self.ui.actRecDelete.setEnabled(current.isValid()) self.ui.actPhoto.setEnabled(current.isValid()) self.ui.actPhotoClear.setEnabled(current.isValid()) if(current.isValid() == False): self.ui.dbLabPhoto.clear() return self.mapper.setCurrentIndex(current.row()) curRec = self.tabModel.record(current.row()) if(curRec.isNull("Photo")): self.ui.dbLabPhoto.clear() else: data = curRec.value("Photo") pic = QPixmap() pic.loadFromData(data) w = self.ui.dbLabPhoto.size().width() self.ui.dbLabPhoto.setPixmap(pic.scaledToWidth(w)) @pyqtSlot() def on_actOpenDB_triggered(self): dbFilename ,flt = QFileDialog.getOpenFileName(self, "选择数据库文件", "", "SQL Lite数据库(*.db *.db3)") if (dbFilename == ''): return self.DB = QSqlDatabase.addDatabase("QSQLITE") self.DB.setDatabaseName(dbFilename) if self.DB.open(): self.__openTable() else: QMessageBox.warning(self, "错误", "打开数据库失败") @pyqtSlot() def on_actSubmit_triggered(self): res = self.tabModel.submitAll() if(res == False): QMessageBox.information(self, "消息", "数据保存错误,错误信息\n" + self.tabModel.lastError().text()) else: self.ui.actSubmit.setEnabled(False) self.ui.actRevert.setEnabled(False) @pyqtSlot() def on_actRevert_triggered(self): self.tabModel.revertAll() self.ui.actSubmit.setEnabled(False) self.ui.actRevert.setEnabled(False) @pyqtSlot() def on_actRecAppend_triggered(self): self.tabModel.insertRow(self.tabModel.rowCount(), QModelIndex()) curIndex = self.tabModel.index(self.tabModel.rowCount()-1, 1) self.selModel.clearSelection() self.selModel.setCurrentIndex(curIndex, QItemSelectionModel.Select) currow = curIndex.row() self.tabModel.setData(self.tabModel.index(currow, self.fldNum["empNo"]), 2000+self.tabModel.rowCount()) self.tabModel.setData(self.tabModel.index(currow, self.fldNum["Gender"]), "男") @pyqtSlot() def on_actRecInsert_triggered(self): curIndex = self.ui.tableView.currentIndex() self.tabModel.insertRow(curIndex.row(), QModelIndex()) self.selModel.clearSelection() self.selModel.setCurrentIndex(curIndex, QItemSelectionModel.Select) @pyqtSlot() def on_actRecDelete_triggered(self): curIndex = self.selModel.currentIndex() self.tabModel.removeRow(curIndex.row()) @pyqtSlot() def on_actPhotoClear_triggered(self): curRecNo = self.selModel.currentIndex().row() curRec = self.tabModel.record(curRecNo) curRec.setNull("Photo") self.tabModel.setRecord(curRecNo, curRec) self.ui.dbLabPhoto.clear() @pyqtSlot() def on_actPhoto_triggered(self): fileName, filt = QFileDialog.getOpenFileName(self, "选择图片文件", "", "照片(*.jpg") if(fileName==''): return file=QFile(fileName) file.open(QIODevice.ReadOnly) try: data = file.readAll() finally: file.close() curRecNo = self.selModel.currentIndex().row() curRec = self.tabModel.record(curRecNo) curRec.setValue("Photo", data) self.tabModel.setRecord(curRecNo, curRec) pic = QPixmap() pic.loadFromData(data) w = self.ui.dbLabPhoto.width() self.ui.dbLabPhoto.setPixmap(pic.scaledToWidth(w)) @pyqtSlot() def on_actScan_triggered(self): if(self.tabModel.rowCount()==0): return for i in range(self.tabModel.rowCount()): aRec = self.tabModel.record(i) salary = aRec.value("Salary") salary = salary*1.1 aRec.setValue("Salary", salary) self.tabModel.setRecord(i, aRec) if(self.tabModel.submitAll()): QMessageBox.information(self, "消息", "涨工资计算完毕了") @pyqtSlot() def on_comboFields_currentIndexChanged(self, index): if self.ui.radioBtnAscend.isChecked(): self.tabModel.setSort(index, Qt.AscendingOrder) else: self.tabModel.setSort(index, Qt.DescendingOrder) self.tabModel.select() @pyqtSlot() def on_radioBtnAscend_clicked(self): self.tabModel.setSort(self.ui.comboFields.currentIndex(), Qt.AscendingOrder) self.tabModel.select() @pyqtSlot() def on_radioBtnDescend_clicked(self): self.tabModel.setSort(self.ui.comboFields.currentIndex(), Qt.DescendingOrder) self.tabModel.select() @pyqtSlot() def on_radioBtnMan_clicked(self): self.tabModel.setFilter("Gender='男'") @pyqtSlot() def on_radioBtnWoman_clicked(self): self.tabModel.setFilter("Gender='女'") @pyqtSlot() def on_radioBtnBoth_clicked(self): self.tabModel.setFilter("")
class QmyMainWindow(QMainWindow): def __init__(self, parent=None): super().__init__(parent) #调用父类构造函数,创建窗体 self.ui = Ui_MainWindow() #创建UI对象 self.ui.setupUi(self) #构造UI界面 self.setCentralWidget(self.ui.splitter) ## tableView显示属性设置 self.ui.tableView.setSelectionBehavior(QAbstractItemView.SelectItems) self.ui.tableView.setSelectionMode(QAbstractItemView.SingleSelection) self.ui.tableView.setAlternatingRowColors(True) self.ui.tableView.verticalHeader().setDefaultSectionSize(22) self.ui.tableView.horizontalHeader().setDefaultSectionSize(60) ## ==============自定义功能函数============ def __getFieldNames(self): ##获取所有字段名称 emptyRec = self.tabModel.record() #获取空记录,只有字段名 self.fldNum = {} #字段名与序号的字典 for i in range(emptyRec.count()): fieldName = emptyRec.fieldName(i) self.ui.comboFields.addItem(fieldName) self.fldNum.setdefault(fieldName) self.fldNum[fieldName] = i print(self.fldNum) def __openTable(self): ##打开数据表 self.tabModel = QSqlTableModel(self, self.DB) #数据模型 self.tabModel.setTable("employee") #设置数据表 self.tabModel.setEditStrategy(QSqlTableModel.OnManualSubmit ) #数据保存方式,OnManualSubmit , OnRowChange self.tabModel.setSort(self.tabModel.fieldIndex("empNo"), Qt.AscendingOrder) #排序 if (self.tabModel.select() == False): #查询数据失败 QMessageBox.critical( self, "错误信息", "打开数据表错误,错误信息\n" + self.tabModel.lastError().text()) return self.__getFieldNames() #获取字段名和序号 ##字段显示名 self.tabModel.setHeaderData(self.fldNum["empNo"], Qt.Horizontal, "工号") self.tabModel.setHeaderData(self.fldNum["Name"], Qt.Horizontal, "姓名") self.tabModel.setHeaderData(self.fldNum["Gender"], Qt.Horizontal, "性别") self.tabModel.setHeaderData(self.fldNum["Birthday"], Qt.Horizontal, "出生日期") self.tabModel.setHeaderData(self.fldNum["Province"], Qt.Horizontal, "省份") self.tabModel.setHeaderData(self.fldNum["Department"], Qt.Horizontal, "部门") self.tabModel.setHeaderData(self.fldNum["Salary"], Qt.Horizontal, "工资") self.tabModel.setHeaderData(self.fldNum["Memo"], Qt.Horizontal, "备注") #这两个字段不在tableView中显示 self.tabModel.setHeaderData(self.fldNum["Photo"], Qt.Horizontal, "照片") ## self.tabModel.setHeaderData(self.tabModel.fieldIndex("empNo"), Qt.Horizontal, "工号") ## self.tabModel.setHeaderData(self.tabModel.fieldIndex("Name"), Qt.Horizontal, "姓名") ## self.tabModel.setHeaderData(self.tabModel.fieldIndex("Gender"), Qt.Horizontal, "性别") ## self.tabModel.setHeaderData(self.tabModel.fieldIndex("Birthday"), Qt.Horizontal, "出生日期") ## self.tabModel.setHeaderData(self.tabModel.fieldIndex("Province"), Qt.Horizontal, "省份") ## self.tabModel.setHeaderData(self.tabModel.fieldIndex("Department"),Qt.Horizontal, "部门") ## self.tabModel.setHeaderData(self.tabModel.fieldIndex("Salary"), Qt.Horizontal, "工资") ## self.tabModel.setHeaderData(self.tabModel.fieldIndex("Memo"), Qt.Horizontal, "备注") #这两个字段不在tableView中显示 ## self.tabModel.setHeaderData(self.tabModel.fieldIndex("Photo"), Qt.Horizontal, "照片") ##创建界面组件与数据模型的字段之间的数据映射 self.mapper = QDataWidgetMapper() self.mapper.setModel(self.tabModel) #设置数据模型 self.mapper.setSubmitPolicy(QDataWidgetMapper.AutoSubmit) ##界面组件与tabModel的具体字段之间的联系 self.mapper.addMapping(self.ui.dbSpinEmpNo, self.fldNum["empNo"]) self.mapper.addMapping(self.ui.dbEditName, self.fldNum["Name"]) self.mapper.addMapping(self.ui.dbComboSex, self.fldNum["Gender"]) self.mapper.addMapping(self.ui.dbEditBirth, self.fldNum["Birthday"]) self.mapper.addMapping(self.ui.dbComboProvince, self.fldNum["Province"]) self.mapper.addMapping(self.ui.dbComboDep, self.fldNum["Department"]) self.mapper.addMapping(self.ui.dbSpinSalary, self.fldNum["Salary"]) self.mapper.addMapping(self.ui.dbEditMemo, self.fldNum["Memo"]) self.mapper.toFirst() #移动到首记录 self.selModel = QItemSelectionModel(self.tabModel) #选择模型 self.selModel.currentChanged.connect(self.do_currentChanged) #当前项变化时触发 self.selModel.currentRowChanged.connect( self.do_currentRowChanged) #选择行变化时 self.ui.tableView.setModel(self.tabModel) #设置数据模型 self.ui.tableView.setSelectionModel(self.selModel) #设置选择模型 self.ui.tableView.setColumnHidden(self.fldNum["Memo"], True) #隐藏列 self.ui.tableView.setColumnHidden(self.fldNum["Photo"], True) #隐藏列 ##tableView上为“性别”和“部门”两个字段设置自定义代理组件 strList = ("男", "女") self.__delegateSex = QmyComboBoxDelegate() self.__delegateSex.setItems(strList, False) self.ui.tableView.setItemDelegateForColumn( self.fldNum["Gender"], self.__delegateSex) #Combbox选择型 strList = ("销售部", "技术部", "生产部", "行政部") self.__delegateDepart = QmyComboBoxDelegate() self.__delegateDepart.setItems(strList, True) self.ui.tableView.setItemDelegateForColumn(self.fldNum["Department"], self.__delegateDepart) ##更新actions和界面组件的使能状态 self.ui.actOpenDB.setEnabled(False) self.ui.actRecAppend.setEnabled(True) self.ui.actRecInsert.setEnabled(True) self.ui.actRecDelete.setEnabled(True) self.ui.actScan.setEnabled(True) self.ui.groupBoxSort.setEnabled(True) self.ui.groupBoxFilter.setEnabled(True) ## ==========由connectSlotsByName() 自动连接的槽函数================== @pyqtSlot() ##选择数据库,打开数据表 def on_actOpenDB_triggered(self): dbFilename, flt = QFileDialog.getOpenFileName( self, "选择数据库文件", "", "SQL Lite数据库(*.db *.db3)") if (dbFilename == ''): return #打开数据库 self.DB = QSqlDatabase.addDatabase("QSQLITE") #添加 SQLITE数据库驱动 self.DB.setDatabaseName(dbFilename) #设置数据库名称 ## DB.setHostName() ## DB.setUserName() ## DB.setPassword() if self.DB.open(): #打开数据库 self.__openTable() #打开数据表 else: QMessageBox.warning(self, "错误", "打开数据库失败") @pyqtSlot() ##保存修改 def on_actSubmit_triggered(self): res = self.tabModel.submitAll() if (res == False): QMessageBox.information( self, "消息", "数据保存错误,错误信息\n" + self.tabModel.lastError().text()) else: self.ui.actSubmit.setEnabled(False) self.ui.actRevert.setEnabled(False) @pyqtSlot() ##取消修改 def on_actRevert_triggered(self): self.tabModel.revertAll() self.ui.actSubmit.setEnabled(False) self.ui.actRevert.setEnabled(False) @pyqtSlot() ##添加记录 def on_actRecAppend_triggered(self): self.tabModel.insertRow(self.tabModel.rowCount(), QModelIndex()) #在末尾添加一个记录 curIndex = self.tabModel.index(self.tabModel.rowCount() - 1, 1) #创建最后一行的ModelIndex self.selModel.clearSelection() #清空选择项 self.selModel.setCurrentIndex( curIndex, QItemSelectionModel.Select) #设置刚插入的行为当前选择行 currow = curIndex.row() #获得当前行 self.tabModel.setData(self.tabModel.index(currow, self.fldNum["empNo"]), 2000 + self.tabModel.rowCount()) #自动生成编号 self.tabModel.setData( self.tabModel.index(currow, self.fldNum["Gender"]), "男") @pyqtSlot() ##插入记录 def on_actRecInsert_triggered(self): curIndex = self.ui.tableView.currentIndex() #QModelIndex self.tabModel.insertRow(curIndex.row(), QModelIndex()) self.selModel.clearSelection() #清除已有选择 self.selModel.setCurrentIndex(curIndex, QItemSelectionModel.Select) @pyqtSlot() ##删除记录 def on_actRecDelete_triggered(self): curIndex = self.selModel.currentIndex() #获取当前选择单元格的模型索引 self.tabModel.removeRow(curIndex.row()) #删除当前行 @pyqtSlot() ##清除照片 def on_actPhotoClear_triggered(self): curRecNo = self.selModel.currentIndex().row() curRec = self.tabModel.record(curRecNo) #获取当前记录,QSqlRecord curRec.setNull("Photo") #设置为空值 self.tabModel.setRecord(curRecNo, curRec) self.ui.dbLabPhoto.clear() #清除界面上的图片显示 @pyqtSlot() ##设置照片 def on_actPhoto_triggered(self): fileName, filt = QFileDialog.getOpenFileName(self, "选择图片文件", "", "照片(*.jpg)") if (fileName == ''): return file = QFile(fileName) #fileName为图片文件名 file.open(QIODevice.ReadOnly) try: data = file.readAll() #QByteArray finally: file.close() curRecNo = self.selModel.currentIndex().row() curRec = self.tabModel.record(curRecNo) #获取当前记录QSqlRecord curRec.setValue("Photo", data) #设置字段数据 self.tabModel.setRecord(curRecNo, curRec) pic = QPixmap() pic.loadFromData(data) W = self.ui.dbLabPhoto.width() self.ui.dbLabPhoto.setPixmap(pic.scaledToWidth(W)) #在界面上显示 @pyqtSlot() ##涨工资,遍历数据表所有记录 def on_actScan_triggered(self): if (self.tabModel.rowCount() == 0): return for i in range(self.tabModel.rowCount()): aRec = self.tabModel.record(i) #获取当前记录 ## salary=aRec.value("Salary").toFloat() #错误,无需再使用toFloat()函数 salary = aRec.value("Salary") salary = salary * 1.1 aRec.setValue("Salary", salary) self.tabModel.setRecord(i, aRec) if (self.tabModel.submitAll()): QMessageBox.information(self, "消息", "涨工资计算完毕") @pyqtSlot(int) ##排序字段变化 def on_comboFields_currentIndexChanged(self, index): if self.ui.radioBtnAscend.isChecked(): self.tabModel.setSort(index, Qt.AscendingOrder) else: self.tabModel.setSort(index, Qt.DescendingOrder) self.tabModel.select() @pyqtSlot() ##升序 def on_radioBtnAscend_clicked(self): self.tabModel.setSort(self.ui.comboFields.currentIndex(), Qt.AscendingOrder) self.tabModel.select() @pyqtSlot() ##降序 def on_radioBtnDescend_clicked(self): self.tabModel.setSort(self.ui.comboFields.currentIndex(), Qt.DescendingOrder) self.tabModel.select() @pyqtSlot() ##过滤,男 def on_radioBtnMan_clicked(self): self.tabModel.setFilter("Gender='男'") ## print(self.tabModel.filter()) ## self.tabModel.select() @pyqtSlot() ##数据过滤,女 def on_radioBtnWoman_clicked(self): self.tabModel.setFilter("Gender='女' ") ## print(self.tabModel.filter()) ## self.tabModel.select() @pyqtSlot() ##取消数据过滤 def on_radioBtnBoth_clicked(self): self.tabModel.setFilter("") ## print(self.tabModel.filter()) ## self.tabModel.select() ## =============自定义槽函数=============================== def do_currentChanged(self, current, previous): ##更新actPost和actCancel 的状态 self.ui.actSubmit.setEnabled(self.tabModel.isDirty()) #有未保存修改时可用 self.ui.actRevert.setEnabled(self.tabModel.isDirty()) def do_currentRowChanged(self, current, previous): #行切换时的状态控制 self.ui.actRecDelete.setEnabled(current.isValid()) self.ui.actPhoto.setEnabled(current.isValid()) self.ui.actPhotoClear.setEnabled(current.isValid()) if (current.isValid() == False): self.ui.dbLabPhoto.clear() #清除图片显示 return self.mapper.setCurrentIndex(current.row()) #更新数据映射的行号 curRec = self.tabModel.record(current.row()) #获取当前记录,QSqlRecord类型 if (curRec.isNull("Photo")): #图片字段内容为空 self.ui.dbLabPhoto.clear() else: ## data=bytearray(curRec.value("Photo")) #可以工作 data = curRec.value("Photo") # 也可以工作 pic = QPixmap() pic.loadFromData(data) W = self.ui.dbLabPhoto.size().width() self.ui.dbLabPhoto.setPixmap(pic.scaledToWidth(W))
class QmyMainWindow(QMainWindow): def __init__(self, parent=None, dbFilename='None'): super().__init__(parent) # Call the parent class. Create window. self.ui = Ui_MainWindow() # Create UI object self.ui.setupUi(self) # Create UI interface self.ui.tabWidget.setVisible(True) self.setCentralWidget(self.ui.tabWidget) # tableView setting self.ui.tableView.setSelectionBehavior(QAbstractItemView.SelectItems) self.ui.tableView.setSelectionMode(QAbstractItemView.SingleSelection) self.ui.tableView.setAlternatingRowColors(True) self.ui.tableView.verticalHeader().setDefaultSectionSize(44) self.ui.tableView.horizontalHeader().setDefaultSectionSize(70) self.ui.tableView.setSortingEnabled(True) # Initialize chart self.__iniPieChart() # Initialize pie chart self.__iniStackedBar() # Stacked bar mpl.rcParams['font.sans-serif'] = ['Calibri'] mpl.rcParams['font.size'] = 8 # Choose the SQLITE database drive self.DB = QSqlDatabase.addDatabase("QSQLITE") self.DB.setDatabaseName(dbFilename) # Set the name of database if self.DB.open(): # Open database self.__openTable() # Open tables else: QMessageBox.warning(self, "Warning", "Failed to open the database.") # ==============Self-defined Fuctions============ def __getFieldNames(self): # Get names of all fields. # Get empty records, only field name. emptyRec = self.tabModel.record() self.fldNum = {} # Dictionary of field name and index. for i in range(emptyRec.count()): fieldName = emptyRec.fieldName(i) self.fldNum.setdefault(fieldName) self.fldNum[fieldName] = i def __openTable(self): # Open the table of database self.tabModel = QSqlTableModel(self, self.DB) # Data model # Set the table of database TODO: Can user insert their own tables from # the database? self.tabModel.setTable("battery") # Data storage,OnManualSubmit , OnRowChange self.tabModel.setEditStrategy(QSqlTableModel.OnManualSubmit) # self.tabModel.setSort( # self.tabModel.fieldIndex("RANDOM"), # Qt.DescendingOrder) # Sorting if (self.tabModel.select() == False): # Failed to query the data QMessageBox.critical( self, "Wrong", "Something wrong. Failed to open the database\n" + self.tabModel.lastError().text()) return # self.tabModel.setFilter("NUM_RECORDS LIKE 'NONE'") self.__getFieldNames() # Get the field name and index # Field name shown for i in self.fldNum: self.tabModel.setHeaderData(self.fldNum[i], Qt.Horizontal, i.capitalize()) # Create mappings between interface widget and the field name of data model self.mapper = QDataWidgetMapper() self.mapper.setModel(self.tabModel) # Setting data model self.mapper.setSubmitPolicy(QDataWidgetMapper.AutoSubmit) # The relations between interface widget and field name of tabModel self.mapper.addMapping(self.ui.dbEditValue, self.fldNum["Value"]) self.mapper.addMapping(self.ui.dbEditRunit, self.fldNum["Raw_unit"]) self.mapper.addMapping(self.ui.dbComboProperty, self.fldNum["Property"]) self.mapper.addMapping(self.ui.dbEditUnit, self.fldNum["Unit"]) self.mapper.addMapping(self.ui.dbEditName, self.fldNum["Name"]) self.mapper.addMapping(self.ui.dbEditEname, self.fldNum["Extracted_name"]) self.mapper.addMapping(self.ui.dbEditRvalue, self.fldNum["Raw_value"]) self.mapper.addMapping(self.ui.dbEditDOI_2, self.fldNum["DOI"]) self.mapper.addMapping(self.ui.dbEditDOI_4, self.fldNum["Date"]) self.mapper.addMapping(self.ui.dbEditDOI_3, self.fldNum["Title"]) self.mapper.addMapping(self.ui.dbEditDOI, self.fldNum["Journal"]) self.mapper.addMapping(self.ui.dbEditTag, self.fldNum["Tag"]) self.mapper.addMapping(self.ui.dbEditInfo, self.fldNum["Info"]) self.mapper.addMapping(self.ui.dbEditType, self.fldNum["Type"]) self.mapper.addMapping(self.ui.dbEditWarning, self.fldNum["Warning"]) self.mapper.addMapping(self.ui.dbEditSpecifier, self.fldNum["Specifier"]) self.mapper.toFirst() # Move to the first record self.selModel = QItemSelectionModel(self.tabModel) # Select model self.selModel.currentChanged.connect( self.do_currentChanged) # Trigger when the current changed self.selModel.currentRowChanged.connect( self.do_currentRowChanged) # Trigger when the current row changed self.ui.tableView.setModel(self.tabModel) # Setting the data model self.ui.tableView.setSelectionModel( self.selModel) # Setting the selection model # self.ui.tableView.setColumnHidden(self.fldNum["TAG"], True) #Hide columns # self.ui.tableView.setColumnHidden(self.fldNum["INFO"], True) #Hide # columns self.ui.tableView.setColumnHidden(self.fldNum["Extracted_name"], True) # Hide columns self.ui.tableView.setColumnHidden(self.fldNum["Unit"], True) # Hide columns self.ui.tableView.setColumnHidden(self.fldNum["Num_records"], True) # Hide columns # self.ui.tableView.setColumnHidden( # self.fldNum["RAW_VALUE"], True) # Hide columns # Update the conditions of actions of interface widget self.ui.actRecAppend.setEnabled(True) self.ui.actRecInsert.setEnabled(True) self.ui.actRecDelete.setEnabled(True) self.ui.btnDrawPieChart.setEnabled(True) # Pie Chart self.ui.spinPieSize.setEnabled(True) self.ui.spinHoleSize.setEnabled(True) self.ui.chkBox_PieLegend.setEnabled(True) self.ui.generateButton_3.setEnabled(True) self.ui.searchButton_3.setEnabled(True) self.ui.clearButton_3.setEnabled(True) self.ui.frame_4.setEnabled(True) self.ui.frame_3.setEnabled(True) self.ui.searchInput.returnPressed.connect(self.ui.searchButton.click) self.ui.searchInput_3.returnPressed.connect( self.ui.searchButton_3.click) def __iniPieChart(self): # Initialize pie chart chart = QChart() # chart.setTitle("Piechart") chart.setAnimationOptions(QChart.SeriesAnimations) chart.setTheme(QChart.ChartTheme(0)) self.ui.chartViewPie.setChart(chart) # Setting chart for chart view self.ui.chartViewPie.setRenderHint(QPainter.Antialiasing) self.ui.chartViewPie.setCursor(Qt.CrossCursor) # Setting cross cursor def __iniStackedBar(self): # Initialize stacked bar chart chart = QChart() # chart.setTitle("Number of property records for each chemical") chart.setAnimationOptions(QChart.SeriesAnimations) chart.setTheme(QChart.ChartTheme(0)) self.ui.chartViewStackedBar.setChart(chart) # Set chart self.ui.chartViewStackedBar.setRenderHint(QPainter.Antialiasing) self.ui.chartViewStackedBar.setCursor(Qt.CrossCursor) # Set mouse # ==========Table tab slot function================== @pyqtSlot() # Save changes def on_actSubmit_triggered(self): res = self.tabModel.submitAll() if (res == False): QMessageBox.information( self, "Information", "Failed to store the changes. Wrong information. \n" + self.tabModel.lastError().text()) else: self.ui.actSubmit.setEnabled(False) self.ui.actRevert.setEnabled(False) @pyqtSlot() # Cancel changes def on_actRevert_triggered(self): self.tabModel.revertAll() self.ui.actSubmit.setEnabled(False) self.ui.actRevert.setEnabled(False) @pyqtSlot() # Add records def on_actRecAppend_triggered(self): self.tabModel.insertRow( self.tabModel.rowCount(), QModelIndex()) # Add one record in the last row curIndex = self.tabModel.index(self.tabModel.rowCount() - 1, 1) # Create ModelIndex of the last row self.selModel.clearSelection() # Clear selections # Choosing the current row when selection. self.selModel.setCurrentIndex(curIndex, QItemSelectionModel.Select) currow = curIndex.row() # Get current row @pyqtSlot() # Insert records def on_actRecInsert_triggered(self): curIndex = self.ui.tableView.currentIndex() # QModelIndex self.tabModel.insertRow(curIndex.row(), QModelIndex()) self.selModel.clearSelection() # Clear selections self.selModel.setCurrentIndex(curIndex, QItemSelectionModel.Select) @pyqtSlot() # Delete records def on_actRecDelete_triggered(self): # Get the current index of current model curIndex = self.selModel.currentIndex() self.tabModel.removeRow(curIndex.row(), QModelIndex()) # Delete the current row self.tabModel.submit() @pyqtSlot() # Help message box def on_actHelp_triggered(self): msg = QMessageBox() msg.about( self, "Help", '<div>' '<h3>Table: Query the database according to data types and name or DOI.</h3>' '<ul>' '<li>Search the exact compound name in "<em>Exact Match</em>". Search the element or part of compound name in "<em>Generic Match</em>". </li>' '<li>Click the "<em>Home</em>" or "<em>All</em>" button to view the full database</li>' '<li>Refer to "<em>Correctness</em>" column for database evaluation details. </li>' '<li>You can add your own database entries using the "<em>Insert</em>" tab; click "<em>Save</em>" to save the changes.</li>' '</ul>' '<h3>Figure: Statistical analysis of the database.</h3>' '<ul>' '<li><em>Pie chart</em> shows the proportion of data records.</li>' '<li><em>Stacked bar chart</em> shows the data types for each compound. First input the compound name, click "<em>Add</em>" and then "<em>Generate</em>" data for overview. </li>' '<li><em>Histogram </em>shows the distribution of each data type.</li>' '<li><em>Venn diagram</em> shows the correlation of each data type.</li>' '</ul>' '</div>') @pyqtSlot() # Filtering def on_radioBtnVoltage_clicked(self): flag, sqlmerge = self.merged_or_not() print(sqlmerge) self.tabModel.setFilter("PROPERTY LIKE 'Voltage' AND %s" % sqlmerge) @pyqtSlot() # Filtering def on_radioBtnCoulombic_clicked(self): flag, sqlmerge = self.merged_or_not() self.tabModel.setFilter("PROPERTY LIKE 'Coulombic Efficiency' AND %s" % sqlmerge) @pyqtSlot() # Filtering def on_radioBtnConductivity_clicked(self): flag, sqlmerge = self.merged_or_not() self.tabModel.setFilter("PROPERTY LIKE 'Conductivity' AND %s" % sqlmerge) @pyqtSlot() # Filetering def on_radioBtnCapacity_clicked(self): flag, sqlmerge = self.merged_or_not() self.tabModel.setFilter("PROPERTY LIKE 'Capacity' AND %s" % sqlmerge) @pyqtSlot() # Filtering def on_radioBtnEnergy_clicked(self): flag, sqlmerge = self.merged_or_not() self.tabModel.setFilter("PROPERTY LIKE 'Energy' AND %s" % sqlmerge) @pyqtSlot() # Cancel Filetering def on_radioBtnAll_clicked(self): flag, sqlmerge = self.merged_or_not() self.tabModel.setFilter("%s" % sqlmerge) # print(self.tabModel.filter()) # self.tabModel.select() def get_elements(self): element_dic = { "Hydrogen": "H", "Helium": "He", "Lithium": "Li", "Beryllium": "Be", "Boron": "B", "Carbon": "C", "Nitrogen": "N", "Oxygen": "O", "Fluorine": "F", "Neon": "Ne", "Sodium": "Na", "Magnesium": "Mg", "Aluminum": "Al", "Silicon": "Si", "Phosphorus": "P", "Sulfur": "S", "Chlorine": "Cl", "Argon": "Ar", "Potassium": "K", "Calcium": "Ca", "Scandium": "Sc", "Titanium": "Ti", "Vanadium": "V", "Chromium": "Cr", "Manganese": "Mn", "Iron": "Fe", "Cobalt": "Co", "Nickel": "Ni", "Copper": "Cu", "Zinc": "Zn", "Gallium": "Ga", "Germanium": "Ge", "Arsenic": "As", "Selenium": "Se", "Bromine": "Br", "Krypton": "Kr", "Rubidium": "Rb", "Strontium": "Sr", "Yttrium": "Y", "Zirconium": "Zr", "Niobium": "Nb", "Molybdenum": "Mo", "Technetium": "Tc", "Ruthenium": "Ru", "Rhodium": "Rh", "Palladium": "Pd", "Silver": "Ag", "Cadmium": "Cd", "Indium": "In", "Tin": "Sn", "Antimony": "Sb", "Tellurium": "Te", "Iodine": "I", "Xenon": "Xe", "Cesium": "Cs", "Barium": "Ba", "Lanthanum": "La", "Cerium": "Ce", "Praseodymium": "Pr", "Neodymium": "Nd", "Promethium": "Pm", "Samarium": "Sm", "Europium": "Eu", "Gadolinium": "Gd", "Terbium": "Tb", "Dysprosium": "Dy", "Holmium": "Ho", "Erbium": "Er", "Thulium": "Tm", "Ytterbium": "Yb", "Lutetium": "Lu", "Hafnium": "Hf", "Tantalum": "Ta", "Tungsten": "W", "Rhenium": "Re", "Osmium": "Os", "Iridium": "Ir", "Platinum": "Pt", "Gold": "Au", "Mercury": "Hg", "Thallium": "Tl", "Lead": "Pb", "Bismuth": "Bi", "Polonium": "Po", "Astatine": "At", "Radon": "Rn", "Francium": "Fr", "Radium": "Ra", "Actinium": "Ac", "Thorium": "Th", "Protactinium": "Pa", "Uranium": "U", "Neptunium": "Np", "Plutonium": "Pu", "Americium": "Am", "Curium": "Cm", "Berkelium": "Bk", "Californium": "Cf", "Einsteinium": "Es", "Fermium": "Fm", "Mendelevium": "Md", "Nobelium": "No", "Lawrencium": "Lr", "Rutherfordium": "Rf", "Dubnium": "Db", "Seaborgium": "Sg", "Bohrium": "Bh", "Hassium": "Hs", "Meitnerium": "Mt", "Darmstadtium": "Ds", "Roentgenium": "Rg", "Copernicium": "Cn", "Nihonium": "Nh", "Flerovium": "Fl", "Moscovium": "Mc", "Livermorium": "Lv", "Tennessine": "Ts", "Oganesson": "Og" } return element_dic def merged_or_not(self): flag = self.ui.mergeBox.isChecked() if flag: sqlmerge = "NUM_RECORDS NOT LIKE 'NONE'" else: sqlmerge = "NUM_RECORDS LIKE 'NONE'" return flag, sqlmerge @pyqtSlot() def on_homeButton_clicked(self): self.tabModel.setFilter("NUM_RECORDS LIKE 'NONE'") self.ui.mergeBox.setChecked(False) @pyqtSlot() def on_mergeButton_clicked(self): self.tabModel.setFilter("NUM_RECORDS NOT LIKE 'NONE'") self.ui.mergeBox.setChecked(True) @pyqtSlot() def on_searchButton_clicked(self): searchtext = self.ui.searchInput.text() searchclass = self.ui.searchClass.currentText() matchtype = self.ui.matchType.currentText() flag, sqlmerge = self.merged_or_not() if searchclass == "DOI": if matchtype == "Exact Match": self.tabModel.setFilter("DOI LIKE '%s' AND %s" % (searchtext, sqlmerge)) elif matchtype == "Generic Match": self.tabModel.setFilter("DOI LIKE '%%%s%%' AND %s" % (searchtext, sqlmerge)) if self.tabModel.rowCount() == 0: self.tabModel.setFilter("") QMessageBox.warning( self, "Warning", "No such DOIs in the database. Please search new DOI.") elif searchclass == "Warning": if matchtype == "Exact Match": self.tabModel.setFilter("WARNING LIKE '%s' AND %s" % (searchtext, sqlmerge)) elif matchtype == "Generic Match": self.tabModel.setFilter("WARNING LIKE '%%%s%%' AND %s" % (searchtext, sqlmerge)) if self.tabModel.rowCount() == 0: self.tabModel.setFilter("") QMessageBox.warning( self, "Warning", "No such DOIs in the database. Please search new DOI.") elif searchclass == 'Name': try: searchtext = self.get_elements()[searchtext.capitalize()] except BaseException: pass if matchtype == "Exact Match": if self.ui.radioBtnAll.isChecked(): self.tabModel.setFilter("%s LIKE '%s' " % (searchclass, searchtext)) elif self.ui.radioBtnVoltage.isChecked(): self.tabModel.setFilter( "%s LIKE '%s' AND PROPERTY LIKE 'VOLTAGE' AND %s" % (searchclass, searchtext, sqlmerge)) elif self.ui.radioBtnCapacity.isChecked(): self.tabModel.setFilter( "%s LIKE '%s' AND PROPERTY LIKE 'CAPACITY' AND %s" % (searchclass, searchtext, sqlmerge)) elif self.ui.radioBtnConductivity.isChecked(): self.tabModel.setFilter( "%s LIKE '%s' AND PROPERTY LIKE 'CONDUCTIVITY' AND %s" % (searchclass, searchtext, sqlmerge)) elif self.ui.radioBtnCoulombic.isChecked(): self.tabModel.setFilter( "%s LIKE '%s' AND PROPERTY LIKE 'COULOMBIC EFFICIENCY' AND %s " % (searchclass, searchtext, sqlmerge)) elif self.ui.radioBtnEnergy.isChecked(): self.tabModel.setFilter( "%s LIKE '%s' AND PROPERTY LIKE 'ENERGY'AND %s " % (searchclass, searchtext, sqlmerge)) elif matchtype == "Generic Match": if self.ui.radioBtnAll.isChecked(): self.tabModel.setFilter( "(EXTRACTED_NAME LIKE '%%''%s''%%' OR NAME LIKE '%s') AND %s" % (searchtext, searchtext, sqlmerge)) elif self.ui.radioBtnVoltage.isChecked(): self.tabModel.setFilter( "EXTRACTED_NAME LIKE '%%''%s''%%' OR NAME LIKE '%s' AND %s" % (searchtext, searchtext, sqlmerge)) elif self.ui.radioBtnCapacity.isChecked(): self.tabModel.setFilter( "EXTRACTED_NAME LIKE '%%''%s''%%' OR NAME LIKE '%s' AND %s" % (searchtext, searchtext, sqlmerge)) elif self.ui.radioBtnConductivity.isChecked(): self.tabModel.setFilter( "EXTRACTED_NAME LIKE '%%''%s''%%' OR NAME LIKE '%s' AND %s" % (searchtext, searchtext, sqlmerge)) elif self.ui.radioBtnCoulombic.isChecked(): self.tabModel.setFilter( "EXTRACTED_NAME LIKE '%%''%s''%%' OR NAME LIKE '%s' AND %s" % (searchtext, searchtext, sqlmerge)) elif self.ui.radioBtnEnergy.isChecked(): self.tabModel.setFilter( "EXTRACTED_NAME LIKE '%%''%s''%%' OR NAME LIKE '%s' AND %s" % (searchtext, searchtext, sqlmerge)) if self.tabModel.rowCount() == 0: self.tabModel.setFilter("") QMessageBox.warning( self, "Warning", "No such compounds in the database. Please search new compounds." ) # ============Picture Tab 1, Pie Chart===================== @pyqtSlot() # Draw the pie chart def on_btnDrawPieChart_clicked(self): self.draw_pieChart() @pyqtSlot(float) # Set holeSize def on_spinHoleSize_valueChanged(self, arg1): seriesPie = self.ui.chartViewPie.chart().series()[0] seriesPie.setHoleSize(arg1) @pyqtSlot(float) # Set pieSize def on_spinPieSize_valueChanged(self, arg1): seriesPie = self.ui.chartViewPie.chart().series()[0] seriesPie.setPieSize(arg1) @pyqtSlot(bool) # Set legend checkbox def on_chkBox_PieLegend_clicked(self, checked): self.ui.chartViewPie.chart().legend().setVisible(checked) def pie_data( self): # Return a list of property name and the number of property num_list = [] pro_list = [ "CAPACITY", "CONDUCTIVITY", "COULOMBIC EFFICIENCY", "ENERGY", "VOLTAGE" ] for i in range(len(pro_list)): query = QSqlQuery( db=self.DB, query= "SELECT COUNT(NAME) FROM BATTERY WHERE PROPERTY LIKE '%s' AND NUM_RECORDS LIKE 'NONE'" % pro_list[i]) # Query database while query.next(): num_value = query.value(0) # Returned value for each query item = self.ui.treeWidget_2.topLevelItem(i) # The ith row # The 2nd column item.setText(1, str(num_value)) item.setTextAlignment(1, Qt.AlignHCenter) num_list.append(num_value) return pro_list, num_list def draw_pieChart(self): # Draw the pie chart chart = self.ui.chartViewPie.chart() chart.legend().setAlignment(Qt.AlignRight) # AlignRight,AlignBottom chart.removeAllSeries() # Delete all series seriesPie = QPieSeries() # Pie chart series seriesPie.setHoleSize(self.ui.spinHoleSize.value()) # Hole size seriesPie.setPieSize(self.ui.spinPieSize.value()) # Pie size sec_count = 5 # Number of properties seriesPie.setLabelsVisible(True) # Label pro, num = self.pie_data() for i in range(sec_count): seriesPie.append(pro[i], num[i]) seriesPie.setLabelsVisible(True) # Label # Pie hoverd when mouse selected seriesPie.hovered.connect(self.do_pieHovered) chart.addSeries(seriesPie) chart.setTitle("Proportion of data records for each property") font = QFont() font.setPointSize(12) font.setWeight(75) chart.setTitleFont(font) font = QFont() font.setPointSize(12) font.setBold(False) font.setWeight(35) legend = chart.legend() legend.setFont(font) # =========Picture tab 2. StackedBar========= @pyqtSlot() # Draw StackedBar def on_btnStackedBar_clicked(self): self.draw_stackedBar() @pyqtSlot() # Draw horizontal StackedBar def on_btnStackedBarH_clicked(self): self.draw_stackedBar(False) # Search button in the Stacked bar chart tab. Add name to the first column # of the table. @pyqtSlot() def on_searchButton_3_clicked(self): searchtext = self.ui.searchInput_3.text() current_index = self.ui.stackedWidget.topLevelItemCount() item_0 = QtWidgets.QTreeWidgetItem(self.ui.stackedWidget) item_0.setText(0, searchtext) @pyqtSlot() # Generate data using the input chemical names def on_generateButton_3_clicked(self): self.ui.zoominButton.setEnabled(True) self.ui.zoomoutButton.setEnabled(True) self.ui.originalButton.setEnabled(True) self.ui.btnStackedBar.setEnabled(True) # Stacked bar chart self.ui.btnStackedBarH.setEnabled(True) current_index = self.ui.stackedWidget.topLevelItemCount() chemical_list = [ self.ui.stackedWidget.topLevelItem(index).text(0) for index in range(current_index) ] # Get a list of the inputed chemical name pro_list = [ "CAPACITY", "CONDUCTIVITY", "COULOMBIC EFFICIENCY", "ENERGY", "VOLTAGE" ] for row, chemical in enumerate(chemical_list): for index, pro in enumerate(pro_list): query = QSqlQuery( db=self.DB, query= "SELECT SUM(NUM_RECORDS) FROM BATTERY WHERE PROPERTY LIKE '%s' AND NAME LIKE '%s' " % (pro, chemical)) while query.next(): num_value = query.value(0) print(num_value) if num_value == "": num_value = 0 item = self.ui.stackedWidget.topLevelItem( row) # The row'th row item.setText(index + 1, str(num_value)) item.setTextAlignment(index + 1, Qt.AlignHCenter) @pyqtSlot() # Clear button def on_clearButton_3_clicked(self): self.ui.stackedWidget.clear() @pyqtSlot() # Zoom in def on_zoominButton_clicked(self): self.ui.chartViewStackedBar.chart().zoom(1.2) @pyqtSlot() # Zoom out def on_zoomoutButton_clicked(self): self.ui.chartViewStackedBar.chart().zoom(0.8) @pyqtSlot() # Reset original size def on_originalButton_clicked(self): self.ui.chartViewStackedBar.chart().zoomReset() def draw_stackedBar(self, isVertical=True): # Stacked bar chart chart = self.ui.chartViewStackedBar.chart() chart.removeAllSeries() # Remove all series chart.removeAxis(chart.axisX()) # remove axis chart.removeAxis(chart.axisY()) if isVertical: # Vertical chart.setTitle("Number of property records for each chemical ") chart.legend().setAlignment(Qt.AlignBottom) else: # Horizontal chart.setTitle("Number of property records for each chemical") chart.legend().setAlignment(Qt.AlignRight) # Create data sets setCapacity = QBarSet("Capacity") setConductivity = QBarSet("Conductivity") setCoulombic = QBarSet("Coulombic") setEnergy = QBarSet("Energy") setVoltage = QBarSet("Voltage") chemical_Count = self.ui.stackedWidget.topLevelItemCount() nameList = [] # Chemical lists for i in range(chemical_Count): item = self.ui.stackedWidget.topLevelItem(i) # print(item.text(1)) nameList.append(item.text(0)) setCapacity.append(float(item.text(1))) setConductivity.append(float(item.text(2))) setCoulombic.append(float(item.text(3))) setEnergy.append(float(item.text(4))) setVoltage.append(float(item.text(5))) # Create series if isVertical: seriesBar = QStackedBarSeries() else: seriesBar = QHorizontalStackedBarSeries() seriesBar.append(setCapacity) seriesBar.append(setConductivity) seriesBar.append(setCoulombic) seriesBar.append(setEnergy) seriesBar.append(setVoltage) seriesBar.setLabelsVisible(True) # Show labels for each bar seriesBar.setLabelsFormat("@value") seriesBar.setLabelsPosition(QAbstractBarSeries.LabelsCenter) seriesBar.setBarWidth(0.3) chart.addSeries(seriesBar) axisStud = QBarCategoryAxis() # Category axis axisStud.append(nameList) axisStud.setRange(nameList[0], nameList[chemical_Count - 1]) axisValue = QValueAxis() # Value axis # axisValue.setRange(0, 300) axisValue.setTitleText("Number of records") # axisValue.setTickCount(6) axisValue.applyNiceNumbers() if isVertical: chart.setAxisX(axisStud, seriesBar) chart.setAxisY(axisValue, seriesBar) else: chart.setAxisY(axisStud, seriesBar) chart.setAxisX(axisValue, seriesBar) for marker in chart.legend().markers(): # QLegendMarker lists marker.clicked.connect(self.do_LegendMarkerClicked) # =========Picture tab 3. Histogram========= @pyqtSlot(bool) # Show toolbar def on_gBoxHist_toolbar_2_clicked(self, checked): self.ui.widgetHist_2.setToolbarVisible(checked) @pyqtSlot() # Draw def on_histButton_clicked(self): self.__drawHist() def hist_data(self, pro): query = QSqlQuery( db=self.DB, query= "SELECT VALUE FROM BATTERY WHERE PROPERTY LIKE '%s' AND NUM_RECORDS LIKE 'NONE'" % pro) data = [] while query.next(): num_value = query.value(0) data.append(num_value) return data def __drawHist(self): # Histogram pro = self.ui.propertycomboBox_2.currentText() data = self.hist_data(pro) self.ui.widgetHist_2.figure.clear() # Clear figure ax = self.ui.widgetHist_2.figure.add_subplot(1, 1, 1) if pro == 'Capacity': M, bins, patches = ax.hist( [5000 if float(i) > 4999 else float(i) for i in data], bins='auto', color='darkgreen', alpha=0.5, rwidth=1) ax.set_xlim(0, 5001) # ax.set_ylim(0,15000) ax.tick_params(labelsize=12) ax.set_xticklabels(['0', '1000', '2000', '3000', '4000', '5000+']) ax.set_xlabel('Capacity (mAh/g)', fontsize=14) ax.set_ylabel('Frequency', fontsize=14) ax.set_title('Battery Capacity Distrbution', fontsize=14) ax.figure.canvas.draw() elif pro == "Voltage": count1 = [] for i in data: count1.append(float(i)) n, bins, patches = ax.hist(x=count1, bins='auto', range=(0, 8), color='r', alpha=0.5, rwidth=1) ax.set_xticklabels(['0', '1', '2', '3', '4', '5', '6', '7', '8+']) ax.set_xlim(0, 8) ax.tick_params(labelsize=12) ax.set_xlabel('Voltage (V)', fontsize=14) ax.set_ylabel('Frequency', fontsize=14) ax.set_title('Battery Voltage Distrbution', fontsize=14) ax.figure.canvas.draw() elif pro == "Energy": n, bins, patches = ax.hist( [3000 if float(i) > 2999 else float(i) for i in data], bins='auto', color='y', alpha=0.5, rwidth=1) ax.set_xticklabels( ['0', '500', '1000', '1500', '2000', '2500', '3000+']) ax.set_xlim(0, 3001) ax.tick_params(labelsize=12) ax.set_xlabel('Energy (Wh/kg)', fontsize=14) ax.set_ylabel('Frequency', fontsize=14) ax.set_title('Battery Energy Distrbution', fontsize=14) ax.figure.canvas.draw() elif pro == "Coulombic Efficiency": dataplot = [float(i) for i in data] n, bins, patches = ax.hist(x=dataplot, bins='auto', color='c', alpha=0.5, rwidth=1) ax.set_xlim(0, 100) ax.tick_params(labelsize=12) ax.set_xlabel('Coulombic Effciency (%)', fontsize=14) ax.set_ylabel('Frequency', fontsize=14) ax.set_title('Battery Coulombic Effciency Distrbution', fontsize=14) ax.figure.canvas.draw() elif pro == "Conductivity": dataplot = [float(i) for i in data] n, bins, patches = ax.hist(x=dataplot, bins=np.logspace( np.log10(1e-15), np.log10(1)), color='k', alpha=0.5, rwidth=1) ax.set_xlim(1e-20, 10) ax.set_xscale('log') ax.tick_params(labelsize=12) ax.set_xlabel('log10 (Conductivity (S/cm))', fontsize=14) ax.set_ylabel('Frequency', fontsize=14) ax.set_title('Battery Conductivity Distrbution', fontsize=14) ax.figure.canvas.draw() # =========Picture tab 4. Venn diagram========= @pyqtSlot() # Draw def on_btnVenn_clicked(self): self.__drawVenn() @pyqtSlot(bool) # Show toolbar def on_gBoxHist_toolbar_3_clicked(self, checked): self.ui.widgetVenn.setToolbarVisible(checked) def __drawVenn(self): self.ui.widgetVenn.figure.clear() # Clear figure pro_list = [ "Capacity", "Conductivity", "Coulombic Efficiency", "Energy", "Voltage" ] data_dic = {} color_dic = dict(zip(pro_list, ['C0', 'C2', 'C6', 'C8', 'C9'])) for i in pro_list: query = QSqlQuery( db=self.DB, query= "SELECT COUNT(DISTINCT NAME) FROM BATTERY WHERE PROPERTY LIKE '%s' AND NUM_RECORDS LIKE 'NONE'" % i) while query.next(): num_value = query.value(0) data_dic[i] = num_value for i, combo in enumerate(itertools.combinations(pro_list, 2)): query = QSqlQuery( db=self.DB, query= "SELECT COUNT() FROM (SELECT DISTINCT NAME AS PRO1 FROM BATTERY WHERE PROPERTY LIKE '%s'AND NUM_RECORDS LIKE 'NONE') INNER JOIN (SELECT DISTINCT NAME AS PRO2 FROM BATTERY WHERE PROPERTY LIKE '%s'AND NUM_RECORDS LIKE 'NONE') ON PRO1 = PRO2" % combo) while query.next(): num = query.value(0) x3 = num x1 = data_dic[combo[0]] - x3 x2 = data_dic[combo[1]] - x3 hf = self.ui.widgetVenn.figure # hf.set_figheight(30) # hf.set_figwidth(30) # print(dir(hf)) hf.set_size_inches((10, 10)) ax1 = hf.add_subplot(5, 2, i + 1) v = venn2(subsets=(x1, x2, x3), set_labels=(combo[0], combo[1]), ax=ax1) v.get_patch_by_id('A').set_alpha(1) v.get_patch_by_id('A').set_color(color_dic[combo[0]]) v.get_patch_by_id('B').set_color(color_dic[combo[1]]) ax1.figure.canvas.draw() # =============Self-defined slot function=============================== # Update the conditions of actPost and actCancel def do_currentChanged(self, current, previous): self.ui.actSubmit.setEnabled( self.tabModel.isDirty()) # Use when not saving changes self.ui.actRevert.setEnabled(self.tabModel.isDirty()) def do_currentRowChanged(self, current, previous): # Control during row changes self.ui.actRecDelete.setEnabled(current.isValid()) # Update current row index of mapping self.mapper.setCurrentIndex(current.row()) # Get current record,QSqlRecord curRec = self.tabModel.record(current.row()) def do_pieHovered(self, pieSlice, state): # Mouse move in and out in the pie chart pieSlice.setExploded(state) # Pop-up animation if state: # Show the tab of percentages self.__oldLabel = pieSlice.label() # Save original labels pieSlice.setLabel(self.__oldLabel + ": %.1f%%" % (pieSlice.percentage() * 100)) font = QFont() font.setPointSize(10) font.setBold(False) font.setWeight(25) pieSlice.setLabelFont(font) else: # show original labels pieSlice.setLabel(self.__oldLabel) font = QFont() font.setPointSize(10) font.setBold(False) font.setWeight(25) pieSlice.setLabelFont(font) def do_LegendMarkerClicked(self): # Click legend marker marker = self.sender() # QLegendMarker marker.series().setVisible(not marker.series().isVisible()) marker.setVisible(True) alpha = 1.0 if not marker.series().isVisible(): alpha = 0.5 brush = marker.labelBrush() # QBrush color = brush.color() # QColor color.setAlphaF(alpha) brush.setColor(color) marker.setLabelBrush(brush) brush = marker.brush() color = brush.color() color.setAlphaF(alpha) brush.setColor(color) marker.setBrush(brush) pen = marker.pen() # QPen color = pen.color() color.setAlphaF(alpha) pen.setColor(color) marker.setPen(pen)
class QmyMainWindow(QtWidgets.QMainWindow): def __init__(self, parent=None): super().__init__(parent) self.ui = Ui_MainWindow() self.ui.setupUi(self) self.setCentralWidget(self.ui.splitter) self.ui.tableView.setSelectionBehavior(QAbstractItemView.SelectRows) self.ui.tableView.setSelectionMode(QAbstractItemView.SingleSelection) self.ui.tableView.setAlternatingRowColors(True) self.ui.tableView.verticalHeader().setDefaultSectionSize(22) self.ui.tableView.horizontalHeader().setDefaultSectionSize(60) @pyqtSlot() def on_actOpenDB_triggered(self): dbFilename, flt = QFileDialog.getOpenFileName( self, "选择数据库文件", "", "SQL Lite数据库(*.db *.db3)") if (dbFilename == ''): return self.DB = QSqlDatabase.addDatabase("QSQLITE") self.DB.setDatabaseName(dbFilename) if self.DB.open(): self.__openTable() else: QMessageBox.warning(self, "错误", "打开数据库失败") def __openTable(self): self.qryModel = QSqlQueryModel(self) self.qryModel.setQuery( '''SELECT empNo, Name, Gender, Birthday, Province, Department, Salary FROM employee ORDER BY empNo''' ) if self.qryModel.lastError().isValid(): QMessageBox.critical( self, "错误", "数据表查询错误,错误信息\n" + self.qryModel.lastError().text()) return self.ui.statusBar.showMessage("记录条数:%d" % self.qryModel.rowCount()) self.__getFieldNames() self.qryModel.setHeaderData(0, Qt.Horizontal, "工号") self.qryModel.setHeaderData(1, Qt.Horizontal, "姓名") self.qryModel.setHeaderData(2, Qt.Horizontal, "性别") self.qryModel.setHeaderData(3, Qt.Horizontal, "出生日期") self.qryModel.setHeaderData(4, Qt.Horizontal, "省份") self.qryModel.setHeaderData(5, Qt.Horizontal, "部门") self.qryModel.setHeaderData(6, Qt.Horizontal, "工资") self.mapper = QDataWidgetMapper() self.mapper.setModel(self.qryModel) self.mapper.addMapping(self.ui.dbSpinEmpNo, 0) self.mapper.addMapping(self.ui.dbEditName, 1) self.mapper.addMapping(self.ui.dbComboSex, 2) self.mapper.addMapping(self.ui.dbEditBirth, 3) self.mapper.addMapping(self.ui.dbComboProvince, 4) self.mapper.addMapping(self.ui.dbComboDep, 5) self.mapper.addMapping(self.ui.dbSpinSalary, 6) self.mapper.toFirst() self.selModel = QItemSelectionModel(self.qryModel) self.selModel.currentRowChanged.connect(self.do_currentRowChanged) self.ui.tableView.setModel(self.qryModel) self.ui.tableView.setSelectionModel(self.selModel) self.ui.actOpenDB.setEnabled(False) def __refreshTableView(self): index = self.mapper.currentIndex() curIndex = self.qryModel.index(index, 1) self.selModel.clearSelection() self.selModel.setCurrentIndex(curIndex, QItemSelectionModel.Select) def __getFieldNames(self): emptyRec = self.qryModel.record() self.fldNum = {} for i in range(emptyRec.count()): fieldName = emptyRec.fieldName(i) self.fldNum.setdefault(fieldName) self.fldNum[fieldName] = i print(self.fldNum) @pyqtSlot() def on_actRecFirst_triggered(self): self.mapper.toFirst() self.__refreshTableView() @pyqtSlot() def on_actRecPrevious_triggered(self): self.mapper.toPrevious() self.__refreshTableView() @pyqtSlot() def on_actRecNext_triggered(self): self.mapper.toNext() self.__refreshTableView() @pyqtSlot() def on_actRecLast_triggered(self): self.mapper.toLast() self.__refreshTableView() def do_currentRowChanged(self, current, previous): if (current.isValid() == False): self.ui.dbLabPhoto.clear() return self.mapper.setCurrentIndex(current.row()) first = (current.row() == 0) last = (current.row() == self.qryModel.rowCount() - 1) self.ui.actRecFirst.setEnabled(not first) self.ui.actRecPrevious.setEnabled(not first) self.ui.actRecNext.setEnabled(not last) self.ui.actRecLast.setEnabled(not last) curRec = self.qryModel.record(current.row()) empNo = curRec.value("EmpNo") query = QSqlQuery(self.DB) query.prepare( '''SELECT EmpNo, Memo, Photo FROM employee WHERE EmpNo = :ID''') query.bindValue(":ID", empNo) if not query.exec(): QMessageBox.critical(self, "错误", "执行SQL语句错误\n" + query.lastError().text()) return else: query.first() picData = query.value("Photo") if (picData == None): self.ui.dbLabPhoto.clear() else: pic = QPixmap() pic.loadFromData(picData) W = self.ui.dbLabPhoto.size().width() self.ui.dbLabPhoto.setPixmap(pic.scaledToWidth(W)) memoData = query.value("Memo") self.ui.dbEditMemo.setPlainText(memoData)
class QmyMainWindow(QMainWindow): def __init__(self, parent=None): super().__init__(parent) #调用父类构造函数,创建窗体 self.ui = Ui_MainWindow() #创建UI对象 self.ui.setupUi(self) #构造UI界面 self.setCentralWidget(self.ui.splitter) self.__ColCount = 6 #列数=6 self.itemModel = QStandardItemModel(5, self.__ColCount, self) # 数据模型,10行6列 self.selectionModel = QItemSelectionModel(self.itemModel) #Item选择模型 self.selectionModel.currentChanged.connect(self.do_curChanged) self.__lastColumnTitle = "测井取样" self.__lastColumnFlags = (Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled) ##tableView设置 self.ui.tableView.setModel(self.itemModel) #设置数据模型 self.ui.tableView.setSelectionModel(self.selectionModel) #设置选择模型 oneOrMore = QAbstractItemView.ExtendedSelection self.ui.tableView.setSelectionMode(oneOrMore) #可多选 itemOrRow = QAbstractItemView.SelectItems self.ui.tableView.setSelectionBehavior(itemOrRow) #单元格选择 self.ui.tableView.verticalHeader().setDefaultSectionSize(22) #缺省行高 self.ui.tableView.setAlternatingRowColors(True) #交替行颜色 self.ui.tableView.setEnabled(False) #先禁用tableView self.__buildStatusBar() ## ==============自定义功能函数============ def __buildStatusBar(self): ##构建状态栏 self.LabCellPos = QLabel("当前单元格:", self) self.LabCellPos.setMinimumWidth(180) self.ui.statusBar.addWidget(self.LabCellPos) self.LabCellText = QLabel("单元格内容:", self) self.LabCellText.setMinimumWidth(150) self.ui.statusBar.addWidget(self.LabCellText) self.LabCurFile = QLabel("当前文件:", self) self.ui.statusBar.addPermanentWidget(self.LabCurFile) def __iniModelFromStringList(self, allLines): ##从字符串列表构建模型 rowCnt = len(allLines) #文本行数,第1行是标题 self.itemModel.setRowCount(rowCnt - 1) #实际数据行数 headerText = allLines[0].strip() #第1行是表头,去掉末尾的换行符 "\n" headerList = headerText.split("\t") #转换为字符串列表 self.itemModel.setHorizontalHeaderLabels(headerList) #设置表头标题 self.__lastColumnTitle = headerList[len(headerList) - 1] # 最后一列表头的标题,即“测井取样” lastColNo = self.__ColCount - 1 #最后一列的列号 for i in range(rowCnt - 1): lineText = allLines[i + 1].strip() #一行的文字,\t分隔 strList = lineText.split("\t") #分割为字符串列表 for j in range(self.__ColCount - 1): #不含最后一列 item = QStandardItem(strList[j]) self.itemModel.setItem(i, j, item) #设置模型的item item = QStandardItem(self.__lastColumnTitle) #最后一列 item.setFlags(self.__lastColumnFlags) item.setCheckable(True) if (strList[lastColNo] == "0"): item.setCheckState(Qt.Unchecked) else: item.setCheckState(Qt.Checked) self.itemModel.setItem(i, lastColNo, item) #设置最后一列的item def __setCellAlignment(self, align=Qt.AlignHCenter): if (not self.selectionModel.hasSelection()): #没有选择的项 return selectedIndex = self.selectionModel.selectedIndexes() #模型索引列表 count = len(selectedIndex) for i in range(count): index = selectedIndex[i] #获取其中的一个模型索引 item = self.itemModel.itemFromIndex(index) #获取一个单元格的项数据对象 item.setTextAlignment(align) #设置文字对齐方式 ## ==========由connectSlotsByName() 自动连接的槽函数================== @pyqtSlot() ##“打开文件” def on_actOpen_triggered(self): ## curPath=QDir.currentPath() #获取当前路径 curPath = os.getcwd() #获取当前路径 filename, flt = QFileDialog.getOpenFileName( self, "打开一个文件", curPath, "井斜数据文件(*.txt);;所有文件(*.*)") if (filename == ""): return self.LabCurFile.setText("当前文件:" + filename) self.ui.plainTextEdit.clear() aFile = open(filename, 'r') allLines = aFile.readlines() #读取所有行,list类型,每行末尾带有 \n aFile.close() for strLine in allLines: self.ui.plainTextEdit.appendPlainText(strLine.strip()) self.__iniModelFromStringList(allLines) self.ui.tableView.setEnabled(True) #tableView可用 self.ui.actAppend.setEnabled(True) #更新Actions的enable属性 self.ui.actInsert.setEnabled(True) self.ui.actDelete.setEnabled(True) self.ui.actSave.setEnabled(True) self.ui.actModelData.setEnabled(True) @pyqtSlot() ##保存文件 def on_actSave_triggered(self): ## curPath=QDir.currentPath() #获取当前路径 curPath = os.getcwd() #获取当前路径 filename, flt = QFileDialog.getSaveFileName( self, "保存文件", curPath, "井斜数据文件(*.txt);;所有文件(*.*)") if (filename == ""): return self.on_actModelData_triggered() #更新数据到plainTextEdit aFile = open(filename, 'w') #以写方式打开 aFile.write(self.ui.plainTextEdit.toPlainText()) aFile.close() @pyqtSlot() ##在最后添加一行 def on_actAppend_triggered(self): itemlist = [] # QStandardItem 对象列表 for i in range(self.__ColCount - 1): #不包括最后一列 item = QStandardItem("0") itemlist.append(item) item = QStandardItem(self.__lastColumnTitle) #最后一列 item.setCheckable(True) item.setFlags(self.__lastColumnFlags) itemlist.append(item) self.itemModel.appendRow(itemlist) #添加一行 curIndex = self.itemModel.index(self.itemModel.rowCount() - 1, 0) self.selectionModel.clearSelection() self.selectionModel.setCurrentIndex(curIndex, QItemSelectionModel.Select) @pyqtSlot() ##插入一行 def on_actInsert_triggered(self): itemlist = [] # QStandardItem 对象列表 for i in range(self.__ColCount - 1): #不包括最后一列 item = QStandardItem("0") itemlist.append(item) item = QStandardItem(self.__lastColumnTitle) #最后一列 item.setFlags(self.__lastColumnFlags) item.setCheckable(True) item.setCheckState(Qt.Checked) itemlist.append(item) curIndex = self.selectionModel.currentIndex() #获取当前选中项的模型索引 self.itemModel.insertRow(curIndex.row(), itemlist) #在当前行的前面插入一行 self.selectionModel.clearSelection() self.selectionModel.setCurrentIndex(curIndex, QItemSelectionModel.Select) @pyqtSlot() ##删除当前行 def on_actDelete_triggered(self): curIndex = self.selectionModel.currentIndex() #获取当前选择单元格的模型索引 self.itemModel.removeRow(curIndex.row()) #删除当前行 @pyqtSlot() ##左对齐 def on_actAlignLeft_triggered(self): self.__setCellAlignment(Qt.AlignLeft | Qt.AlignVCenter) @pyqtSlot() ##中间对齐 def on_actAlignCenter_triggered(self): self.__setCellAlignment(Qt.AlignHCenter | Qt.AlignVCenter) @pyqtSlot() ##右对齐 def on_actAlignRight_triggered(self): self.__setCellAlignment(Qt.AlignRight | Qt.AlignVCenter) @pyqtSlot(bool) ##字体Bold def on_actFontBold_triggered(self, checked): if (not self.selectionModel.hasSelection()): #没有选择的项 return selectedIndex = self.selectionModel.selectedIndexes() #模型索引列表 count = len(selectedIndex) for i in range(count): index = selectedIndex[i] #获取其中的一个模型索引 item = self.itemModel.itemFromIndex(index) #获取一个单元格的项数据对象 font = item.font() font.setBold(checked) item.setFont(font) @pyqtSlot() ##模型数据显示到plainTextEdit里 def on_actModelData_triggered(self): self.ui.plainTextEdit.clear() lineStr = "" for i in range(self.itemModel.columnCount() - 1): #表头,不含最后一列 item = self.itemModel.horizontalHeaderItem(i) lineStr = lineStr + item.text() + "\t" item = self.itemModel.horizontalHeaderItem(self.__ColCount - 1) #最后一列 lineStr = lineStr + item.text() #表头文字字符串 self.ui.plainTextEdit.appendPlainText(lineStr) for i in range(self.itemModel.rowCount()): lineStr = "" for j in range(self.itemModel.columnCount() - 1): #不包括最后一列 item = self.itemModel.item(i, j) lineStr = lineStr + item.text() + "\t" item = self.itemModel.item(i, self.__ColCount - 1) #最后一列 if (item.checkState() == Qt.Checked): lineStr = lineStr + "1" else: lineStr = lineStr + "0" self.ui.plainTextEdit.appendPlainText(lineStr) ## =============自定义槽函数=============================== def do_curChanged(self, current, previous): if (current != None): #当前模型索引有效 text = "当前单元格:%d行,%d列" % (current.row(), current.column()) self.LabCellPos.setText(text) item = self.itemModel.itemFromIndex(current) #从模型索引获得Item self.LabCellText.setText("单元格内容:" + item.text()) #显示item的文字内容 font = item.font() #获取item的字体 self.ui.actFontBold.setChecked(font.bold()) #更新actFontBold的check状态
class AccountsManager(ui.manageAccounts.Ui_Dialog, QDialog): """ GUI that handles creation, editing and deletion of accounts. """ def __init__(self, orm): super().__init__() self.setupUi(self) self.orm = orm self.accounts = ListModel() self.accountsView.setModel(self.accounts) self.selection = QItemSelectionModel(self.accounts) self.accountsView.setSelectionModel(self.selection) self.selection.currentRowChanged.connect(self.selection_changed) self.typeBox.addItems(ACCOUNT_TYPES) self.typeBox.currentTextChanged.connect(self.type_changed) self.closedBox.stateChanged.connect(self.closed_changed) self.exBudgetBox.stateChanged.connect(self.budget_changed) self.addAccountButton.clicked.connect(self.add_account) self.deleteAccountButton.clicked.connect(self.delete_account) for acc in self.orm.fetch_accounts(): self.accounts.addItem(acc) def selection_changed(self, curr_index: QModelIndex, prev_index: QModelIndex): """ Updates the information about currently selected account. """ if not curr_index.isValid(): return None # Make sure selection is visible in the view self.selection.setCurrentIndex( curr_index, QItemSelectionModel.SelectCurrent) acc = curr_index.data(role=Qt.UserRole) # Set the type of account self.typeBox.setCurrentText(acc.type) # Set the checkboxes self.closedBox.setChecked(acc.closed) self.exBudgetBox.setChecked(acc.exbudget) def type_changed(self, text: str): """ Changes the type of the account in DB and GUI. """ model_indexes = self.selection.selectedIndexes() if not model_indexes: return None model_index = model_indexes[0] acc = model_index.data(role=Qt.UserRole) # Catch only changes that differ for selected account if acc.type != text: self.orm.update_account_type(acc, text) acc.type = text def closed_changed(self, state: int): """ Changes close status of the account in DB and GUI. """ model_indexes = self.selection.selectedIndexes() if not model_indexes: return None model_index = model_indexes[0] acc = model_index.data(role=Qt.UserRole) # Catch only changes that differ for selected account # acc states in (0,1); Qt.CheckState in (0,1,2) # (0,1,2)//2 -> (0,0,1) state //= 2 if acc.closed != state: self.orm.update_account_status(acc, state) acc.closed = state def budget_changed(self, state: int): """ Changes the budget status of account in DB and GUI. """ model_indexes = self.selection.selectedIndexes() if not model_indexes: return None model_index = model_indexes[0] acc = model_index.data(role=Qt.UserRole) # Catch only changes that differ for selected account # acc states in (0,1); Qt.CheckState in (0,1,2) # (0,1,2)//2 -> (0,0,1) state //= 2 if acc.exbudget != state: self.orm.update_account_budget_status(acc, state) acc.exbudget = state def add_account(self): """ Creates account with default attributes in DB and adds it to GUI. """ name, ok = QInputDialog.getText(self, "Add new account", "Enter the name of the new account:") if ok and name: acc = self.orm.add_account(name) self.accounts.addItem(acc) def delete_account(self): """ Tries to delete the account from DB, if successful deletes it from GUI. """ model_indexes = self.selection.selectedIndexes() if not model_indexes: # the list of accounts is empty return None model_index = model_indexes[0] acc = model_index.data(role=Qt.UserRole) msgBox = QMessageBox() msgBox.setText("Delete the {} account?".format(acc.name)) msgBox.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel) msgBox.setDefaultButton(QMessageBox.Cancel) ret = msgBox.exec() if ret == QMessageBox.Ok: deletion = self.orm.delete_account(acc) if not deletion: show_warning("Can't delete account.") else: self.accounts.removeRow(model_index.row())
class QmyMainWindow(QMainWindow): cellIndexChange = pyqtSignal(int, int) def __init__(self, parent=None): super().__init__(parent) self.ui = Ui_MainWindow() self.ui.setupUi(self) self.__dlgSetHeaders = None self.setCentralWidget(self.ui.qTableView) self.qLabel1 = QLabel("当前单元格:", self) self.qLabel1.setMinimumWidth(180) self.ui.qStatusBar.addWidget(self.qLabel1) self.qLabel2 = QLabel("单元格内容:", self) self.qLabel2.setMinimumWidth(200) self.ui.qStatusBar.addWidget(self.qLabel2) self.itemModel = QStandardItemModel(10, 5, self) self.selectionModel = QItemSelectionModel(self.itemModel) self.selectionModel.currentChanged.connect( self.do_currentChanged) # 显示状态栏信息 self.ui.qTableView.setModel(self.itemModel) self.ui.qTableView.setSelectionModel(self.selectionModel) @pyqtSlot() def on_qAction1_triggered(self): # 设置行数列数 dlgTableSize = QmyDialogSize(self.itemModel.rowCount(), self.itemModel.columnCount()) ret = dlgTableSize.exec() if (ret == QDialog.Accepted): rows, cols = dlgTableSize.getTableSize() self.itemModel.setRowCount(rows) self.itemModel.setColumnCount(cols) @pyqtSlot() def on_qAction2_triggered(self): # 设置表头标题 if (self.__dlgSetHeaders == None): self.__dlgSetHeaders = QmyDialogHeaders(self) count = len(self.__dlgSetHeaders.headerList()) if (count != self.itemModel.columnCount() ): # 逻辑感人。首先表头标题不允许在表格内部直接改,因此如果发现标题与设置界面不一致,肯定是列数发生变化 strList = [] for i in range(self.itemModel.columnCount()): text = str( self.itemModel.headerData(i, Qt.Horizontal, Qt.DisplayRole)) strList.append(text) self.__dlgSetHeaders.setHeaderList(strList) ret = self.__dlgSetHeaders.exec() if (ret == QDialog.Accepted): strList2 = self.__dlgSetHeaders.headerList() self.itemModel.setHorizontalHeaderLabels(strList2) @pyqtSlot() def on_qAction3_triggered(self): # 定位单元格 dlgLocate = QmyDialogLocate(self) dlgLocate.setSpinRange(self.itemModel.rowCount(), self.itemModel.columnCount()) dlgLocate.changeActionEnable.connect(self.do_setActLocateEnable) dlgLocate.changeCellText.connect(self.do_setACellText) self.cellIndexChange.connect(dlgLocate.do_setSpinValue) dlgLocate.setAttribute(Qt.WA_DeleteOnClose) dlgLocate.show() def do_setActLocateEnable(self, enable): self.ui.qAction3.setEnabled(enable) def do_setACellText(self, row, column, text): index = self.itemModel.index(row, column) self.selectionModel.clearSelection() self.selectionModel.setCurrentIndex(index, QItemSelectionModel.Select) self.itemModel.setData(index, text, Qt.DisplayRole) def do_currentChanged(self, current, previous): if (current != None): self.qLabel1.setText("当前单元格:%d行,%d列" % (current.row(), current.column())) item = self.itemModel.itemFromIndex(current) self.qLabel2.setText("单元格内容:" + item.text()) self.cellIndexChange.emit(current.row(), current.column())
class QmyMainWindow(QMainWindow): def __init__(self, parent=None): super().__init__(parent) #调用父类构造函数,创建窗体 self.ui = Ui_MainWindow() #创建UI对象 self.ui.setupUi(self) #构造UI界面 self.setCentralWidget(self.ui.splitter) self.__buildStatusBar() self.COL_COUNT = 6 #常数,列数=6 self.itemModel = QStandardItemModel(5, self.COL_COUNT, self) # 数据模型,10行6列 ## headerList=["测深(m)","垂深(m)","方位(°)","总位移(m)","固井质量","测井取样"] ## self.itemModel.setHorizontalHeaderLabels(headerList) #设置表头文字 self.selectionModel = QItemSelectionModel(self.itemModel) #Item选择模型 self.selectionModel.currentChanged.connect(self.do_currentChanged) self.__lastColumnFlags = (Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled) self.__lastColumnTitle = "测井取样" #为tableView设置数据模型 self.ui.tableView.setModel(self.itemModel) #设置数据模型 self.ui.tableView.setSelectionModel(self.selectionModel) #设置选择模型 oneOrMore = QAbstractItemView.ExtendedSelection self.ui.tableView.setSelectionMode(oneOrMore) #可多选 itemOrRow = QAbstractItemView.SelectItems self.ui.tableView.setSelectionBehavior(itemOrRow) #单元格选择 self.ui.tableView.verticalHeader().setDefaultSectionSize(22) #缺省行高 self.ui.tableView.setEnabled(False) #先禁用tableView ## self.__resetTable(5) #创建自定义代理组件并设置 self.spinCeShen = QmyFloatSpinDelegate(0, 10000, 0, self) #用于 测深 self.spinLength = QmyFloatSpinDelegate(0, 6000, 2, self) #垂深,总位移 self.spinDegree = QmyFloatSpinDelegate(0, 360, 1, self) #用于 方位 self.ui.tableView.setItemDelegateForColumn(0, self.spinCeShen) #测深 self.ui.tableView.setItemDelegateForColumn(1, self.spinLength) #垂深 self.ui.tableView.setItemDelegateForColumn(3, self.spinLength) #总位移 self.ui.tableView.setItemDelegateForColumn(2, self.spinDegree) #方位角 qualities = ["优", "良", "合格", "不合格"] self.comboDelegate = QmyComboBoxDelegate(self) self.comboDelegate.setItems(qualities, False) #不可编辑 self.ui.tableView.setItemDelegateForColumn(4, self.comboDelegate) #固井质量 ## ==============自定义功能函数============ def __buildStatusBar(self): ##构建状态栏 self.LabCellPos = QLabel("当前单元格:", self) self.LabCellPos.setMinimumWidth(180) self.ui.statusBar.addWidget(self.LabCellPos) self.LabCellText = QLabel("单元格内容:", self) self.LabCellText.setMinimumWidth(150) self.ui.statusBar.addWidget(self.LabCellText) self.LabCurFile = QLabel("当前文件:", self) self.ui.statusBar.addPermanentWidget(self.LabCurFile) ## def __resetTable(self,tableRowCount): #复位数据表,必须为数值设置0,否则代理组件出错 ## self.itemModel.removeRows(0,self.itemModel.rowCount()) #删除所有行 ## self.itemModel.setRowCount(tableRowCount) #设置新的行数 ## ## for i in range(tableRowCount): #设置最后一列 ## for j in range(self.COL_COUNT-1): ## index=self.itemModel.index(i,j) #获取模型索引 ## item=self.itemModel.itemFromIndex(index) #获取item ## item.setData(0,Qt.DisplayRole) #数值必须初始化为0 ## item.setTextAlignment(Qt.AlignHCenter) ## ## index=self.itemModel.index(i,self.COL_COUNT-1) #获取模型索引 ## item=self.itemModel.itemFromIndex(index) #获取item ## item.setCheckable(True) ## item.setData(self.__lastColumnTitle,Qt.DisplayRole) ## item.setEditable(False) #不可编辑 def __iniModelFromStringList(self, allLines): ##从字符串列表构建模型 rowCnt = len(allLines) #文本行数,第1行是标题 self.itemModel.setRowCount(rowCnt - 1) #实际数据行数 headerText = allLines[0].strip() #第1行是表头,去掉末尾的换行符 "\n" headerList = headerText.split("\t") self.itemModel.setHorizontalHeaderLabels(headerList) self.__lastColumnTitle = headerList[len(headerList) - 1] # 最后一列表头的标题,即“测井取样” lastColNo = self.COL_COUNT - 1 #最后一列的列号 for i in range(rowCnt - 1): lineText = allLines[i + 1].strip() #一行的文字,\t分隔 strList = lineText.split("\t") for j in range(self.COL_COUNT - 1): #不含最后一列 item = QStandardItem(strList[j]) self.itemModel.setItem(i, j, item) item = QStandardItem(self.__lastColumnTitle) #最后一列 item.setFlags(self.__lastColumnFlags) item.setCheckable(True) if (strList[lastColNo] == "0"): item.setCheckState(Qt.Unchecked) else: item.setCheckState(Qt.Checked) self.itemModel.setItem(i, lastColNo, item) #设置最后一列的item def __setCellAlignment(self, align=Qt.AlignHCenter): if (not self.selectionModel.hasSelection()): #没有选择的项 return selectedIndex = self.selectionModel.selectedIndexes() #列表类型 count = len(selectedIndex) for i in range(count): index = selectedIndex[i] #获取其中的一个模型索引 item = self.itemModel.itemFromIndex(index) #获取一个单元格的项数据对象 item.setTextAlignment(align) #设置文字对齐方式 ## ==========由connectSlotsByName() 自动连接的槽函数================== @pyqtSlot() ##“打开文件” def on_actOpen_triggered(self): curPath = os.getcwd() #获取当前路径 filename, flt = QFileDialog.getOpenFileName( self, "打开一个文件", curPath, "井斜数据文件(*.txt);;所有文件(*.*)") if (filename == ""): return self.LabCurFile.setText("当前文件:" + filename) self.ui.plainTextEdit.clear() aFile = open(filename, 'r') allLines = aFile.readlines() #读取所有行,list类型,每行末尾带有 \n ## for eachLine in aFile: #每次读取一行 ## self.ui.plainTextEdit.appendPlainText(eachLine) aFile.close() for strLine in allLines: self.ui.plainTextEdit.appendPlainText(strLine.strip()) self.__iniModelFromStringList(allLines) self.ui.actAppend.setEnabled(True) #更新Actions的enable属性 self.ui.actInsert.setEnabled(True) self.ui.actDelete.setEnabled(True) self.ui.actSave.setEnabled(True) self.ui.actModelData.setEnabled(True) self.ui.tableView.setEnabled(True) #启用tableView @pyqtSlot() ##保存文件 def on_actSave_triggered(self): curPath = os.getcwd() #获取当前路径 filename, flt = QFileDialog.getSaveFileName( self, "保存文件", curPath, "井斜数据文件(*.txt);;所有文件(*.*)") if (filename == ""): return self.on_actModelData_triggered() #更新数据到plainTextEdit aFile = open(filename, 'w') #以写方式打开 aFile.write(self.ui.plainTextEdit.toPlainText()) aFile.close() @pyqtSlot() def on_actAppend_triggered(self): #在最后添加一行 itemlist = [] # 列表 for i in range(self.COL_COUNT - 1): item = QStandardItem("0") itemlist.append(item) item = QStandardItem(self.__lastColumnTitle) #最后一列 item.setCheckable(True) item.setFlags(self.__lastColumnFlags) itemlist.append(item) self.itemModel.appendRow(itemlist) curIndex = self.itemModel.index(self.itemModel.rowCount() - 1, 0) self.selectionModel.clearSelection() self.selectionModel.setCurrentIndex(curIndex, QItemSelectionModel.Select) @pyqtSlot() ##插入一行 def on_actInsert_triggered(self): itemlist = [] # 列表 for i in range(self.COL_COUNT - 1): item = QStandardItem("0") itemlist.append(item) item = QStandardItem(self.__lastColumnTitle) #最后一列 item.setFlags(self.__lastColumnFlags) item.setCheckable(True) item.setCheckState(Qt.Checked) itemlist.append(item) curIndex = self.selectionModel.currentIndex() #获取当前选中项的模型索引 self.itemModel.insertRow(curIndex.row(), itemlist) #在当前行的前面插入一行 self.selectionModel.clearSelection() self.selectionModel.setCurrentIndex(curIndex, QItemSelectionModel.Select) @pyqtSlot() ##删除当前行 def on_actDelete_triggered(self): curIndex = self.selectionModel.currentIndex() #获取当前选择单元格的模型索引 self.itemModel.removeRow(curIndex.row()) # //删除当前行 @pyqtSlot() ##左对齐 def on_actAlignLeft_triggered(self): self.__setCellAlignment(Qt.AlignLeft | Qt.AlignVCenter) @pyqtSlot() ##中间对齐 def on_actAlignCenter_triggered(self): self.__setCellAlignment(Qt.AlignHCenter | Qt.AlignVCenter) @pyqtSlot() ##右对齐 def on_actAlignRight_triggered(self): self.__setCellAlignment(Qt.AlignRight | Qt.AlignVCenter) @pyqtSlot(bool) ##字体Bold def on_actFontBold_triggered(self, checked): if (not self.selectionModel.hasSelection()): #没有选择的项 return selectedIndex = self.selectionModel.selectedIndexes() #列表类型 count = len(selectedIndex) for i in range(count): index = selectedIndex[i] #获取其中的一个模型索引 item = self.itemModel.itemFromIndex(index) #获取一个单元格的项数据对象 font = item.font() font.setBold(checked) item.setFont(font) @pyqtSlot() ##模型数据显示到plainTextEdit里 def on_actModelData_triggered(self): self.ui.plainTextEdit.clear() lineStr = "" for i in range(self.itemModel.columnCount() - 1): #表头 item = self.itemModel.horizontalHeaderItem(i) lineStr = lineStr + item.text() + "\t" item = self.itemModel.horizontalHeaderItem(self.COL_COUNT - 1) #最后一列 lineStr = lineStr + item.text() self.ui.plainTextEdit.appendPlainText(lineStr) for i in range(self.itemModel.rowCount()): lineStr = "" for j in range(self.itemModel.columnCount() - 1): #不包括最后一列 item = self.itemModel.item(i, j) lineStr = lineStr + item.text() + "\t" item = self.itemModel.item(i, self.COL_COUNT - 1) #最后一列 if (item.checkState() == Qt.Checked): lineStr = lineStr + "1" else: lineStr = lineStr + "0" self.ui.plainTextEdit.appendPlainText(lineStr) ## =============自定义槽函数=============================== def do_currentChanged(self, current, previous): if (current != None): #当前模型索引有效 self.LabCellPos.setText( "当前单元格:%d行,%d列" % (current.row(), current.column())) #显示模型索引的行和列号 item = self.itemModel.itemFromIndex(current) #从模型索引获得Item self.LabCellText.setText("单元格内容:" + item.text()) #显示item的文字内容 font = item.font() #获取item的字体 self.ui.actFontBold.setChecked(font.bold()) #更新actFontBold的check状态
class LayerStackModel(QAbstractListModel): canMoveSelectedUp = pyqtSignal("bool") canMoveSelectedDown = pyqtSignal("bool") canDeleteSelected = pyqtSignal("bool") orderChanged = pyqtSignal() layerAdded = pyqtSignal(Layer, int) # is now in row layerRemoved = pyqtSignal(Layer, int) # was in row stackCleared = pyqtSignal() def __init__(self, parent=None): QAbstractListModel.__init__(self, parent) self._layerStack = [] self.selectionModel = QItemSelectionModel(self) self.selectionModel.selectionChanged.connect(self.updateGUI) self.selectionModel.selectionChanged.connect(self._onSelectionChanged) self._movingRows = False QTimer.singleShot(0, self.updateGUI) def _handleRemovedLayer(layer): # Layerstacks *own* the layers they hold, and thus are # responsible for cleaning them up when they are removed: layer.clean_up() self.layerRemoved.connect(_handleRemovedLayer) #### ## High level API to manipulate the layerstack ### def __len__(self): return self.rowCount() def __repr__(self): return "<LayerStackModel: layerStack='%r'>" % (self._layerStack, ) def __getitem__(self, i): return self._layerStack[i] def __iter__(self): return self._layerStack.__iter__() def layerIndex(self, layer): #note that the 'index' function already has a different implementation #from Qt side return self._layerStack.index(layer) def findMatchingIndex(self, func): """Call the given function with each layer and return the index of the first layer for which f is True.""" for index, layer in enumerate(self._layerStack): if func(layer): return index raise ValueError("No matching layer in stack.") def append(self, data): self.insert(0, data) def clear(self): if len(self) > 0: self.removeRows(0, len(self)) self.stackCleared.emit() def insert(self, index, data): """ Insert a layer into this layer stack, which *takes ownership* of the layer. """ assert isinstance( data, Layer), "Only Layers can be added to a LayerStackModel" self.insertRow(index) self.setData(self.index(index), data) if self.selectedRow() >= 0: self.selectionModel.select(self.index(self.selectedRow()), QItemSelectionModel.Deselect) self.selectionModel.select(self.index(index), QItemSelectionModel.Select) data.changed.connect( functools.partial(self._onLayerChanged, self.index(index))) index = self._layerStack.index(data) self.layerAdded.emit(data, index) self.updateGUI() def selectRow(self, row): already_selected_rows = self.selectionModel.selectedRows() if len(already_selected_rows) == 1 and row == already_selected_rows[0]: # Nothing to do if this row is already selected return self.selectionModel.clear() self.selectionModel.setCurrentIndex(self.index(row), QItemSelectionModel.SelectCurrent) def deleteSelected(self): num_rows = len(self.selectionModel.selectedRows()) assert num_rows == 1, "Can't delete selected row: {} layers are currently selected.".format( num_rows) row = self.selectionModel.selectedRows()[0] layer = self._layerStack[row.row()] assert not layer._cleaned_up, "This layer ({}) has already been cleaned up. Shouldn't it already be removed from the layerstack?".format( layer.name) self.removeRow(row.row()) if self.rowCount() > 0: self.selectionModel.select(self.index(0), QItemSelectionModel.Select) self.layerRemoved.emit(layer, row.row()) self.updateGUI() def moveSelectedUp(self): assert len(self.selectionModel.selectedRows()) == 1 row = self.selectionModel.selectedRows()[0] if row.row() != 0: oldRow = row.row() newRow = oldRow - 1 self._moveToRow(oldRow, newRow) def moveSelectedDown(self): assert len(self.selectionModel.selectedRows()) == 1 row = self.selectionModel.selectedRows()[0] if row.row() != self.rowCount() - 1: oldRow = row.row() newRow = oldRow + 1 self._moveToRow(oldRow, newRow) def moveSelectedToTop(self): assert len(self.selectionModel.selectedRows()) == 1 row = self.selectionModel.selectedRows()[0] if row.row() != 0: oldRow = row.row() newRow = 0 self._moveToRow(oldRow, newRow) def moveSelectedToBottom(self): assert len(self.selectionModel.selectedRows()) == 1 row = self.selectionModel.selectedRows()[0] if row.row() != self.rowCount() - 1: oldRow = row.row() newRow = self.rowCount() - 1 self._moveToRow(oldRow, newRow) def moveSelectedToRow(self, newRow): assert len(self.selectionModel.selectedRows()) == 1 row = self.selectionModel.selectedRows()[0] if row.row() != newRow: oldRow = row.row() self._moveToRow(oldRow, newRow) def _moveToRow(self, oldRow, newRow): d = self._layerStack[oldRow] self.removeRow(oldRow) self.insertRow(newRow) self.setData(self.index(newRow), d) self.selectionModel.select(self.index(newRow), QItemSelectionModel.Select) self.orderChanged.emit() self.updateGUI() #### ## Low level API. To add, remove etc. layers use the high level API from above. #### def updateGUI(self): self.canMoveSelectedUp.emit(self.selectedRow() > 0) self.canMoveSelectedDown.emit(self.selectedRow() < self.rowCount() - 1) self.canDeleteSelected.emit(self.rowCount() > 0) self.wantsUpdate() def selectedRow(self): selected = self.selectionModel.selectedRows() if len(selected) == 1: return selected[0].row() return -1 def selectedIndex(self): row = self.selectedRow() if row >= 0: return self.index(self.selectedRow()) else: return QModelIndex() def rowCount(self, parent=QModelIndex()): if not parent.isValid(): return len(self._layerStack) return 0 def insertRows(self, row, count, parent=QModelIndex()): '''Insert empty rows in the stack. DO NOT USE THIS METHOD TO INSERT NEW LAYERS! Always use the insert() or append() method. ''' if parent.isValid(): return False oldRowCount = self.rowCount() #for some reason, row can be negative! beginRow = max(0, row) endRow = min(beginRow + count - 1, len(self._layerStack)) self.beginInsertRows(parent, beginRow, endRow) while (beginRow <= endRow): self._layerStack.insert(row, Layer(datasources=[])) beginRow += 1 self.endInsertRows() assert self.rowCount( ) == oldRowCount + 1, "oldRowCount = %d, self.rowCount() = %d" % ( oldRowCount, self.rowCount()) return True def removeRows(self, row, count, parent=QModelIndex()): '''Remove rows from the stack. DO NOT USE THIS METHOD TO REMOVE LAYERS! Use the deleteSelected() method instead. ''' if parent.isValid(): return False if row + count <= 0 or row >= len(self._layerStack): return False oldRowCount = self.rowCount() beginRow = max(0, row) endRow = min(row + count - 1, len(self._layerStack) - 1) self.beginRemoveRows(parent, beginRow, endRow) while (beginRow <= endRow): del self._layerStack[row] beginRow += 1 self.endRemoveRows() return True def flags(self, index): defaultFlags = Qt.ItemIsSelectable | Qt.ItemIsEditable | Qt.ItemIsEnabled if index.isValid(): return Qt.ItemIsDragEnabled | defaultFlags else: return Qt.ItemIsDropEnabled | defaultFlags def supportedDropActions(self): return Qt.MoveAction def data(self, index, role=Qt.DisplayRole): if not index.isValid(): return None if index.row() >= len(self._layerStack): return None if role == Qt.DisplayRole or role == Qt.EditRole: return self._layerStack[index.row()] elif role == Qt.ToolTipRole: return self._layerStack[index.row()].toolTip() else: return None def setData(self, index, value, role=Qt.EditRole): '''Replace one layer with another. DO NOT USE THIS METHOD TO INSERT NEW LAYERS! Use deleteSelected() followed by insert() or append(). ''' if role == Qt.EditRole: layer = value if not isinstance(value, Layer): layer = value self._layerStack[index.row()] = layer self.dataChanged.emit(index, index) return True elif role == Qt.ToolTipRole: self._layerStack[index.row()].setToolTip() return True return False def headerData(self, section, orientation, role=Qt.DisplayRole): if role != Qt.DisplayRole: return None if orientation == Qt.Horizontal: return "Column %r" % section else: return "Row %r" % section def wantsUpdate(self): self.layoutChanged.emit() def _onLayerChanged(self, idx): self.dataChanged.emit(idx, idx) self.updateGUI() def _onSelectionChanged(self, selected, deselected): for idx in deselected.indexes(): self[idx.row()].setActive(False) for idx in selected.indexes(): self[idx.row()].setActive(True)
class QmyMainWindow(QtWidgets.QMainWindow): def __init__(self, parent=None): super().__init__(parent) self.ui = Ui_MainWindow() self.ui.setupUi(self) self.setCentralWidget(self.ui.qSplitter) self.__ColCount = 6 self.itemModel = QStandardItemModel(5, self.__ColCount, self) self.selectionModel = QItemSelectionModel(self.itemModel) self.selectionModel.currentChanged.connect(self.do_curChanged) self.__lastColumnTitle = "测井取样" self.__lastColumnFlags = (Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled) self.ui.qTableView.setModel(self.itemModel) self.ui.qTableView.setSelectionModel(self.selectionModel) oneOrMore = QAbstractItemView.ExtendedSelection self.ui.qTableView.setSelectionMode(oneOrMore) itemOrRow = QAbstractItemView.SelectItems self.ui.qTableView.setSelectionBehavior(itemOrRow) self.ui.qTableView.verticalHeader().setDefaultSectionSize(22) self.ui.qTableView.setAlternatingRowColors(True) self.ui.qTableView.setEnabled(False) self.qLabel1 = QLabel("当前单元格:", self) self.qLabel1.setMinimumWidth(180) self.qLabel2 = QLabel("单元格内容:", self) self.qLabel2.setMinimumWidth(150) self.qLabel3 = QLabel("当前文件:", self) self.ui.qStatusBar.addWidget(self.qLabel1) self.ui.qStatusBar.addWidget(self.qLabel2) self.ui.qStatusBar.addPermanentWidget(self.qLabel3) def __iniModelFromStringList(self, allLines): rowCnt = len(allLines) self.itemModel.setRowCount(rowCnt - 1) headerText = allLines[0].strip() headerList = headerText.split("\t") self.itemModel.setHorizontalHeaderLabels(headerList) self.__lastColumnTitle = headerList[len(headerList) - 1] lastColNo = self.__ColCount - 1 for i in range(rowCnt - 1): lineText = allLines[i + 1].strip() strList = lineText.split("\t") for j in range(self.__ColCount - 1): item = QStandardItem(strList[j]) self.itemModel.setItem(i, j, item) item = QStandardItem(self.__lastColumnTitle) item.setFlags(self.__lastColumnFlags) item.setCheckable(True) if (strList[lastColNo] == "0"): item.setCheckState(Qt.Unchecked) else: item.setCheckState(Qt.Checked) self.itemModel.setItem(i, lastColNo, item) def __setCellAlignment(self, align=Qt.AlignHCenter): if (not self.selectionModel.hasSelection()): return selectedIndex = self.selectionModel.selectedIndexes() count = len(selectedIndex) for i in range(count): index = selectedIndex[i] item = self.itemModel.itemFromIndex(index) item.setTextAlignment(align) @pyqtSlot() def on_qAction1_triggered(self): # 打开文件 curPath = os.getcwd() filename, flt = QFileDialog.getOpenFileName( self, "打开一个文件", curPath, "井斜数据文件(*.txt);;所有文件(*.*)") if (filename == ""): return self.qLabel3.setText("当前文件:" + filename) self.ui.qPlainTextEdit.clear() aFile = open(filename, 'r') allLines = aFile.readlines() aFile.close() for strLine in allLines: self.ui.qPlainTextEdit.appendPlainText(strLine.strip()) self.__iniModelFromStringList(allLines) self.ui.qTableView.setEnabled(True) self.ui.qAction2.setEnabled(True) self.ui.qAction3.setEnabled(True) self.ui.qAction4.setEnabled(True) self.ui.qAction5.setEnabled(True) self.ui.qAction6.setEnabled(True) @pyqtSlot() def on_qAction2_triggered(self): # 另存文件 curPath = os.getcwd() filename, flt = QFileDialog.getSaveFileName( self, "保存文件", curPath, "井斜数据文件(*.txt);;所有文件(*.*)") if (filename == ""): return self.on_qAction3_triggered() aFile = open(filename, "w") aFile.write(self.ui.qPlainTextEdit.toPlainText()) aFile.close() @pyqtSlot() def on_qAction4_triggered(self): # 添加行 itemList = [] for i in range(self.__ColCount - 1): item = QStandardItem("0") itemList.append(item) item = QStandardItem(self.__lastColumnTitle) item.setCheckable(True) item.setFlags(self.__lastColumnFlags) itemList.append(item) self.itemModel.appendRow(itemList) curIndex = self.itemModel.index(self.itemModel.rowCount() - 1, 0) self.selectionModel.clearSelection() self.selectionModel.setCurrentIndex(curIndex, QItemSelectionModel.Select) @pyqtSlot() def on_qAction5_triggered(self): # 插入行 itemlist = [] for i in range(self.__ColCount - 1): item = QStandardItem("0") itemlist.append(item) item = QStandardItem(self.__lastColumnTitle) item.setFlags(self.__lastColumnFlags) item.setCheckable(True) item.setCheckState(Qt.Checked) itemlist.append(item) curIndex = self.selectionModel.currentIndex() self.itemModel.insertRow(curIndex.row(), itemlist) self.selectionModel.clearSelection() self.selectionModel.setCurrentIndex(curIndex, QItemSelectionModel.Select) @pyqtSlot() def on_qAction6_triggered(self): # 删除行 curIndex = self.selectionModel.currentIndex() self.itemModel.removeRow(curIndex.row()) @pyqtSlot() def on_qAction7_triggered(self): # 居左 self.__setCellAlignment(Qt.AlignLeft | Qt.AlignVCenter) @pyqtSlot() def on_qAction8_triggered(self): # 居中 self.__setCellAlignment(Qt.AlignHCenter | Qt.AlignVCenter) @pyqtSlot() def on_qAction9_triggered(self): # 居右 self.__setCellAlignment(Qt.AlignRight | Qt.AlignVCenter) @pyqtSlot(bool) def on_qAction10_triggered(self, checked): # 粗体 if (not self.selectionModel.hasSelection()): return selectedIndex = self.selectionModel.selectedIndexes() count = len(selectedIndex) for i in range(count): index = selectedIndex[i] item = self.itemModel.itemFromIndex(index) font = item.font() font.setBold(checked) item.setFont(font) @pyqtSlot() def on_qAction3_triggered(self): # 模型数据 self.ui.qPlainTextEdit.clear() lineStr = "" for i in range(self.itemModel.columnCount() - 1): item = self.itemModel.horizontalHeaderItem(i) lineStr = lineStr + item.text() + "\t" item = self.itemModel.horizontalHeaderItem(self.__ColCount - 1) lineStr = lineStr + item.text() self.ui.qPlainTextEdit.appendPlainText(lineStr) for i in range(self.itemModel.rowCount()): lineStr = "" for j in range(self.itemModel.columnCount() - 1): item = self.itemModel.item(i, j) lineStr = lineStr + item.text() + "\t" item = self.itemModel.item(i, self.__ColCount - 1) if (item.checkState() == Qt.Checked): lineStr = lineStr + "1" else: lineStr = lineStr + "0" self.ui.qPlainTextEdit.appendPlainText(lineStr) def do_curChanged(self, current, previous): if (current != None): text = "当前单元格:%d行,%d列" % (current.row(), current.column()) self.qLabel1.setText(text) item = self.itemModel.itemFromIndex(current) self.qLabel2.setText("单元格内容:" + item.text()) font = item.font() self.ui.qAction10.setChecked(font.bold())
class LayerStackModel(QAbstractListModel): canMoveSelectedUp = pyqtSignal("bool") canMoveSelectedDown = pyqtSignal("bool") canDeleteSelected = pyqtSignal("bool") orderChanged = pyqtSignal() layerAdded = pyqtSignal(Layer, int) # is now in row layerRemoved = pyqtSignal(Layer, int) # was in row stackCleared = pyqtSignal() def __init__(self, parent=None): QAbstractListModel.__init__(self, parent) self._layerStack = [] self.selectionModel = QItemSelectionModel(self) self.selectionModel.selectionChanged.connect(self.updateGUI) self.selectionModel.selectionChanged.connect(self._onSelectionChanged) self._movingRows = False QTimer.singleShot(0, self.updateGUI) def _handleRemovedLayer(layer): # Layerstacks *own* the layers they hold, and thus are # responsible for cleaning them up when they are removed: layer.clean_up() self.layerRemoved.connect(_handleRemovedLayer) #### ## High level API to manipulate the layerstack ### def __len__(self): return self.rowCount() def __repr__(self): return "<LayerStackModel: layerStack='%r'>" % (self._layerStack,) def __getitem__(self, i): return self._layerStack[i] def __iter__(self): return self._layerStack.__iter__() def layerIndex(self, layer): # note that the 'index' function already has a different implementation # from Qt side return self._layerStack.index(layer) def findMatchingIndex(self, func): """Call the given function with each layer and return the index of the first layer for which f is True.""" for index, layer in enumerate(self._layerStack): if func(layer): return index raise ValueError("No matching layer in stack.") def append(self, data): self.insert(0, data) def clear(self): if len(self) > 0: self.removeRows(0, len(self)) self.stackCleared.emit() def insert(self, index, data): """ Insert a layer into this layer stack, which *takes ownership* of the layer. """ assert isinstance(data, Layer), "Only Layers can be added to a LayerStackModel" self.insertRow(index) self.setData(self.index(index), data) if self.selectedRow() >= 0: self.selectionModel.select(self.index(self.selectedRow()), QItemSelectionModel.Deselect) self.selectionModel.select(self.index(index), QItemSelectionModel.Select) data.changed.connect(functools.partial(self._onLayerChanged, self.index(index))) index = self._layerStack.index(data) self.layerAdded.emit(data, index) self.updateGUI() def selectRow(self, row): already_selected_rows = self.selectionModel.selectedRows() if len(already_selected_rows) == 1 and row == already_selected_rows[0]: # Nothing to do if this row is already selected return self.selectionModel.clear() self.selectionModel.setCurrentIndex(self.index(row), QItemSelectionModel.SelectCurrent) def deleteSelected(self): num_rows = len(self.selectionModel.selectedRows()) assert num_rows == 1, "Can't delete selected row: {} layers are currently selected.".format(num_rows) row = self.selectionModel.selectedRows()[0] layer = self._layerStack[row.row()] assert ( not layer._cleaned_up ), "This layer ({}) has already been cleaned up. Shouldn't it already be removed from the layerstack?".format( layer.name ) self.removeRow(row.row()) if self.rowCount() > 0: self.selectionModel.select(self.index(0), QItemSelectionModel.Select) self.layerRemoved.emit(layer, row.row()) self.updateGUI() def moveSelectedUp(self): assert len(self.selectionModel.selectedRows()) == 1 row = self.selectionModel.selectedRows()[0] if row.row() != 0: oldRow = row.row() newRow = oldRow - 1 self._moveToRow(oldRow, newRow) def moveSelectedDown(self): assert len(self.selectionModel.selectedRows()) == 1 row = self.selectionModel.selectedRows()[0] if row.row() != self.rowCount() - 1: oldRow = row.row() newRow = oldRow + 1 self._moveToRow(oldRow, newRow) def moveSelectedToTop(self): assert len(self.selectionModel.selectedRows()) == 1 row = self.selectionModel.selectedRows()[0] if row.row() != 0: oldRow = row.row() newRow = 0 self._moveToRow(oldRow, newRow) def moveSelectedToBottom(self): assert len(self.selectionModel.selectedRows()) == 1 row = self.selectionModel.selectedRows()[0] if row.row() != self.rowCount() - 1: oldRow = row.row() newRow = self.rowCount() - 1 self._moveToRow(oldRow, newRow) def moveSelectedToRow(self, newRow): assert len(self.selectionModel.selectedRows()) == 1 row = self.selectionModel.selectedRows()[0] if row.row() != newRow: oldRow = row.row() self._moveToRow(oldRow, newRow) def _moveToRow(self, oldRow, newRow): d = self._layerStack[oldRow] self.removeRow(oldRow) self.insertRow(newRow) self.setData(self.index(newRow), d) self.selectionModel.select(self.index(newRow), QItemSelectionModel.Select) self.orderChanged.emit() self.updateGUI() #### ## Low level API. To add, remove etc. layers use the high level API from above. #### def updateGUI(self): self.canMoveSelectedUp.emit(self.selectedRow() > 0) self.canMoveSelectedDown.emit(self.selectedRow() < self.rowCount() - 1) self.canDeleteSelected.emit(self.rowCount() > 0) self.wantsUpdate() def selectedRow(self): selected = self.selectionModel.selectedRows() if len(selected) == 1: return selected[0].row() return -1 def selectedIndex(self): row = self.selectedRow() if row >= 0: return self.index(self.selectedRow()) else: return QModelIndex() def rowCount(self, parent=QModelIndex()): if not parent.isValid(): return len(self._layerStack) return 0 def insertRows(self, row, count, parent=QModelIndex()): """Insert empty rows in the stack. DO NOT USE THIS METHOD TO INSERT NEW LAYERS! Always use the insert() or append() method. """ if parent.isValid(): return False oldRowCount = self.rowCount() # for some reason, row can be negative! beginRow = max(0, row) endRow = min(beginRow + count - 1, len(self._layerStack)) self.beginInsertRows(parent, beginRow, endRow) while beginRow <= endRow: self._layerStack.insert(row, Layer(datasources=[])) beginRow += 1 self.endInsertRows() assert self.rowCount() == oldRowCount + 1, "oldRowCount = %d, self.rowCount() = %d" % ( oldRowCount, self.rowCount(), ) return True def removeRows(self, row, count, parent=QModelIndex()): """Remove rows from the stack. DO NOT USE THIS METHOD TO REMOVE LAYERS! Use the deleteSelected() method instead. """ if parent.isValid(): return False if row + count <= 0 or row >= len(self._layerStack): return False oldRowCount = self.rowCount() beginRow = max(0, row) endRow = min(row + count - 1, len(self._layerStack) - 1) self.beginRemoveRows(parent, beginRow, endRow) while beginRow <= endRow: del self._layerStack[row] beginRow += 1 self.endRemoveRows() return True def flags(self, index): defaultFlags = Qt.ItemIsSelectable | Qt.ItemIsEditable | Qt.ItemIsEnabled if index.isValid(): return Qt.ItemIsDragEnabled | defaultFlags else: return Qt.ItemIsDropEnabled | defaultFlags def supportedDropActions(self): return Qt.MoveAction def data(self, index, role=Qt.DisplayRole): if not index.isValid(): return None if index.row() >= len(self._layerStack): return None if role == Qt.DisplayRole or role == Qt.EditRole: return self._layerStack[index.row()] elif role == Qt.ToolTipRole: return self._layerStack[index.row()].toolTip() else: return None def setData(self, index, value, role=Qt.EditRole): """Replace one layer with another. DO NOT USE THIS METHOD TO INSERT NEW LAYERS! Use deleteSelected() followed by insert() or append(). """ if role == Qt.EditRole: layer = value if not isinstance(value, Layer): layer = value self._layerStack[index.row()] = layer self.dataChanged.emit(index, index) return True elif role == Qt.ToolTipRole: self._layerStack[index.row()].setToolTip() return True return False def headerData(self, section, orientation, role=Qt.DisplayRole): if role != Qt.DisplayRole: return None if orientation == Qt.Horizontal: return "Column %r" % section else: return "Row %r" % section def wantsUpdate(self): self.layoutChanged.emit() def _onLayerChanged(self, idx): self.dataChanged.emit(idx, idx) self.updateGUI() def _onSelectionChanged(self, selected, deselected): for idx in deselected.indexes(): self[idx.row()].setActive(False) for idx in selected.indexes(): self[idx.row()].setActive(True)