在PyQt中设置的如下的窗口:
其中的图标是通过新建Resource File加入的
images里面的图片可以在这里面取:
链接:https://pan.baidu.com/s/1gOgBpW7s-ZWn_5aRoaYLkQ
提取码:jyjy
我们把这个文件取名为res.qrc
资源文件的使用可以看这里:http://t.csdn.cn/ba4X4
因为结构有些复杂,对于窗口的ui文件可以从这里自取
链接:https://pan.baidu.com/s/1NKFRtbxS_xE9eQq_bd_hnA
提取码:jyjy
1.建立如下数据库:
2.新建appMain文件
import sys
from PyQt6.QtWidgets import QApplication
from myMainWindow import QmyMainWindow
app=QApplication(sys.argv)#构造GUI应用程序
mainform=QmyMainWindow() #创建主窗体
mainform.show() #显示主窗体
sys.exit(app.exec())
3.新建myMainWindow文件
其中def on_actOpenDB_triggered是通过添加槽函数得来的
点击后选择trigger信号
其他按钮操作类似,这里不再一一演示:
import sys
from PyQt6.QtWidgets import (QApplication,QMainWindow, QMessageBox,
QAbstractItemView, QDataWidgetMapper)
from PyQt6.QtSql import QSqlDatabase, QSqlTableModel
from PyQt6.QtCore import pyqtSlot,Qt, QItemSelectionModel, QModelIndex
from Ui_MainWindow import Ui_MainWindow
from myDelegates import QmyComboBoxDelegate
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) #将splitter放在窗体中间
#限制选择时只能选择一行
self.ui.tableView.setSelectionBehavior(QAbstractItemView.SelectionBehavior.
SelectItems)
self.ui.tableView.setSelectionMode(QAbstractItemView.SelectionMode.
SingleSelection)
self.ui.tableView.setAlternatingRowColors(True)
self.ui.tableView.verticalHeader().setDefaultSectionSize(22)
self.ui.tableView.horizontalHeader().setDefaultSectionSize(100)
@pyqtSlot()
def on_actOpenDB_triggered(self):
self.DB=QSqlDatabase.addDatabase("QODBC")
self.DB.setDatabaseName("Driver={sql Server};Server=localhost;Database=pyqt;Uid=pyqt;Pwd=xxx")
if self.DB.open():#打开数据库
self.__openTable()#打开数据表
else:
QMessageBox.warning(self, "错误", "打开数据失败")
#添加下拉框的数据
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
def __openTable(self):
self.tabModel=QSqlTableModel(self, self.DB)#数据模型
self.tabModel.setTable("employee")#设置数据表
self.tabModel.setEditStrategy(QSqlTableModel.EditStrategy.OnManualSubmit)
self.tabModel.setSort(self.tabModel.fieldIndex("EmoNo"), Qt.SortOrder.AscendingOrder)
if(self.tabModel.select()==False):#查询数据失败
QMessageBox.critical(self, "错误信息",
"打开数据库错误,错误信息\n"+self.tabModel.lastError().text())
return
self.__getFieldNames()#获取字段名与序号
#将表列名变为自定义的列名,可以显示表头
self.tabModel.setHeaderData(0, Qt.Orientation.Horizontal, "工号")
self.tabModel.setHeaderData(1, Qt.Orientation.Horizontal, "姓名")
self.tabModel.setHeaderData(2, Qt.Orientation.Horizontal, "性别")
self.tabModel.setHeaderData(3, Qt.Orientation.Horizontal, "出生日期")
self.tabModel.setHeaderData(4, Qt.Orientation.Horizontal, "省份")
self.tabModel.setHeaderData(5, Qt.Orientation.Horizontal, "部门")
self.tabModel.setHeaderData(0, Qt.Orientation.Horizontal, "工资")
self.tabModel.setHeaderData(0, Qt.Orientation.Horizontal, "备注")
self.ui.tableView.setModel(self.tabModel)#设置数据模型
self.ui.tableView.resizeColumnsToContents()#重新调整列宽
#QDataWidgetMapper用于建立界面组件与数据模型的字段之间的数据映射。这样,我们选
#择一个tableView的行,才能在右边看到相关数据的信息。
#选择模型的作用是当用户在tableView上操作时,获取当前选择的行、列信息,并在选择的
#单元格变化时发射currentChanged信号,在当前行变化时发射currentRowChanqed()信号。
self.mapper =QDataWidgetMapper()#创建界面组件与数据模型的字段之间的数据映射
self.mapper.setModel(self.tabModel)#设置数据模型
self.mapper.setSubmitPolicy(QDataWidgetMapper.SubmitPolicy.AutoSubmit)
self.mapper.addMapping(self.ui.dbspinEmpNo, 0)
self.mapper.addMapping(self.ui.dbEditName, 1)
self.mapper.addMapping(self.ui.dbComboDep, 2)
self.mapper.addMapping(self.ui.dbEditBirth, 3)
self.mapper.addMapping(self.ui.comboBoxProvince, 4)
self.mapper.addMapping(self.ui.dbComboDep, 5)
self.mapper.addMapping(self.ui.dbSpinSalary, 6)
self.mapper.addMapping(self.ui.dbEditMemo, 7)
self.mapper.toFirst()#移动首记录
self.selModel=QItemSelectionModel(self.tabModel)#选择模型
self.ui.tableView.setSelectionModel(self.selModel)#设置选择模型
self.selModel.currentChanged.connect(self.do_currentChanged)#当前项变化时触发
self.selModel.currentRowChanged.connect(self.do_currentRowChanged)#选择行变化时
strList=("男", "女")
self.__delegateSex=QmyComboBoxDelegate()
self.__delegateSex.setItems(strList, False)
self.ui.tableView.setItemDelegateForColumn(self.fldNum["Gender"], self.__delegateSex)
strList=("浙江","蒙古","陆西","吉林","广东","新疆")
self.__delegateProvince=QmyComboBoxDelegate()
self.__delegateProvince.setItems(strList,True)
self.ui.tableView.setItemDelegateForColumn(self.fldNum["Province"], self.__delegateProvince)
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.actRecAppend.setEnabled(True)
self.ui.actRecInsert.setEnabled(True)
self.ui.actRecDelete.setEnabled(True)
self.ui.actScan.setEnabled(True)
#一开始,排序和数据过滤这两个groupBox里面的组件是不能选的。因为数据表没有打开,
#没法数据排序和数据过滤。
#数据表一旦打开,这两个GroupBox应该enabled
self.ui.groupBoxSort.setEnabled(True)
self.ui.groupBoxFilter.setEnabled(True)
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.mapper.setCurrentIndex(current.row())#更新数据映射的行号
#点降序。注意不改变EmpNo,直接点降序那个radio Button是没反应的,因为不会触发#currentindexChanged信号。
#只有在排序字段先点了降序,然后修改了ComboBox的索引,比如改到Salary,然后选回
#EmpNo,再次点击降序,才会变化。可以理解一下。只有ComboBox里面的索引改变了,
#才会触发槽函数。
@pyqtSlot(int)##排序字段变化
def on_comboFields_currentIndexChanged(self, index):
if self.ui.radioBtnAscend.isChecked():
self.tabModel.setSort(index, Qt.SortOrder.AscendingOrder)
else:
self.tabModel.setSort(index, Qt.SortOrder.DescendingOrder)
self.tabModel.select()
@pyqtSlot()
def on_radioBtnAscend_clicked(self):#升序
self.tabModel.setSort(self.ui.comboFields.currentIndex(), Qt.SortOrder.AscendingOrder)
self.tabModel.select()
@pyqtSlot()
def on_radioBtnDescend_clicked(self):#降序
self.tabModel.setSort(self.ui.comboFields.currentIndex(), Qt.SortOrder.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("")
@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.SelectionFlag.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.SelectionFlag.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_actRecDelete_triggered(self):##删除记录
curIndex=self.selModel.currentIndex()
self.tabModel.removeRow(curIndex.row())
@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)
@pyqtSlot()##edit策略需要手工submit
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)
if(self.tabModel.submitAll()):
QMessageBox.information(self, "消息", "涨工资计算完毕")
if __name__=="__main__": #用于当前窗体测试
app=QApplication(sys.argv) #创建GUI应用程序
form=QmyMainWindow() #创建窗体
form.show()
sys.exit(app.exec())
我们发现在数据库中将性别修改为不合理的X,可以成功保存
而右侧框,因为有下拉框限定,不会修改为X
怎么解决这个问题呢?
要对tableview里面数据的修改进行限制。
tableview默认的单元格编辑组件是QlineEdit,对输入的数据无法限制。可以为某列设置自定义代理组件,比如OcommoBox。在上面的bua中,希望把性别的编辑组件改成QcommoBox,只能选择某些项(比如男和女),而不能随便输入。
新建文件myDelegates.py,处理浮点数和下拉框的限定数据数据
注:每个自定义代理组件必须继承4个函数:
(1)createEditor:创建用于编辑模型数据的widget组件
(2) setEditData:从数据模型获取数据,供widget组件进行编辑
(3)setModelData:将widget上的数据更新到数据模型
(4)updateEditorGeometry:给widet组件设置合适的大小
在这个类中,用itemList来获取可以选的所有选项,比如男、女。然后限定只能使用这些选项。
from PyQt6.QtWidgets import QStyledItemDelegate,QDoubleSpinBox,QComboBox
from PyQt6.QtCore import Qt
##======================基于QComboBox的代理组件====
class QmyComboBoxDelegate(QStyledItemDelegate):
def __init__(self,parent=None):
super().__init__(parent)
self.__itemList=[]
self.__isEditable=False
def setItems(self, itemList, isEditable=False):
self.__itemList=itemList
self.__isEditable=isEditable
#自定义代理组件必须继承一下4个函数
def createEditor(self, parent, option,index):
editor=QComboBox(parent)
editor.setFrame(False)
editor.setEditable(self.__isEditable)
editor.addItems(self.__itemList)
return editor
def setEditorData(self, editor, index):
model=index.model()
text=model.data(index, Qt.ItemDataRole.EditRole)
editor.setCurrentText(text)
def setModelData(self, editor,model, index):
text=editor.currentText()
model.setData(index, text, Qt.ItemDataRole.EditRole)
def updateEditorGeometry(self,editor,option,index):
editor.setGeometry(option.rect)
##====================基于QDoubleSpinbox的代理组件=======
#在这个类中,定义了_min,_max,_decimals,然后限定范围只能在_min和_max之间
class QmyFloatSpinDelegate(QStyledItemDelegate):
def __init__(self,minV=0, maxV=10000, digi=2, parent=None):
super().__init__(parent)
self. __min=minV
self.__max=maxV
self.__decimals=digi
def createEditor(self, parent, option,index):
editor=QDoubleSpinBox(parent)
editor.setFrame(False)
editor.setRange(self.__min, self.__max)
editor.setDecimals(self.__decimals)
return editor
def setEditorData(self,editor,index):
model=index.model()#关联的数据模型
text=model.data(index, Qt.EditRole)#单元格文字
editor.setValue(float(text))
def setModelData(self,editor,model,index):
value=editor.value()
model.setData(index,value, Qt.EditRole)
def updateEditorGeometry(self, editor, option, index):
editor.setGeometry(option.rect)
注:从myDelegates.py文件中,import QmyComboBoxDelegate 这个类。
在myMainWindow中添加如下代码,上面的myMainWindow已经添加了,是完整的代码!
strList=("男", "女")
self.__delegateSex=QmyComboBoxDelegate()
self.__delegateSex.setItems(strList, False)
self.ui.tableView.setItemDelegateForColumn(self.fldNum["Gender"], self.__delegateSex)
strList=("浙江","蒙古","陆西","吉林","广东","新疆")
self.__delegateProvince=QmyComboBoxDelegate()
self.__delegateProvince.setItems(strList,True)
self.ui.tableView.setItemDelegateForColumn(self.fldNum["Province"], self.__delegateProvince)
strList=("销售部", "技术部","生产部","行政部")
self.__delegateDepart=QmyComboBoxDelegate()
self.__delegateDepart.setItems(strList, True)
self.ui.tableView.setItemDelegateForColumn(self.fldNum["Department"], self.__delegateDepart)
注:在Ui_MainWindow中需加入import res_rc
同时将res_rc.py中的
from PySide6 import QtCore
改为:from PyQt6 import QtCore
这样才能在运行窗口中显示图标