使用Python pyqt打造任意Excel數(shù)據(jù)庫系統(tǒng)
一、引言
在數(shù)字化轉(zhuǎn)型浪潮中,超過76%的基層業(yè)務(wù)人員仍被困在"SQL恐懼癥"的泥潭里——他們精通業(yè)務(wù)邏輯卻受限于技術(shù)門檻,面對(duì)海量數(shù)據(jù)時(shí)只能反復(fù)請(qǐng)求IT部門協(xié)助。本項(xiàng)目通過Python+PyQt來構(gòu)建基于Excel風(fēng)格的查詢系統(tǒng),從而打破這種低效循環(huán):PyQt5提供直觀界面可視化組件,pandas庫實(shí)現(xiàn)"無SQL"的數(shù)據(jù)操作轉(zhuǎn)換,對(duì)用戶上傳的任意電子表格Excel文件均可完成復(fù)雜數(shù)據(jù)庫查詢。
二、GUI界面設(shè)計(jì)
使用PyQt5進(jìn)行界面的搭建,最終界面如下:

1.第一步:上傳任意Excel文件
直接點(diǎn)擊上傳即可。

這里可選擇上傳任意填寫好的Excel文件,但需要注意:
①Excel文件是純數(shù)據(jù)文件,不能包含表頭、標(biāo)題行、合并單元格等特殊格式。
②Excel中的數(shù)據(jù)量不受限,但要求第一列必須為查找的主鍵值(主鍵值是指數(shù)據(jù)庫表中主鍵字段(或字段組合)所存儲(chǔ)的具體數(shù)值或字符串,用于唯一標(biāo)識(shí)表中的每一行記錄,確保數(shù)據(jù)可唯一識(shí)別和訪問),其余列可填寫對(duì)應(yīng)數(shù)據(jù)。
以成績(jī)表作為演示示例:如若想查詢?nèi)K腥说母骺瞥煽?jī)信息,則第一列應(yīng)為該學(xué)生的學(xué)號(hào)(主鍵),其余各列可放置該學(xué)生的全部科目成績(jī)。

2.第二步:選擇查詢字段

在第一步上傳任意Excel成功后,會(huì)自動(dòng)根據(jù)上傳文件更新“請(qǐng)選擇主鍵字段”和“請(qǐng)選擇待查詢字段”內(nèi)的選項(xiàng),并且在“請(qǐng)選擇主鍵字段”這一選項(xiàng)下方出現(xiàn)輸入框,方便用戶輸入主鍵內(nèi)容。對(duì)于“請(qǐng)選擇主鍵字段”這一選項(xiàng),在第一步中已明確規(guī)定:Excel中第一列為主鍵字段,所以這里僅額外增加一個(gè)選項(xiàng)(以成績(jī)表為例,這里自動(dòng)增加“學(xué)號(hào)”字段);而對(duì)于“請(qǐng)選擇待查詢字段”這一選項(xiàng),會(huì)將Excel中除第一列以外的所有列名均作為新增選項(xiàng)(以成績(jī)表為例,這里自動(dòng)增加“語文”、“數(shù)學(xué)”、“英語”等其他所有字段),效果如下:




3.第三步:輸入主鍵值并查詢
選擇好主鍵字段及待查詢字段,并輸入待查詢主鍵值后,點(diǎn)擊“開始查詢”即可。

最終查詢效果如下:

可以看到,結(jié)果被分為兩個(gè)區(qū)域:左邊表示查詢結(jié)果(即學(xué)生姓名),右邊表示與查詢結(jié)果相關(guān)的其他所有Excel數(shù)據(jù)(即除姓名和學(xué)號(hào)之外,該生的所有成績(jī)信息)。
4.異常提示
若未進(jìn)行相應(yīng)的操作,跳過某個(gè)步驟或直接點(diǎn)擊“開始查詢”,則會(huì)出現(xiàn)以下的異常提示。




