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

news2024/9/28 19:25:54

在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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/730678.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于matlab显示城市街区场景中配备立体摄像机的无人机开发视觉SLAM算法(附源码)

一、前言 视觉SLAM是计算摄像机相对于周围环境的位置和方向,同时映射环境的过程。开发可视化 SLAM 算法并评估其在不同条件下的性能是一项具有挑战性的任务。最大的挑战之一是生成相机传感器的地面实况,尤其是在户外环境中。使用仿真可以在各种场景和相…

HarmonyOS学习路之开发篇—数据管理(分布式文件服务)

分布式文件服务概述 分布式文件服务能够为用户设备中的应用程序提供多设备之间的文件共享能力,支持相同帐号下同一应用文件的跨设备访问,应用程序可以不感知文件所在的存储设备,能够在多个设备之间无缝获取文件。 基本概念 分布式文件 分布…

WebGIS 信息系统-Element项目实战

WebGIS 信息系统-Element项目实战 Element的安装OpenLayers的安装采用直接引用的方式配置开发环境下载Vue文件下载Element文件下载OpenLayers文件 Element的安装 在项目的根目录中,首先按下 Shift鼠标右键,在弹出的右键菜单中选择“在此处打开命令行窗口…

OpenCV 的 remap 函数改变图像中像素的位置

#include "opencv2/highgui/highgui.hpp" #include "opencv2/imgproc/imgproc.hpp" #

Spring Boot 中的 @Scheduled 注解是什么,如何使用

Spring Boot 中的 Scheduled 注解是什么,如何使用 引言 Spring Boot 是一个非常流行的 Java Web 开发框架,它提供了很多方便的功能,其中就包括了 Scheduled 注解。本文将会介绍 Scheduled 注解的基本用法,并附上代码示例&#x…

2023 最新「阿里」Java 高级工程师面试高频题

7月份快要过去了,也意味着金九银十快到了,不知道大家有没有发现今年的面试比之前的都难;去年因为疫情的原因压抑住程序员跳槽的想法,再加上现在 IT 行业内卷越来越严重,这两点原因导致今年的招聘市场非常难受&#xff…

【模式识别目标检测】——基于机器视觉的无人机避障RP-YOLOv3实例

目录 引入 一、YOLOv3模型 1、实时目标检测YOLOv3简介 2、改进的实时目标检测模型 二、数据集建立&结果分析 1、数据集建立 2、模型结果分析 三、无人机避障实现 参考文献: 引入 目前对于障碍物的检测整体分为:激光、红外线、超声波、雷达、…

使用FAST方法检测特征点,然后计算这些特征点的ORB描述子,并使用暴力匹配方法找到匹配的特征点

这段代码主要做了以下几件事情: 读取两幅图像使用FAST方法找出图像中的特征点手写ORB方法计算特征点的描述子使用汉明距离(Hamming distance)进行描述子的匹配显示匹配的结果下面我们会逐行解释每一句代码: 包含头文件:这一部分包含了所有需要的库。 ComputeORB 函数:该函…

【验证码逆向专栏】某度滑块、点选、旋转验证码 v1、v2 逆向分析

声明 本文章中所有内容仅供学习交流使用,不用于其他任何目的,不提供完整代码,抓包内容、敏感网址、数据接口等均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关! 本文章未…

SSL密钥算法检测工具-sslciphercheck-SSL/TLS Suffers ‘Bar Mitzvah Attack’漏洞

SSL密钥算法检测工具-sslciphercheck-SSL/TLS Suffers ‘Bar Mitzvah Attack’漏洞 文章目录 sslciphercheck漏洞:SSL/TLS Suffers ‘Bar Mitzvah Attack’漏洞漏洞描述漏洞检测修复建议 sslciphercheck 下载:https://github.com/woanware/woanware.git…

Ubuntu vim 以十六进制的形式显示文件内容

Vim的介绍 Vim(Vi IMproved)是一款开源的文本编辑器,是 Unix 系统中经典的 vi 编辑器的改进版本。它具有强大的功能和高度可定制性,被广泛应用于程序开发、系统管理和文本编辑等领域。 以下是 Vim 的一些主要特点和功能&#xf…

CRC Principle and Implementation Method(Java C)

CRC原理和程序实现方法1_哔哩哔哩_bilibili 其实原理很简单 但是我想了两个小时。。 收获的是原来一些复杂的运算都可以通过位运算来实现。 实现思路 public class CRC16Calculator {public static String CRC16(byte[] bytes) {int CRC 0x0000ffff;int POLYNOMIAL 0x0000a…

【复习3-5天的内容】【我们一起60天准备考研算法面试(大全)-第七天 7/60】

专注 效率 记忆 预习 笔记 复习 做题 欢迎观看我的博客,如有问题交流,欢迎评论区留言,一定尽快回复!(大家可以去看我的专栏,是所有文章的目录)   文章字体风格: 红色文字表示&#…

Ubuntu Linux系统下创建Git项目并push到远程Github

首先在本地创建git仓库 jasminelhl:~/prj$ mkdir Github-test jasminelhl:~/prj$ cd Github-test jasminelhl:~/prj/Github-test$ git init 提示:使用 master 作为初始分支的名称。这个默认分支名称可能会更改。要在新仓库中 提示:配置使用初始分支名&a…

RabbitMQ的可视化管理页面简介

模块 描述 Overview 概览 Connections 查看连接情况 Channels 信道(通过)情况 Exchanges 交换机(路由)情况,默认四类七个 Queues 消息队列情况 Admin …

高并发架构实战:从需求分析到系统设计

很多软件工程师的职业规划是成为架构师,但是要成为架构师很多时候要求先有架构设计经验,而不做架构师又怎么会有架构设计经验呢?那么要如何获得架构设计经验呢? 一方面可以通过工作来学习,观察所在团队的架构师是如何…

网络安全工程师工作内容具体是什么?

在知乎看到一个帖子:网络安全工程师或者网络安全研究员的工作内容具体是什么?或者说他们的一天具体是怎么度过的,是否和程序员一样编码做项目? Neeao的回复: 只在甲方互联网公司呆过,简单说下甲方的情况。…

“千模千测”——针对大语言模型认知能力的高效测试方法

©PaperWeekly 原创 作者 | 庄严、宁雨亭 单位 | 中国科学技术大学BASE课题组 论文标题: Efficiently Measuring the Cognitive Ability of LLMs: An Adaptive Testing Perspective 作者: Yan Zhuang, Qi Liu, Yuting Ning, Weizhe Huang, Rui Lv, …

Docker 安装Nginx

查看Nginx镜像并拉取镜像: [rootlocalhost nginx]# docker search nginx NAME DESCRIPTION STARS OFFICIAL AUTOMATED nginx …

使用Mybatis添加记录错误

使用Mybatis添加记录错误:Error updating database. Cause: com.mysql.cj.jdbc.exceptions.MysqlDataTruncation: Data truncation: Out of range value for column ‘id’ at row 1 分析:添加记录的id值超出范围,查看JavaBean中的ID&#…