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(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): 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.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 ViewAssistMixin(object): selection_changed = pyqtSignal(QItemSelection, QItemSelection) # ... selected (set), deselected (set) selected_maydelete = pyqtSignal(bool, bool) # ... selected # ------------------------------------------------------------------------- # Initialization and setting data (model) # ------------------------------------------------------------------------- def __init__(self, session: Session, modal_dialog_class, readonly: bool = False, **kwargs) -> None: super().__init__(**kwargs) self.session = session self.modal_dialog_class = modal_dialog_class self.readonly = readonly self.selection_model = None def set_model_common(self, model, list_base_class) -> None: if self.selection_model: self.selection_model.selectionChanged.disconnect() list_base_class.setModel(self, model) self.selection_model = QItemSelectionModel(model) self.selection_model.selectionChanged.connect(self._selection_changed) self.setSelectionModel(self.selection_model) # ------------------------------------------------------------------------- # Selection # ------------------------------------------------------------------------- def clear_selection(self) -> None: # log.debug("GenericAttrTableView.clear_selection") if not self.selection_model: return self.selection_model.clearSelection() def get_selected_row_index(self) -> Optional[int]: """Returns an integer or None.""" selected_modelindex = self.get_selected_modelindex() if selected_modelindex is None: return None return selected_modelindex.row() def is_selected(self) -> bool: row_index = self.get_selected_row_index() return row_index is not None def get_selected_object(self) -> Optional[object]: index = self.get_selected_row_index() if index is None: return None model = self.model() if model is None: return None return model.get_object(index) def get_selected_modelindex(self) -> Optional[QModelIndex]: raise NotImplementedError() def go_to(self, row: Optional[int]) -> None: model = self.model() if row is None: # Go to the end. nrows = model.rowCount() if nrows == 0: return row = nrows - 1 modelindex = model.index(row, 0) # second parameter is column self.setCurrentIndex(modelindex) def _selection_changed(self, selected, deselected) -> None: self.selection_changed.emit(selected, deselected) selected_model_indexes = selected.indexes() selected_row_indexes = [mi.row() for mi in selected_model_indexes] is_selected = bool(selected_row_indexes) model = self.model() may_delete = is_selected and all( [model.item_deletable(ri) for ri in selected_row_indexes]) self.selected_maydelete.emit(is_selected, may_delete) def get_n_rows(self) -> int: model = self.model() return model.rowCount() # ------------------------------------------------------------------------- # Add # ------------------------------------------------------------------------- def insert_at_index(self, obj: object, index: int = None, add_to_session: bool = True, flush: bool = True) -> None: # index: None for end, 0 for start model = self.model() model.insert_at_index(obj, index, add_to_session=add_to_session, flush=flush) self.go_to(index) def insert_at_start(self, obj: object, add_to_session: bool = True, flush: bool = True) -> None: self.insert_at_index(obj, 0, add_to_session=add_to_session, flush=flush) def insert_at_end(self, obj: object, add_to_session: bool = True, flush: bool = True) -> None: self.insert_at_index(obj, None, add_to_session=add_to_session, flush=flush) def add_in_nested_transaction(self, new_object: object, at_index: int = None) -> Optional[int]: # at_index: None for end, 0 for start if self.readonly: log.warning("Can't add; readonly") return result = None try: with self.session.begin_nested(): self.session.add(new_object) win = self.modal_dialog_class(self.session, new_object) result = win.edit_in_nested_transaction() if result != QDialog.Accepted: raise EditCancelledException() self.insert_at_index(new_object, at_index, add_to_session=False) return result except EditCancelledException: log.debug("Add operation has been rolled back.") return result # ------------------------------------------------------------------------- # Remove # ------------------------------------------------------------------------- def remove_selected(self, delete_from_session: bool = True) -> None: row_index = self.get_selected_row_index() self.remove_by_index(row_index, delete_from_session=delete_from_session) def remove_by_index(self, row_index: int, delete_from_session: bool = True) -> None: if row_index is None: return model = self.model() model.delete_item(row_index, delete_from_session=delete_from_session) # ------------------------------------------------------------------------- # Move # ------------------------------------------------------------------------- def move_selected_up(self) -> None: row_index = self.get_selected_row_index() if row_index is None or row_index == 0: return model = self.model() model.move_up(row_index) self.go_to(row_index - 1) def move_selected_down(self) -> None: row_index = self.get_selected_row_index() if row_index is None or row_index == self.get_n_rows() - 1: return model = self.model() model.move_down(row_index) self.go_to(row_index + 1) # ------------------------------------------------------------------------- # Edit # ------------------------------------------------------------------------- # noinspection PyUnusedLocal def edit(self, index: QModelIndex, trigger, event) -> bool: if trigger != QAbstractItemView.DoubleClicked: return False self.edit_by_modelindex(index) return False def edit_by_modelindex(self, index: QModelIndex, readonly: bool = None) -> None: if index is None: return if readonly is None: readonly = self.readonly model = self.model() item = model.listdata[index.row()] win = self.modal_dialog_class(self.session, item, readonly=readonly) win.edit_in_nested_transaction() def edit_selected(self, readonly: bool = None) -> None: selected_modelindex = self.get_selected_modelindex() self.edit_by_modelindex(selected_modelindex, readonly=readonly)
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 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(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 GraphDigitGraphicsView(QGraphicsView): sigMouseMovePoint = pyqtSignal(QPoint, QPointF) sigModified = pyqtSignal(bool) sigNewCurveAdded = pyqtSignal() # 自定义信号sigMouseMovePoint,当鼠标移动时,在mouseMoveEvent事件中,将当前的鼠标位置发送出去 # QPoint--传递的是view坐标 def __init__(self, parent=None): super(GraphDigitGraphicsView, self).__init__(parent) # scene rect = QRectF(0, 0, 300, 400) self.scene = QGraphicsScene(rect) # 创建场景 参数:场景区域 self.setScene(self.scene) # 给视图窗口设置场景 # image self.graphicsPixmapItem = QGraphicsPixmapItem() # chart image self.scene.addItem(self.graphicsPixmapItem) # setAntialias self.setRenderHint(QPainter.Antialiasing) # image object stored in project data # item objects storage self.axesxObjs = {} self.axesyObjs = {} self.gridObjs = [] self.curveObjs = {} self.pointObjs = {} # axis coord stored in project data # grid setting stored in project data # grid position self.gridxpos = [] self.gridypos = [] # axes curve and point datamodel self.axesxModel = QStandardItemModel() self.axesyModel = QStandardItemModel() self.axesxSelectModel = QItemSelectionModel(self.axesxModel) self.axesySelectModel = QItemSelectionModel(self.axesyModel) self.curveModel = QStandardItemModel() self.pointsModel = QStandardItemModel() self.curveModel.setHorizontalHeaderLabels( ["current", "name", "visible"]) self.pointsModel.setHorizontalHeaderLabels(["order", "x", "y"]) self.axesxModel.setHorizontalHeaderLabels(["position", "x"]) self.axesyModel.setHorizontalHeaderLabels(["position", "y"]) ### self.xNo = 0 self.yNo = 0 ### self.mode = OpMode.default # data manage self.proj = Digi() # init self.currentCurve = None self.pointsModel.itemChanged.connect(self.changePointOrder) self.curveModel.itemChanged.connect(self.changeCurveVisible) self.scene.selectionChanged.connect(self.slotSceneSeletChanged) # state self._lastCurve = None def load(self, proj): # image # only for load self.scaleGraphImage(proj.imgScale) # axes xadded = [] yadded = [] for xpos, xcoord in proj.data["axesxObjs"].items(): if xpos in xadded: continue else: xadded.append(xpos) item = QGraphicsAxesItem( 0, self.scene.sceneRect().y(), 0, self.scene.sceneRect().y() + self.scene.sceneRect().height()) item.setFlags(QGraphicsItem.ItemIsSelectable | QGraphicsItem.ItemIsFocusable | QGraphicsItem.ItemIsMovable) item.setPos(xpos, 0) item.axis = "x" item.setPen(QPen(Qt.red, 1, Qt.DashLine)) self.scene.addItem(item) self.axesxObjs[item] = xcoord labelitem = QStandardItem("x:{}".format(xpos)) labelitem.setData(item) self.axesxModel.appendRow([labelitem, QStandardItem(str(xcoord))]) self.xNo += 1 self.axesxModel.setVerticalHeaderItem( self.axesxModel.rowCount() - 1, QStandardItem("x{}".format(self.xNo))) for ypos, ycoord in proj.data["axesyObjs"].items(): if ypos in yadded: continue else: yadded.append(ypos) item = QGraphicsAxesItem( self.scene.sceneRect().x(), 0, self.scene.sceneRect().x() + self.scene.sceneRect().width(), 0) item.setFlags(QGraphicsItem.ItemIsSelectable | QGraphicsItem.ItemIsFocusable | QGraphicsItem.ItemIsMovable) item.setPos(0, ypos) item.axis = "y" item.setPen(QPen(Qt.red, 1, Qt.DashLine)) self.scene.addItem(item) self.axesyObjs[item] = ycoord labelitem = QStandardItem("y:{}".format(ypos)) labelitem.setData(item) self.axesyModel.appendRow([labelitem, QStandardItem(str(ycoord))]) # curve for curve in proj.data["curves"]: self.pointObjs[curve] = [] self.addCurve(curve) clr = RandItem.nextColor() for x, y in proj.data["curves"][curve]: ptitem = QGraphicsPointItem() ptitem.pointColor = clr ptitem.linewidth = 1 ptitem.setPos(x, y) ptitem.parentCurve = curve ptitem.setFlags(QGraphicsItem.ItemIsSelectable | QGraphicsItem.ItemIsFocusable | QGraphicsItem.ItemIsMovable) i = pointInsertPosition(ptitem, self.pointObjs[self.currentCurve]) self.pointObjs[self.currentCurve].insert(i, ptitem) self.scene.addItem(ptitem) self.updateCurve(curve) # grid self.calGridCoord() self.updateGrid() self.sigModified.emit(True) self.mode = OpMode.select def resetview(self): self.setGraphImage(None) self.scaleGraphImage(1) for obj in self.axesxObjs: self.scene.removeItem(obj) self.axesxObjs = {} for obj in self.axesyObjs: self.scene.removeItem(obj) self.axesyObjs = {} for obj in self.gridObjs: self.scene.removeItem(obj) self.gridObjs = [] for curve in self.curveObjs: for obj in self.curveObjs[curve]: self.scene.removeItem(obj) self.curveObjs = {} for curve in self.pointObjs: for obj in self.pointObjs[curve]: self.scene.removeItem(obj) self.pointObjs = {} self.axesxModel.clear() self.axesyModel.clear() self.curveModel.clear() self.pointsModel.clear() self.axesxSelectModel.clear() self.axesySelectModel.clear() self.curveModel.setHorizontalHeaderLabels( ["current", "name", "visible"]) self.pointsModel.setHorizontalHeaderLabels(["order", "x", "y"]) self.axesxModel.setHorizontalHeaderLabels(["position", "x"]) self.axesyModel.setHorizontalHeaderLabels(["position", "y"]) self.xNo = 0 self.yNo = 0 ### self.mode = OpMode.default # data manage self.proj.resetData(True) # init self.currentCurve = None self._lastCurve = None def dump(self): proj = self.proj proj.data["axesxObjs"] = {} # [x1,x2,x3] proj.data["axesyObjs"] = {} # [y1,y2,y3] proj.data["curves"] = {} # {'default':(x1,y1),(x2,y2)} # axes for item, xcoord in self.axesxObjs.items(): proj.data["axesxObjs"][item.pos().x()] = xcoord for item, ycoord in self.axesyObjs.items(): proj.data["axesyObjs"][item.pos().y()] = ycoord # curve for curve in self.pointObjs: proj.data["curves"][curve] = [] for item in self.pointObjs[curve]: proj.data["curves"][curve].append((item.x(), item.y())) def mouseMoveEvent(self, evt): pt = evt.pos() # 获取鼠标坐标--view坐标 self.sigMouseMovePoint.emit(pt, self.mapToScene(pt)) # 发送鼠标位置 QGraphicsView.mouseMoveEvent(self, evt) item = self.scene.focusItem() if not item: items = self.scene.selectedItems() if len(items) != 1: return else: item = items[0] if item: if isinstance(item, QGraphicsPointItem) and item.parentCurve: # self.changeCurrentCurve(item.parentCurve) self.updateCurve(self.currentCurve, Qt.red) self.updateCurvePoints(self.currentCurve) self.sigModified.emit(True) elif isinstance(item, QGraphicsAxesItem): if item.axis == "x": if self.mode != OpMode.axesx: self.scene.clearSelection() return self.axesxSelectModel.clearSelection() self.axesySelectModel.clearSelection() for i in range(self.axesxModel.rowCount()): if self.axesxModel.item(i, 0).data() is item: parent = QModelIndex() topleftindex = self.axesxModel.index( i, 0, parent) # self.axesxModel.item(i,0).index() rightbottomindex = self.axesxModel.index( i, 1, parent) self.axesxSelectModel.select( QItemSelection(topleftindex, rightbottomindex), QItemSelectionModel.Select) self.axesxModel.item(i, 0).setText("x:{}".format( evt.pos().x())) break item.setLine( 0, self.scene.sceneRect().y(), 0, self.scene.sceneRect().y() + self.scene.sceneRect().height()) item.setPos(self.mapToScene(evt.pos()).x(), 0) self.sigModified.emit(True) self.calGridCoord() self.updateGrid() elif item.axis == "y": if self.mode != OpMode.axesy: self.scene.clearSelection() return self.axesxSelectModel.clearSelection() self.axesySelectModel.clearSelection() for i in range(self.axesyModel.rowCount()): if self.axesyModel.item(i, 0).data() is item: topleftindex = self.axesyModel.index( i, 0) # self.axesxModel.item(i,0).index() rightbottomindex = self.axesyModel.index(i, 1) self.axesySelectModel.select( QItemSelection(topleftindex, rightbottomindex), QItemSelectionModel.Select) self.axesyModel.item(i, 0).setText("y:{}".format( evt.pos().y())) break item.setLine( self.scene.sceneRect().x(), 0, self.scene.sceneRect().x() + self.scene.sceneRect().width(), 0) item.setPos(0, self.mapToScene(evt.pos()).y()) self.sigModified.emit(True) self.calGridCoord() self.updateGrid() # self.updateCurve(self.currentCurve) # self.repaint() # self.setDragMode(QGraphicsView.NoDrag) #(RubberBandDrag) #ScrollHandDrag) #NoDrag) def mousePressEvent(self, event): super().mousePressEvent(event) self.__pressPt = event.pos() ptscene = self.mapToScene(event.pos()) if self.mode is OpMode.axesx: for axisitem in self.axesxObjs: if abs(ptscene.x() - axisitem.pos().x()) < 5: self.scene.clearSelection() axisitem.setSelected(True) return elif self.mode is OpMode.axesy: for axisitem in self.axesyObjs: if abs(ptscene.y() - axisitem.pos().y()) < 5: self.scene.clearSelection() axisitem.setSelected(True) return def keyPressEvent(self, event: QKeyEvent) -> None: # super().keyPressEvent(event) item = self.scene.focusItem() if not item: items = self.scene.selectedItems() if len(items) != 1: return else: item = items[0] if item: if isinstance(item, QGraphicsPointItem) and item.parentCurve: if event.key() == Qt.Key_Up: item.setPos(item.pos().x(), item.pos().y() - 1) elif event.key() == Qt.Key_Down: item.setPos(item.pos().x(), item.pos().y() + 1) elif event.key() == Qt.Key_Left: item.setPos(item.pos().x() - 1, item.pos().y()) elif event.key() == Qt.Key_Right: item.setPos(item.pos().x() + 1, item.pos().y()) else: return self.updateCurve(self.currentCurve, Qt.red) self.updateCurvePoints(self.currentCurve) self.sigModified.emit(True) elif isinstance(item, QGraphicsAxesItem): if item.axis == "x": if event.key() == Qt.Key_Left: item.moveBy(-1, 0) elif event.key() == Qt.Key_Right: item.moveBy(1, 0) else: return self.sigModified.emit(True) self.calGridCoord() self.updateGrid() self.axesxSelectModel.clearSelection() self.axesySelectModel.clearSelection() for i in range(self.axesxModel.rowCount()): if self.axesxModel.item(i, 0).data() is item: parent = QModelIndex() topleftindex = self.axesxModel.index( i, 0, parent) # self.axesxModel.item(i,0).index() rightbottomindex = self.axesxModel.index( i, 1, parent) self.axesxSelectModel.select( QItemSelection(topleftindex, rightbottomindex), QItemSelectionModel.Select) self.axesxModel.item(i, 0).setText("x:{}".format( item.pos().x())) break elif item.axis == "y": if event.key() == Qt.Key_Up: item.setPos(0, item.pos().y() - 1) elif event.key() == Qt.Key_Down: item.setPos(0, item.pos().y() + 1) else: return self.sigModified.emit(True) self.calGridCoord() self.updateGrid() self.axesxSelectModel.clearSelection() self.axesySelectModel.clearSelection() for i in range(self.axesyModel.rowCount()): if self.axesyModel.item(i, 0).data() is item: topleftindex = self.axesyModel.index( i, 0) # self.axesxModel.item(i,0).index() rightbottomindex = self.axesyModel.index(i, 1) self.axesySelectModel.select( QItemSelection(topleftindex, rightbottomindex), QItemSelectionModel.Select) self.axesyModel.item(i, 0).setText("y:{}".format( item.pos().y())) break def mouseReleaseEvent(self, event): super().mouseReleaseEvent(event) ptscene = self.mapToScene(event.pos()) # item = self.scene.itemAt(ptscene, self.transform()) clicked = True if event.pos() == self.__pressPt else False if self.mode is OpMode.select: pass # super().mousePressEvent(event) elif self.mode is OpMode.axesx and clicked: self.axesxSelectModel.clear() self.axesySelectModel.clear() for axisitem in self.axesxObjs: if abs(ptscene.x() - axisitem.pos().x()) < 5: self.scene.clearSelection() axisitem.setSelected(True) return item = QGraphicsAxesItem( 0, self.scene.sceneRect().y(), 0, self.scene.sceneRect().y() + self.scene.sceneRect().height()) item.setPos(ptscene.x(), 0) item.axis = "x" item.setPen(QPen(Qt.red, 1, Qt.DashLine)) self.scene.addItem(item) item.setFlags(QGraphicsItem.ItemIsSelectable | QGraphicsItem.ItemIsFocusable | QGraphicsItem.ItemIsMovable) xs = list(self.axesxObjs.values()) if not self.axesxObjs: nextx = 0 elif len(self.axesxObjs) == 1: nextx = xs[0] + 0.1 else: nextx = 2 * xs[-1] - xs[-2] x, okPressed = QInputDialog.getDouble( self, self.tr("set x coordiniate"), self.tr("set the x coord for axis"), nextx, decimals=3) if okPressed and x not in self.axesxObjs.values(): self.axesxObjs[item] = x labelItem = QStandardItem("x:{}".format(item.pos().x())) labelItem.setData(item) self.axesxModel.appendRow([labelItem, QStandardItem(str(x))]) self.calGridCoord("x") self.updateGrid() # item.setSelected(True) for i in range(self.axesxModel.rowCount()): if self.axesxModel.item(i, 0).data() is item: topleftindex = self.axesxModel.index( i, 0) # self.axesxModel.item(i,0).index() rightbottomindex = self.axesxModel.index(i, 1) self.axesxSelectModel.select( QItemSelection(topleftindex, rightbottomindex), QItemSelectionModel.Select) break self.sigModified.emit(True) self.scene.clearSelection() else: self.scene.removeItem(item) elif self.mode is OpMode.axesy and clicked: self.axesxSelectModel.clear() self.axesySelectModel.clear() for axisitem in self.axesyObjs: if abs(ptscene.y() - axisitem.pos().y()) < 5: self.scene.clearSelection() axisitem.setSelected(True) return item = QGraphicsAxesItem( self.scene.sceneRect().x(), 0, self.scene.sceneRect().x() + self.scene.sceneRect().width(), 0) item.setPos(0, ptscene.y()) item.axis = "y" item.setPen(QPen(Qt.red, 1, Qt.DashLine)) self.scene.addItem(item) item.setFlags(QGraphicsItem.ItemIsSelectable | QGraphicsItem.ItemIsFocusable | QGraphicsItem.ItemIsMovable) ys = list(self.axesyObjs.values()) if not self.axesyObjs: nexty = 0 elif len(self.axesyObjs) == 1: nexty = ys[0] + 0.1 else: nexty = 2 * ys[-1] - ys[-2] y, okPressed = QInputDialog.getDouble( self, self.tr("set y coordiniate"), self.tr("set the y coord for axis"), nexty, decimals=3) if okPressed and y not in self.axesyObjs.values(): self.axesyObjs[item] = y labelItem = QStandardItem("y:{}".format(item.pos().y())) labelItem.setData(item) self.axesyModel.appendRow([labelItem, QStandardItem(str(y))]) self.calGridCoord("y") self.updateGrid() # item.setSelected(True) for i in range(self.axesyModel.rowCount()): if self.axesyModel.item(i, 0).data() is item: topleftindex = self.axesyModel.index( i, 0) # self.axesxModel.item(i,0).index() rightbottomindex = self.axesyModel.index(i, 1) self.axesySelectModel.select( QItemSelection(topleftindex, rightbottomindex), QItemSelectionModel.Select) break self.sigModified.emit(True) else: self.scene.removeItem(item) elif self.mode is OpMode.curve and clicked: self.sigMouseMovePoint.emit(event.pos(), ptscene) if len(self.curveObjs) == 0: self.addCurve('curve1') if self.currentCurve not in self.pointObjs: self.pointObjs[self.currentCurve] = [] ptitem = QGraphicsPointItem() ptitem.pointColor = Qt.blue ptitem.linewidth = 1 ptitem.setPos(ptscene) ptitem.parentCurve = self.currentCurve ptitem.setFlags(QGraphicsItem.ItemIsSelectable | QGraphicsItem.ItemIsFocusable | QGraphicsItem.ItemIsMovable) self.scene.addItem(ptitem) i = pointInsertPosition(ptitem, self.pointObjs[self.currentCurve]) self.pointObjs[self.currentCurve].insert(i, ptitem) self.updateCurve(self.currentCurve, Qt.red) self.sigModified.emit(True) ptitem.setSelected(True) # item1=QGraphicsRectItem(rect) #创建矩形---以场景为坐标 # item1.setFlags(QGraphicsItem.ItemIsSelectable|QGraphicsItem.ItemIsFocusable|QGraphicsItem.ItemIsMovable) #给图元设置标志 # QGraphicsItem.ItemIsSelectable---可选择 # QGraphicsItem.ItemIsFocusable---可设置焦点 # QGraphicsItem.ItemIsMovable---可移动 # QGraphicsItem.ItemIsPanel--- # self.scene.addItem(item1) #给场景添加图元 def slotSceneSeletChanged(self): items = self.scene.selectedItems() if len(items) != 1: # ony allow select one item self.scene.clearSelection() return # item = items[0] item = self.scene.focusItem() if not item: items = self.scene.selectedItems() if len(items) != 1: return else: item = items[0] if item: if isinstance(item, QGraphicsPointItem) and item.parentCurve: self.changeCurrentCurve(item.parentCurve) def deleteItem(self, item): """delete point on curve or axis object""" curvechange = None if isinstance(item, QGraphicsPointItem): for curvename, pointitems in self.pointObjs.items(): for ptitem in pointitems: if ptitem is item: curvechange = curvename pointitems.remove(ptitem) self.scene.removeItem(item) self.sigModified.emit(True) break if curvechange: self.updateCurve(curvechange) if isinstance(item, QGraphicsAxesItem): if item.axis == "x": for line in self.axesxObjs: if line is item: for i in range(self.axesxModel.rowCount()): if self.axesxModel.item(i, 0).data( ) is item: # float(self.axesxModel.item(i, 0).text().strip("x:")) == line.pos().x(): self.axesxModel.removeRow(i) break self.axesxObjs.pop(line) self.scene.removeItem(line) self.sigModified.emit(True) break elif item.axis == "y": for line in self.axesyObjs: if line is item: for i in range(self.axesyModel.rowCount()): if float( self.axesyModel.item(i, 0).text().strip( "y:")) == line.pos().y(): self.axesyModel.removeRow(i) break self.axesyObjs.pop(line) self.scene.removeItem(line) self.sigModified.emit(True) break self.calGridCoord() self.updateGrid() def deleteSelectedItem(self): pointitems = self.scene.selectedItems() if len(pointitems) == 1: self.deleteItem(pointitems[0]) def setGraphImage(self, imgfile): if not isinstance(imgfile, str): img = QPixmap(300, 400) img.fill(QColor("#EEE")) self.graphicsPixmapItem.setPixmap(img) self.scene.setSceneRect(0, 0, 300, 400) elif os.path.exists(imgfile): img = QPixmap(imgfile) self.graphicsPixmapItem.setPixmap(img) self.scene.setSceneRect(0, 0, img.width(), img.height()) self.scene.clearSelection() # 【清除选择】 self.sigModified.emit(True) return True else: return False def scaleGraphImage(self, scale=1): if scale and scale > 0: self.proj.imgScale = scale self.graphicsPixmapItem.setScale(scale) if self.graphicsPixmapItem.pixmap().width( ) > 0 and self.graphicsPixmapItem.pixmap().height() > 0: self.scene.setSceneRect( 0, 0, self.graphicsPixmapItem.pixmap().width() * scale, self.graphicsPixmapItem.pixmap().height() * scale) self.scene.clearSelection() # 【清除选择】 self.sigModified.emit(True) def addCurve(self, name=None): if not name: name = nextName(self.currentCurve) while (name in self.curveObjs): name = nextName(name) self.curveObjs[name] = [] self.pointObjs[name] = [] item1 = IconItem() item2 = QStandardItem(name) item3 = QStandardItem() item1.setEditable(False) item3.setCheckable(True) item3.setAutoTristate(False) item3.setEditable(False) item3.setCheckState(Qt.Checked) item1.setTextAlignment(Qt.AlignCenter) item2.setTextAlignment(Qt.AlignCenter) item3.setTextAlignment(Qt.AlignCenter) self.curveModel.appendRow([item1, item2, item3]) self.changeCurrentCurve(name) self.sigModified.emit(True) self.sigNewCurveAdded.emit() def renameCurve(self, newname=None, name=None): if name not in self.curveObjs: name = self.currentCurve if not newname: newname, okPressed = QInputDialog.getText( self, self.tr("change curve name"), self.tr("Curve to be renamed:{}".format(name)), QLineEdit.Normal, name) if okPressed and newname != '': if newname != name: self.curveObjs[newname] = self.curveObjs.pop(name) self.pointObjs[newname] = self.pointObjs.pop(name) for i in range(self.curveModel.rowCount()): item = self.curveModel.item(i, 1) if item.text() == name: item.setText(newname) self.sigModified.emit(True) self.changeCurrentCurve(newname) def delCurve(self, name=None): if name is None: name = self.currentCurve if name not in self.curveObjs: return try: self.curveObjs.pop(name) self.pointObjs.pop(name) for i in range(self.curveModel.rowCount()): item = self.curveModel.item(i, 1) if item.text() == name: self.curveModel.removeRows(i, 1) self.sigModified.emit(True) if len(self.curveObjs) > 0: self.changeCurrentCurve(list(self.curveObjs.keys())[0]) else: self.currentCurve = None except: pass def showCurve(self, curvename, visible=True): for pts in self.pointObjs[curvename]: pts.setVisible(visible) for line in self.curveObjs[curvename]: line.setVisible(visible) def updateCurve(self, name, color=Qt.black): # if name in self.curveObjs: # curveitem = self.curveObjs[name] # else: # curveitem = QGraphicsPathItem() # self.scene.addItem(curveitem) # # path=curveitem.path() # path = QPainterPath() # # pointItems = self.curvePointObjs[name] # if len(pointItems) > 0: # path.moveTo(pointItems[0].pos()) # for pointitem in pointItems[1:]: # path.lineTo(pointitem.pos()) # curveitem.setPath(path) # curveitem.update(curveitem.boundingRect()) # curveitem.prepareGeometryChange() # self.scene.update() # self.viewport().repaint() # self.viewport().update() if not isinstance(name, str): return if name not in self.pointObjs: return lastitems = [] if name in self.curveObjs: lastitems = self.curveObjs[name] if not isinstance(lastitems, list): lastitems = [] if name in self.pointObjs: pointItems = self.pointObjs[name] else: pointItems = [] points = [] for ptitem in pointItems: points.append(ptitem.pos()) self.curveObjs[name] = [] if len(points) > 1: for i in range(1, len(points)): l = QGraphicsLineItem(points[i - 1].x(), points[i - 1].y(), points[i].x(), points[i].y()) l.setPen(color) l.setZValue(10) # l.setFlag(QGraphicsItem.ItemIsSelectable) self.curveObjs[name].append(l) self.scene.addItem(l) for line in lastitems: self.scene.removeItem(line) self.updateCurvePoints(name) def showAxes(self, visible=True): if visible: for line in self.axesxObjs: line.setVisible(True) for line in self.axesyObjs: line.setVisible(True) else: for line in self.axesxObjs: line.setVisible(False) for line in self.axesyObjs: line.setVisible(False) def showGrid(self, visible=True): if visible: for line in self.gridObjs: line.setVisible(True) else: for line in self.gridObjs: line.setVisible(False) def calGridCoord(self, mode="all"): """calc the coord and pixel position of gridx list and gridy list""" if mode in ("x", "all") and len(self.axesxObjs) >= 2: axesxcoord = list(self.axesxObjs.values()) xmin = min(axesxcoord) if self.proj.gridx[0] is None else min( self.proj.gridx[0], min(axesxcoord)) xmax = max(axesxcoord) if self.proj.gridx[1] is None else max( self.proj.gridx[1], max(axesxcoord)) if self.proj.gridx[2] is None: if len(self.axesxObjs) == 2: xstep = (xmax - xmin) / 5 else: axesStep = round(abs(axesxcoord[1] - axesxcoord[0]), 5) for i in range(2, len(axesxcoord)): st = round(abs(axesxcoord[i] - axesxcoord[i - 1]), 5) if axesStep > st: axesStep = st xstep = axesStep else: xstep = self.proj.gridx[2] n = int(round((xmax - xmin) / xstep, 0)) + 1 gridxcoord = list(np.linspace(xmin, xmax, n)) else: gridxcoord = [] if mode in ("y", "all") and len(self.axesyObjs) >= 2: axesycoord = list(self.axesyObjs.values()) ymin = min(axesycoord) if self.proj.gridy[0] is None else min( self.proj.gridy[0], min(axesycoord)) ymax = max(axesycoord) if self.proj.gridy[1] is None else max( self.proj.gridy[1], max(axesycoord)) if self.proj.gridy[2] is None: if len(self.axesyObjs) == 2: ystep = (ymax - ymin) / 5 else: axesStep = round(abs(axesycoord[1] - axesycoord[0]), 5) for i in range(2, len(axesycoord)): st = round(axesycoord[i] - axesycoord[i - 1], 5) if axesStep > st: axesStep = st ystep = axesStep else: ystep = self.proj.gridy[2] n = int(round((ymax - ymin) / ystep, 0)) + 1 gridycoord = list(np.linspace(ymin, ymax, n)) # gridycoord = list(np.arange(ymin, ymax, ystep)) + [ymax] else: gridycoord = [] xpos, ypos = self.coordToPoint(gridxcoord, gridycoord) if mode in ["x", "all"]: self.gridxpos = xpos if mode in ["y", "all"]: self.gridypos = ypos return def updateGrid(self): for line in self.gridObjs: self.scene.removeItem(line) if self.gridxpos and self.gridypos: clr = QColor(self.proj.gridColor) clr.setAlphaF(self.proj.gridOpacity) for x in self.gridxpos: line = QGraphicsLineItem(x, self.gridypos[0], x, self.gridypos[-1]) line.setZValue(5) line.setPen( QPen(clr, self.proj.gridLineWidth, self.proj.gridLineType)) self.gridObjs.append(line) self.scene.addItem(line) for y in self.gridypos: line = QGraphicsLineItem(self.gridxpos[0], y, self.gridxpos[-1], y) line.setZValue(5) line.setPen( QPen(clr, self.proj.gridLineWidth, self.proj.gridLineType)) self.gridObjs.append(line) self.scene.addItem(line) def updateCurvePoints(self, name): extra = len(self.pointObjs[name]) - self.pointsModel.rowCount() if extra > 0: for i in range(extra): item1 = QStandardItem() item2 = QStandardItem() item3 = QStandardItem() item2.setEditable(False) item3.setEditable(False) self.pointsModel.appendRow([item1, item2, item3]) elif extra < 0: j = self.pointsModel.rowCount() i = j + extra self.pointsModel.removeRows(i, -extra) for i in range(self.pointsModel.rowCount()): pt = self.pointObjs[name][i] self.pointsModel.item(i, 0).setText(str(i + 1)) xlist, ylist = self.pointToCoord([pt.x()], [pt.y()]) self.pointsModel.item(i, 1).setText(str(round(xlist[0], 6))) self.pointsModel.item(i, 2).setText(str(round(ylist[0], 6))) def calcCurveCoords(self): """calculate datas for export""" data = {} for curve in self.pointObjs: data[curve] = ([], []) for item in self.pointObjs[curve]: data[curve][0].append(item.x()) data[curve][1].append(item.y()) data[curve] = self.pointToCoord(data[curve][0], data[curve][1]) return data def axisvalid(self): """if there are axes with same coord value, or if there are axes at the save position, return False""" a = len(self.axesxObjs) b = len(set(self.axesxObjs.values())) if a != b: return False a = len(self.axesyObjs) b = len(set(self.axesyObjs.values())) if a != b: return False xs = [] for item in self.axesxObjs: xs.append(item.pos().x()) ys = [] for item in self.axesyObjs: ys.append(item.pos().y()) a = len(xs) b = len(set(xs)) if a != b: return False a = len(ys) b = len(set(ys)) if a != b: return False return True def exportToCSVtext(self): """return text in csv format, like following: curve1 x,1,2,3 y,2,3,4 """ text = "" data = self.calcCurveCoords() for curve in data: text += curve text += "\nx," for x in data[curve][0]: text += str(x) + ',' text += "\ny," for y in data[curve][1]: text += str(y) + ',' text += "\n" return text def changeCurrentCurve(self, name=None): self._lastCurve = self.currentCurve if name is None: name = self.currentCurve if name is None: return for i in range(self.curveModel.rowCount()): if self.curveModel.item(i, 1).text() == name: self.curveModel.item(i, 0).switch(True) else: self.curveModel.item(i, 0).switch(False) self.currentCurve = name self.updateCurve(self._lastCurve) self.updateCurve(self.currentCurve, Qt.red) self.updateCurvePoints(name) def changeCurveVisible(self, item): if item.index().column() == 2: visible = item.checkState() curvename = self.curveModel.item(item.index().row(), 1).text() self.showCurve(curvename, visible) def changePointOrder(self, item): row = item.row() if item.column() != 0: return newindex = item.text() if not newindex.isdigit(): return newindex = int(newindex) if newindex == row + 1: return if newindex > self.pointsModel.rowCount(): newindex = self.pointsModel.rowCount() newindex -= 1 self.pointObjs[self.currentCurve].insert( newindex, self.pointObjs[self.currentCurve].pop(row)) self.updateCurve(self.currentCurve) self.updateCurvePoints(self.currentCurve) # def curvetabChanged(self, item): # i = item.row() # j = item.column() # if j == 0: # if item.checkState() is Qt.Checked: # self.curveModel.item(i,0).setCheckState(Qt.Checked) # return # else: # for k in range(self.curveModel.rowCount()): # if k == i: # #self.curveModel.item(k,0).setCheckState(Qt.Checked) # newcurrent = self.curveModel.item(k,1).text() # print(self.curveModel.item(k,0).checkState()) # else: # self.curveModel.item(k,0).setCheckState(Qt.Unchecked) # if newcurrent and newcurrent != self.currentCurve: # self.changeCurrentCurve(newcurrent) def pointToCoord(self, xlist, ylist): if len(self.axesxObjs) >= 2: gridxs = [] for item in self.axesxObjs: gridxs.append(item.pos().x()) coordx = list(self.axesxObjs.values()) xCoords = interp(gridxs, coordx, xlist) else: xCoords = xlist if len(self.axesyObjs) >= 2: gridys = [] for item in self.axesyObjs: gridys.append(item.pos().y()) coordy = list(self.axesyObjs.values()) yCoords = interp(gridys, coordy, ylist) else: yCoords = ylist return (xCoords, yCoords) def coordToPoint(self, xlist, ylist): if len(self.axesxObjs) >= 2: gridxpos = [] for item in self.axesxObjs: gridxpos.append(item.pos().x()) coordx = list(self.axesxObjs.values()) xposs = interp(coordx, gridxpos, xlist) else: xposs = xlist if len(self.axesyObjs) >= 2: gridypos = [] for item in self.axesyObjs: gridypos.append(item.pos().y()) coordy = list(self.axesyObjs.values()) yposs = interp(coordy, gridypos, ylist) else: yposs = ylist return (xposs, yposs)
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.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 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(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 ViewAssistMixin(object): selection_changed = pyqtSignal(QItemSelection, QItemSelection) # ... selected (set), deselected (set) selected_maydelete = pyqtSignal(bool, bool) # ... selected # ------------------------------------------------------------------------- # Initialization and setting data (model) # ------------------------------------------------------------------------- def __init__(self, session: Session, modal_dialog_class, readonly: bool = False, **kwargs) -> None: super().__init__(**kwargs) self.session = session self.modal_dialog_class = modal_dialog_class self.readonly = readonly self.selection_model = None def set_model_common(self, model, list_base_class) -> None: if self.selection_model: self.selection_model.selectionChanged.disconnect() list_base_class.setModel(self, model) self.selection_model = QItemSelectionModel(model) self.selection_model.selectionChanged.connect(self._selection_changed) self.setSelectionModel(self.selection_model) # ------------------------------------------------------------------------- # Selection # ------------------------------------------------------------------------- def clear_selection(self) -> None: # log.debug("GenericAttrTableView.clear_selection") if not self.selection_model: return self.selection_model.clearSelection() def get_selected_row_index(self) -> Optional[int]: """Returns an integer or None.""" selected_modelindex = self.get_selected_modelindex() if selected_modelindex is None: return None return selected_modelindex.row() def is_selected(self) -> bool: row_index = self.get_selected_row_index() return row_index is not None def get_selected_object(self) -> Optional[object]: index = self.get_selected_row_index() if index is None: return None model = self.model() if model is None: return None return model.get_object(index) def get_selected_modelindex(self) -> Optional[QModelIndex]: raise NotImplementedError() def go_to(self, row: Optional[int]) -> None: model = self.model() if row is None: # Go to the end. nrows = model.rowCount() if nrows == 0: return row = nrows - 1 modelindex = model.index(row, 0) # second parameter is column self.setCurrentIndex(modelindex) def _selection_changed(self, selected, deselected) -> None: self.selection_changed.emit(selected, deselected) selected_model_indexes = selected.indexes() selected_row_indexes = [mi.row() for mi in selected_model_indexes] is_selected = bool(selected_row_indexes) model = self.model() may_delete = is_selected and all( [model.item_deletable(ri) for ri in selected_row_indexes]) self.selected_maydelete.emit(is_selected, may_delete) def get_n_rows(self) -> int: model = self.model() return model.rowCount() # ------------------------------------------------------------------------- # Add # ------------------------------------------------------------------------- def insert_at_index(self, obj: object, index: int = None, add_to_session: bool = True, flush: bool = True) -> None: # index: None for end, 0 for start model = self.model() model.insert_at_index(obj, index, add_to_session=add_to_session, flush=flush) self.go_to(index) def insert_at_start(self, obj: object, add_to_session: bool = True, flush: bool = True) -> None: self.insert_at_index(obj, 0, add_to_session=add_to_session, flush=flush) def insert_at_end(self, obj: object, add_to_session: bool = True, flush: bool = True) -> None: self.insert_at_index(obj, None, add_to_session=add_to_session, flush=flush) def add_in_nested_transaction(self, new_object: object, at_index: int = None) -> Optional[int]: # at_index: None for end, 0 for start if self.readonly: log.warning("Can't add; readonly") return result = None try: with self.session.begin_nested(): self.session.add(new_object) win = self.modal_dialog_class(self.session, new_object) result = win.edit_in_nested_transaction() if result != QDialog.Accepted: raise EditCancelledException() self.insert_at_index(new_object, at_index, add_to_session=False) return result except EditCancelledException: log.debug("Add operation has been rolled back.") return result # ------------------------------------------------------------------------- # Remove # ------------------------------------------------------------------------- def remove_selected(self, delete_from_session: bool = True) -> None: row_index = self.get_selected_row_index() self.remove_by_index(row_index, delete_from_session=delete_from_session) def remove_by_index(self, row_index: int, delete_from_session: bool = True) -> None: if row_index is None: return model = self.model() model.delete_item(row_index, delete_from_session=delete_from_session) # ------------------------------------------------------------------------- # Move # ------------------------------------------------------------------------- def move_selected_up(self) -> None: row_index = self.get_selected_row_index() if row_index is None or row_index == 0: return model = self.model() model.move_up(row_index) self.go_to(row_index - 1) def move_selected_down(self) -> None: row_index = self.get_selected_row_index() if row_index is None or row_index == self.get_n_rows() - 1: return model = self.model() model.move_down(row_index) self.go_to(row_index + 1) # ------------------------------------------------------------------------- # Edit # ------------------------------------------------------------------------- # noinspection PyUnusedLocal def edit(self, index: QModelIndex, trigger, event) -> bool: if trigger != QAbstractItemView.DoubleClicked: return False self.edit_by_modelindex(index) return False def edit_by_modelindex(self, index: QModelIndex, readonly: bool = None) -> None: if index is None: return if readonly is None: readonly = self.readonly model = self.model() item = model.listdata[index.row()] win = self.modal_dialog_class(self.session, item, readonly=readonly) win.edit_in_nested_transaction() def edit_selected(self, readonly: bool = None) -> None: selected_modelindex = self.get_selected_modelindex() self.edit_by_modelindex(selected_modelindex, readonly=readonly)