5.界面設(shè)計(jì)jiemian.py
最后附上通過pyuic5產(chǎn)生的GUI界面代碼jiemian.py:
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'jiemian.ui'
#
# Created by: PyQt5 UI code generator 5.15.11
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_Form(object):
def setupUi(self, Form):
Form.setObjectName("Form")
Form.setEnabled(True)
Form.resize(600, 500)
Form.setMinimumSize(QtCore.QSize(600, 500))
Form.setMaximumSize(QtCore.QSize(600, 500))
icon = QtGui.QIcon()
icon.addPixmap(QtGui.QPixmap(":/image1.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
Form.setWindowIcon(icon)
self.label_5 = QtWidgets.QLabel(Form)
self.label_5.setGeometry(QtCore.QRect(390, 50, 141, 31))
font = QtGui.QFont()
font.setFamily("Adobe Arabic")
font.setPointSize(16)
self.label_5.setFont(font)
self.label_5.setObjectName("label_5")
self.label_8 = QtWidgets.QLabel(Form)
self.label_8.setGeometry(QtCore.QRect(220, 20, 231, 31))
font = QtGui.QFont()
font.setFamily("Adobe Arabic")
font.setPointSize(18)
font.setBold(False)
font.setWeight(50)
self.label_8.setFont(font)
self.label_8.setObjectName("label_8")
self.label_10 = QtWidgets.QLabel(Form)
self.label_10.setGeometry(QtCore.QRect(470, 40, 71, 51))
self.label_10.setText("")
self.label_10.setPixmap(QtGui.QPixmap(":/image1.png"))
self.label_10.setObjectName("label_10")
self.label = QtWidgets.QLabel(Form)
self.label.setGeometry(QtCore.QRect(80, 90, 161, 21))
font = QtGui.QFont()
font.setFamily("Adobe Arabic")
font.setPointSize(12)
self.label.setFont(font)
self.label.setObjectName("label")
self.pushButton = QtWidgets.QPushButton(Form)
self.pushButton.setGeometry(QtCore.QRect(120, 120, 161, 23))
self.pushButton.setObjectName("pushButton")
self.label_2 = QtWidgets.QLabel(Form)
self.label_2.setGeometry(QtCore.QRect(80, 170, 161, 21))
font = QtGui.QFont()
font.setFamily("Adobe Arabic")
font.setPointSize(12)
self.label_2.setFont(font)
self.label_2.setObjectName("label_2")
self.comboBox = QtWidgets.QComboBox(Form)
self.comboBox.setGeometry(QtCore.QRect(120, 200, 161, 22))
self.comboBox.setObjectName("comboBox")
self.comboBox.addItem("")
self.comboBox_2 = QtWidgets.QComboBox(Form)
self.comboBox_2.setGeometry(QtCore.QRect(330, 200, 161, 22))
self.comboBox_2.setObjectName("comboBox_2")
self.comboBox_2.addItem("")
self.label_3 = QtWidgets.QLabel(Form)
self.label_3.setGeometry(QtCore.QRect(80, 300, 221, 151))
font = QtGui.QFont()
font.setFamily("Adobe Arabic")
font.setPointSize(28)
self.label_3.setFont(font)
self.label_3.setFrameShape(QtWidgets.QFrame.StyledPanel)
self.label_3.setText("")
self.label_3.setAlignment(QtCore.Qt.AlignCenter)
self.label_3.setObjectName("label_3")
self.pushButton_3 = QtWidgets.QPushButton(Form)
self.pushButton_3.setGeometry(QtCore.QRect(80, 470, 471, 23))
self.pushButton_3.setObjectName("pushButton_3")
self.lineEdit = QtWidgets.QLineEdit(Form)
self.lineEdit.setGeometry(QtCore.QRect(120, 250, 161, 20))
self.lineEdit.setObjectName("lineEdit")
self.textEdit = QtWidgets.QTextEdit(Form)
self.textEdit.setGeometry(QtCore.QRect(320, 300, 231, 151))
self.textEdit.setObjectName("textEdit")
self.label_4 = QtWidgets.QLabel(Form)
self.label_4.setGeometry(QtCore.QRect(80, 280, 161, 16))
self.label_4.setObjectName("label_4")
self.label_6 = QtWidgets.QLabel(Form)
self.label_6.setGeometry(QtCore.QRect(320, 280, 161, 16))
self.label_6.setObjectName("label_6")
self.label_7 = QtWidgets.QLabel(Form)
self.label_7.setGeometry(QtCore.QRect(120, 230, 161, 16))
self.label_7.setObjectName("label_7")
self.retranslateUi(Form)
QtCore.QMetaObject.connectSlotsByName(Form)
def retranslateUi(self, Form):
_translate = QtCore.QCoreApplication.translate
Form.setWindowTitle(_translate("Form", "Kebiao_query"))
self.label_5.setText(_translate("Form", "Designed By"))
self.label_8.setText(_translate("Form", "數(shù)據(jù)庫查詢系統(tǒng)"))
self.label.setText(_translate("Form", "一、上傳數(shù)據(jù)文件"))
self.pushButton.setText(_translate("Form", "點(diǎn)擊上傳"))
self.label_2.setText(_translate("Form", "二、選擇查詢字段"))
self.comboBox.setItemText(0, _translate("Form", "請(qǐng)選擇主鍵字段"))
self.comboBox_2.setItemText(0, _translate("Form", "請(qǐng)選擇待查詢字段"))
self.pushButton_3.setText(_translate("Form", "開始查詢"))
self.label_4.setText(_translate("Form", "查詢結(jié)果如下:"))
self.label_6.setText(_translate("Form", "相關(guān)查詢結(jié)果如下:"))
self.label_7.setText(_translate("Form", "請(qǐng)輸入主鍵值:"))
import ziyuan_rc
三、主要程序詳解
1.導(dǎo)入所需模塊
import sys from jiemian import * from PyQt5.QtWidgets import QApplication, QWidget import pandas as pd # 保持窗口大小和qtdesigner中的一致 from PyQt5 import QtCore QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling)
不懂為啥需要導(dǎo)入Qtcore的,請(qǐng)看一鍵曝光:Python+PyQt實(shí)現(xiàn)的文件目錄。
2.初始化設(shè)置
def __init__(self):
super(QWidget, self).__init__()
self.setupUi(self)
self.pushButton.clicked.connect(self.shangchuan_wenjian)
self.pushButton_3.clicked.connect(self.kaishi)
self.lineEdit.setVisible(False)
self.label_4.setVisible(False)
self.label_6.setVisible(False)
self.label_7.setVisible(False)
self.shangchuan_flag = False主要將pushbutton綁定于對(duì)應(yīng)的信號(hào)函數(shù),并對(duì)一些lineedit和label設(shè)置visible效果,目的是只有觸發(fā)某些動(dòng)作或函數(shù)時(shí),才將其顯示狀態(tài)更改為True,否則一直不顯示(即False狀態(tài));shangchuan_flag作為是否上傳任意Excel表的標(biāo)志位。
3.上傳文件并更新選項(xiàng)
def shangchuan_wenjian(self):
self.wenjian_df = shangchuan()
if not self.wenjian_df.empty:
self.shangchuan_flag = True
self.lineEdit.setVisible(True)
self.label_7.setVisible(True)
self.columns = self.wenjian_df.columns.tolist()
self.comboBox.clear()
self.comboBox.addItem("請(qǐng)選擇主鍵字段")
self.comboBox.addItem(self.columns[0])
self.comboBox_2.clear()
self.comboBox_2.addItem("請(qǐng)選擇待查詢字段")
self.comboBox_2.addItems(self.columns[1:])調(diào)用自定義函數(shù)shangchuan來實(shí)現(xiàn)彈窗效果;當(dāng)文件內(nèi)容不為空時(shí),設(shè)置上傳標(biāo)志位shangchuan_flag為True,并顯示lineedit(主鍵值輸入框)和label_7(“請(qǐng)輸入主鍵值”);讀取所有文件列名并存儲(chǔ)在列表columns中,將列表columns中的第一個(gè)值(即第一列的列名)添加至combobox中,其余值添加至combobox_2中。但需要注意:每次添加item(s)前,需要將其原有選項(xiàng)清空clear,否則容易造成選項(xiàng)堆疊重復(fù)。
4.查詢信息并顯示
def kaishi(self):
if self.shangchuan_flag==True:
if self.comboBox.currentIndex()!=0:
if self.comboBox_2.currentIndex()!=0:
lieziduan = self.comboBox_2.currentText()
liesuoyin = self.wenjian_df.columns.get_loc(lieziduan)
# 根據(jù)行字段確定行索引
hangsuoyin = -1
for i in range(0, len(self.wenjian_df)):
if str(self.wenjian_df.iloc[i,0]) == self.lineEdit.text():
hangsuoyin = i
break
chaxunzhi = self.wenjian_df.iloc[hangsuoyin, liesuoyin]
# 匹配剩余未查詢值
ewai_lst = []
for column_name in self.columns:
new_column_index = self.wenjian_df.columns.get_loc(column_name)
if (new_column_index != liesuoyin) and (new_column_index != 0):
ewaizhi = self.wenjian_df.iloc[hangsuoyin, new_column_index]
ewai_lst.append(column_name+":"+str(ewaizhi))
if pd.isna(chaxunzhi) or hangsuoyin==-1:
self.label_4.setVisible(True)
self.label_6.setVisible(True)
self.label_3.setText("未找到")
self.textEdit.setText("未找到")
else:
self.label_4.setVisible(True)
self.label_6.setVisible(True)
self.label_3.setText(str(chaxunzhi))
self.textEdit.setText("\n".join(ewai_lst))
else:
QtWidgets.QMessageBox.critical(self, "提示", "請(qǐng)選擇主鍵字段!")
else:
QtWidgets.QMessageBox.critical(self, "提示", "請(qǐng)選擇待查詢字段!")
else:
QtWidgets.QMessageBox.critical(self, "提示", "請(qǐng)檢查操作步驟或上傳文件!")以shangchaun_flag作為標(biāo)志位,檢查是否上傳模板文件;依次判斷是否選擇每個(gè)combobox里的對(duì)應(yīng)選項(xiàng);若均滿足,則根據(jù)combobox_2的內(nèi)容定位列字段名稱,再根據(jù)列字段名稱反推列索引;根據(jù)lineedit內(nèi)容通過循環(huán)查詢對(duì)應(yīng)的值,找到后即可退出循環(huán),避免運(yùn)算復(fù)雜度,最終得到行索引;最后通過得到的行索引和列索引確定查詢值,并通過label_3顯示;剩余未查詢值,思路方法也是同理,最終顯示在textedit中。若遇到查詢不到的情況時(shí),顯示的內(nèi)容均設(shè)置為“未找到”。當(dāng)然,哪個(gè)combobox有問題,則顯示不同的對(duì)應(yīng)提示。
5.自定義函數(shù)
def shangchuan():
filepath, _ = QtWidgets.QFileDialog.getOpenFileName(None, "請(qǐng)選擇文件", "", "XLSX工作表 (*.xlsx)") # 獲取文件路徑
if filepath:
# 獲取原始df
yuanshi_df = pd.read_excel(filepath)
if not yuanshi_df.empty:
QtWidgets.QMessageBox.information(None, "成功", "上傳成功!")
return yuanshi_df
else:
QtWidgets.QMessageBox.critical(None, "提示", "請(qǐng)檢查操作步驟或上傳文件!")
else:
QtWidgets.QMessageBox.critical(None, "提示", "請(qǐng)選擇XLSX工作表類型!")
return pd.DataFrame()一旦需要上傳文件時(shí),直接調(diào)用此函數(shù)即可。
四、總程序代碼
import sys
from jiemian import *
from PyQt5.QtWidgets import QApplication, QWidget
import pandas as pd
# 保持窗口大小和qtdesigner中的一致
from PyQt5 import QtCore
QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling)
class mainwindow(QWidget, Ui_Form):
def __init__(self):
super(QWidget, self).__init__()
self.setupUi(self)
self.pushButton.clicked.connect(self.shangchuan_wenjian)
self.pushButton_3.clicked.connect(self.kaishi)
self.lineEdit.setVisible(False)
self.label_4.setVisible(False)
self.label_6.setVisible(False)
self.label_7.setVisible(False)
self.shangchuan_flag = False
def shangchuan_wenjian(self):
self.wenjian_df = shangchuan()
if not self.wenjian_df.empty:
self.shangchuan_flag = True
self.lineEdit.setVisible(True)
self.label_7.setVisible(True)
self.columns = self.wenjian_df.columns.tolist()
self.comboBox.clear()
self.comboBox.addItem("請(qǐng)選擇主鍵字段")
self.comboBox.addItem(self.columns[0])
self.comboBox_2.clear()
self.comboBox_2.addItem("請(qǐng)選擇待查詢字段")
self.comboBox_2.addItems(self.columns[1:])
def kaishi(self):
if self.shangchuan_flag==True:
if self.comboBox.currentIndex()!=0:
if self.comboBox_2.currentIndex()!=0:
lieziduan = self.comboBox_2.currentText()
liesuoyin = self.wenjian_df.columns.get_loc(lieziduan)
# 根據(jù)行字段確定行索引
hangsuoyin = -1
for i in range(0, len(self.wenjian_df)):
if str(self.wenjian_df.iloc[i,0]) == self.lineEdit.text():
hangsuoyin = i
break
chaxunzhi = self.wenjian_df.iloc[hangsuoyin, liesuoyin]
# 匹配剩余未查詢值
ewai_lst = []
for column_name in self.columns:
new_column_index = self.wenjian_df.columns.get_loc(column_name)
if (new_column_index != liesuoyin) and (new_column_index != 0):
ewaizhi = self.wenjian_df.iloc[hangsuoyin, new_column_index]
ewai_lst.append(column_name+":"+str(ewaizhi))
if pd.isna(chaxunzhi) or hangsuoyin==-1:
self.label_4.setVisible(True)
self.label_6.setVisible(True)
self.label_3.setText("未找到")
self.textEdit.setText("未找到")
else:
self.label_4.setVisible(True)
self.label_6.setVisible(True)
self.label_3.setText(str(chaxunzhi))
self.textEdit.setText("\n".join(ewai_lst))
else:
QtWidgets.QMessageBox.critical(self, "提示", "請(qǐng)選擇主鍵字段!")
else:
QtWidgets.QMessageBox.critical(self, "提示", "請(qǐng)選擇待查詢字段!")
else:
QtWidgets.QMessageBox.critical(self, "提示", "請(qǐng)檢查操作步驟或上傳文件!")
def shangchuan():
filepath, _ = QtWidgets.QFileDialog.getOpenFileName(None, "請(qǐng)選擇文件", "", "XLSX工作表 (*.xlsx)") # 獲取文件路徑
if filepath:
# 獲取原始df
yuanshi_df = pd.read_excel(filepath)
if not yuanshi_df.empty:
QtWidgets.QMessageBox.information(None, "成功", "上傳成功!")
return yuanshi_df
else:
QtWidgets.QMessageBox.critical(None, "提示", "請(qǐng)檢查操作步驟或上傳文件!")
else:
QtWidgets.QMessageBox.critical(None, "提示", "請(qǐng)選擇XLSX工作表類型!")
return pd.DataFrame()
if __name__ == '__main__':
app = QApplication(sys.argv)
w = mainwindow()
w.show()
sys.exit(app.exec_())
僅以成績(jī)表作為示例,上傳其他任意Excel均可!
以上就是使用Python pyqt打造任意Excel數(shù)據(jù)庫系統(tǒng)的詳細(xì)內(nèi)容,更多關(guān)于Python Excel數(shù)據(jù)查詢的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
python opencv把一張圖片嵌入(疊加)到另一張圖片上的實(shí)現(xiàn)代碼
這篇文章主要介紹了python opencv把一張圖片嵌入(疊加)到另一張圖片上,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-06-06
Python列表list數(shù)組array用法實(shí)例解析
這篇文章主要介紹了Python列表list數(shù)組array用法,實(shí)例解析了關(guān)于數(shù)組的各種常見操作,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2014-10-10
Python實(shí)現(xiàn)Excel和CSV之間的相互轉(zhuǎn)換
通過使用Python編程語言,編寫腳本來自動(dòng)化Excel和CSV之間的轉(zhuǎn)換過程,可以批量處理大量文件,定期更新數(shù)據(jù),并集成轉(zhuǎn)換過程到自動(dòng)化工作流程中,本文將介紹如何使用Python 實(shí)現(xiàn)Excel和CSV之間的相互轉(zhuǎn)換,需要的朋友可以參考下2024-03-03
使用python實(shí)現(xiàn)一個(gè)瀏覽器自動(dòng)化的腳本
最近工作中有這樣一個(gè)需求:客戶反饋在瀏覽器操作過程中,重復(fù)流程操作太頻繁,能不能讓瀏覽器自動(dòng)操作完成?所以本文給大家介紹了如何使用python實(shí)現(xiàn)一個(gè)瀏覽器自動(dòng)化的腳本,感興趣的朋友可以參考下2024-04-04
Pycharm無法正常安裝第三方庫的幾條應(yīng)對(duì)方法匯總
在使用pycharm學(xué)習(xí)python的時(shí)候,經(jīng)常需要第三方庫,沒有第三方庫程序就會(huì)報(bào)錯(cuò),下面這篇文章主要給大家介紹了關(guān)于Pycharm無法正常安裝第三方庫的幾條應(yīng)對(duì)方法,需要的朋友可以參考下2023-04-04
Python使用unicodedata實(shí)現(xiàn)字符串標(biāo)準(zhǔn)化
這篇文章主要來和大家聊一聊 Python 的一個(gè)內(nèi)置模塊:unicodedata,它是專門用來處理 unicode 字符串的,下面就一起來看看它的用法吧2023-06-06

