PqQt实现对数据库的添加,删除,修改(完整过程演示)

在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

 这样才能在运行窗口中显示图标

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值