分享

超越传统,用PyQt开发的Excel文档查询软件,助你解放生产力!

 山峰云绕 2023-12-23 发布于贵州

https://m.toutiao.com/is/i8HoUedJ/?= 


你是否曾对处理繁琐的Excel文档感到手足无措?你是否渴望找到一种简单而高效的方法来查询和分析这些海量数据?别担心,我们将向你介绍一个超越传统的解决方案——基于PyQt开发的Excel文档查询软件!

在这个快速发展的世界里,Excel仍然是众多企业和个人使用最广泛的电子表格工具。然而,面对庞大的数据表格,常规的筛选和排序功能往往显得力不从心。现在,借助PyQt框架,你将能够轻松开发一款功能强大、操作简便的Excel文档查询软件,用来管理集群指定目录下的所有excel表格,从而解放你的生产力。

接下来介绍如何编写这样的工具,或许程序功能不是很高端,但是开启自定义编程工具的开始,正所谓千里之行始于足下,勇敢迈出的每一步,都是踏上成功之路的关键。每一次迈进的脚步,都让我们离目标更近一步。

首先,构思软件需要有什么样的界面和功能,下面画一个很简洁的图来展示整个工具的作用和界面

环境准备:

python版本:3.10.2

编译器:pycharm(任意一个可编辑代码的工具就成,pycharm代码自动补全的功能比较好)

pyqt5版本: 5.15.10

PyQt5-tools版本:PyQt5-tools

openpyxl版本: 3.1.2

pandas版本:2.1.3

安装模块:

pip install pandas pip install openpyxlpip install pyqt5pip install PyQt5-tools

好的,执行完上面的部分硬件要求以及具备。看起来还是蛮简单的哈。如果想实现一个最简单的pyqt的界面实现可以用以下代码实现:一个空白的界面,没有任何功能

import sysfrom PyQt5.QtWidgets import QApplication, QWidgetdef main():    app = QApplication(sys.argv)    # 创建一个窗口    window = QWidget()    window.setWindowTitle('简单界面示例')    window.setGeometry(100, 100, 300, 200)  # 设置窗口位置和大小        # 显示窗口    window.show()    # 事件循环,确保窗口一直显示    sys.exit(app.exec_())if __name__ == '__main__':    main()

由于前端UI涉及到各个控件的位置的摆放,可以使用PyQt5-tools 工具进行设计,通过该工具可将需要的控件一个一个拖进界面中,之后生成py代码。省去了大部分人工写代码,毕竟专业的事还是用专业的工具的好,生成的图片可在PyQt5-tools工具中直接看到(如何使用PyQt-tools工具可通过:Python Pyqt5快速上手教程_pyqt5教程-CSDN博客 查看)

下面是 直接在PyQt5-tools中展示的软件界面:

将ui文件保存后执行经过简单处理后得到以下py代码,之后代码都在这个基础上完成。

from PyQt5.QtWidgets import QApplication, QWidget, QGroupBox, QLineEdit, QComboBox,QTableWidget,QSizePolicyfrom PyQt5.QtWidgets import QFrame,QPushButton,QMenuBar,QStatusBar,QMainWindow,QVBoxLayout,QHBoxLayout,QScrollAreafrom PyQt5 import QtCoreclass Ui_MainWindow(QMainWindow): def __init__(self): super().__init__() self.setupUi() def setupUi(self): self.resize(1000, 680) # 设置主窗口初始尺寸 # 创建一个容器用于放置其他组件,放置在主窗口的中心位置 self.centralwidget = QWidget(self) self.setCentralWidget(self.centralwidget) # 创建一个分组框组件 self.groupBox = QGroupBox(self.centralwidget) layout1 = QVBoxLayout() hlayout1 = QHBoxLayout() layout1.addLayout(hlayout1) self.groupBox.setLayout(layout1) self.groupBox.setStyleSheet('QGroupBox { margin: 1px; border: 1px solid lightgrey; }') #指定了 QGroupBox 的内边距为 3 像素 self.comboBox = QComboBox(self.groupBox) # 创建一个下拉框组件 # 设置调整策略为 AdjustToContents,自使用内部名字长度 self.comboBox.setSizeAdjustPolicy(QComboBox.AdjustToContents) hlayout1.addWidget(self.comboBox) self.comboBox.setFixedHeight(23)#设置高度 self.comboBox.setFixedWidth(260)#设置宽度 self.lineEdit = QLineEdit(self.groupBox) # 创建输入选择框 self.lineEdit.setFixedHeight(23) hlayout1.addWidget(self.lineEdit) self.pushButton = QPushButton(self.groupBox) # 创建按钮1 self.pushButton.setFixedHeight(23) self.pushButton.setFixedWidth(75) hlayout1.addWidget(self.pushButton) self.pushButton_2 = QPushButton(self.groupBox) # 创建按钮2 self.pushButton_2.setFixedHeight(23) self.pushButton_2.setFixedWidth(75) hlayout1.addWidget(self.pushButton_2) # 创建一个分组框组件,并设置位置和尺寸 self.groupBox_2 = QGroupBox(self.centralwidget) self.line = QFrame(self.centralwidget)#加了条直线,主要是为了美观 self.line.setGeometry(10, 160, 571, 20) self.line.setFrameShape(QFrame.HLine) self.line.setFrameShadow(QFrame.Sunken) self.groupBox_3 = QGroupBox(self.centralwidget)#创建第三个分组框组件 layout2 = QVBoxLayout() hlayout2 = QHBoxLayout() layout2.addLayout(hlayout2) self.groupBox_3.setLayout(layout2) self.tableWidget = QTableWidget(self.groupBox_3) # 创建编辑表格数据的控件 self.tableWidget.setColumnCount(0) self.tableWidget.setRowCount(0) hlayout2.addWidget(self.tableWidget) # 创建一个 QMenuBar 对象 self.menubar,并将其设置为当前窗口的菜单栏 self.menubar = QMenuBar(self) # 设置 self.menubar 的位置和尺寸 self.menubar.setGeometry(0, 0, 604, 21) # 将 self.menubar 设置为当前窗口的菜单栏 self.setMenuBar(self.menubar) # 创建一个 QStatusBar 对象 self.statusbar,用于显示当前窗口的状态栏 self.statusbar = QStatusBar(self) # 将 self.statusbar 设置为当前窗口的状态栏 self.setStatusBar(self.statusbar) self.retranslateUi(self)#是一个自动生成的方法,用于更新界面上的文本内容 QtCore.QMetaObject.connectSlotsByName(self)#是一个用于将信号和槽函数连接起来的方法。在使用 Qt Designer 工具创建界面时,可以在界面中指定某个控件的槽函数。 def resizeEvent(self, event): #设置各个控件随窗口大小而改变 window_size = self.size() self.groupBox.setGeometry(10, 0, window_size.width() - 40, 41) self.groupBox_2.setGeometry(10, 42, window_size.width() - 40, 121) self.line.setGeometry(10, 160, window_size.width() - 40, 20) self.groupBox_3.setGeometry(10, 175, window_size.width() - 40, window_size.height() - 230) def retranslateUi(self, MainWindow): _translate = QtCore.QCoreApplication.translate MainWindow.setWindowTitle(_translate('MainWindow', '表格管理工具')) self.pushButton.setText(_translate('MainWindow', '搜索')) self.pushButton_2.setText(_translate('MainWindow', '导出'))if __name__ == '__main__': import sys# 引入 sys 模块,用于退出应用程序 app = QApplication([])# 创建一个 QApplication 实例 widget = Ui_MainWindow()# 创建一个 Ui_MainWindow 对象实例 widget.show()# 显示窗口 sys.exit(app.exec_())# 运行应用程序,直到退出

程序界面如下,可大致分为4个功能部分:

需要实现的功能如下:

  1. 点击输入框,指定目录位置
  2. 根据指定目录将目录下的所有目录名字展示在1的位置
  3. 将第一个表格的列名放在5的位置,格式如下
  4. 将第一个表格的内容展示在6的位置
  5. 实现搜索
  6. 实现导出

1.选择输入框:第一个功能实现鼠标点击选择输入框,代码如下:

#1.为图中的输入框绑定事件,鼠标点击后触发,在初始化self.lineEdit后面写触发事件 self.lineEdit.mousePressEvent = self.on_mousePressEventdef on_mousePressEvent(self, event):#鼠标触发事件    if event.button() == QtCore.Qt.LeftButton:        directory = QFileDialog.getExistingDirectory(self, '选择目录')         if directory:            self.lineEdit.setText(directory))#让输入框最后得到选择的目录            self.setcombox(directory) #将目录传入到setcombox 方法实现下面收集excel表格的功能

2.下拉列表:当选择好目录后,需要将该目录下的所有excel表格名称都放到1 位置的下拉列表中,所以可在上面的代码中写入调用方法,通过下面代码可实现将指定目录下的所有目录名添加到下拉列表中:

import re #导入re模块,置顶写def setcombox(self,directory): self.comboBox.blockSignals(True)#阻止comboBox触发信号 self.comboBox.clear()#在为combox添加值之前清除combox内的所有内容 self.file_dir = {} pattern = re.compile(r'^[^~].*\.xlsx?$') for item in os.listdir(directory): if pattern.search(item): self.comboBox.addItem(item)#将过滤好的文件名字加入到comboxBox file_data = pd.read_excel(directory +'/'+item) #读取目录下的文件 self.file_dir[item] = file_data#将文件以文件名:文件内容的方式放入到file_dir自动中 self.comboBox.blockSignals(False)#开启comboBox触发信号

界面效果

3.列名和输入框,经展示的列表的列名和输入框显示出来,一般将列表的第一列设为列名

def setcombox(self,directory):    self.show_data()#当选择好目录后就展示    #当然还需要连接comboBox信号和槽函数,为下拉列表设置事件方法    self.comboBox.currentIndexChanged.connect(self.show_data)

已经在上面方法中编写了连接函数,下面是连接函数的内容,这里面有个自定义属性的方法,obj.setProperty('属性名', 值),可以用这种方式为控件添加属性,用的时候可以obj.property('my_attribute') 来获取自定义属性的值。

def show_data(self): #comboxBox的currentText()方法可以得到下拉列表当前显示的内容 #根据当前显示的内容读取之前定义的字典self.file_dir中对应的表格内容 data = self.file_dir[self.comboBox.currentText()] #得到对应表格内容 self.groupBox_2.setProperty('my_attribute', data) #将展示数据传入自定义属性 titles = data.columns #数据类型为dataframe,可用这种方式获取第一行,一般标题为第一行 # 获取 groupBox 中的布局 the_vbox = self.groupBox_2.layout() if the_vbox: for widget in self.groupBox_2.findChildren(QWidget): # 清除groupBox内所有子元素 widget.deleteLater() else: the_vbox = QVBoxLayout() self.groupBox_2.setLayout(the_vbox) the_grid_layout = QGridLayout() # 使用QGridLayout代替QHBoxLayout the_vbox.addLayout(the_grid_layout) num = 0 grid_row = 0 grid_column = 0 for item in titles: label = QLabel('%s:' % item, self.groupBox) lineedit = QLineEdit(self.groupBox) lineedit.setProperty('my_attribute', item) # 为了区分和标记表格,添加一个自定义属性my_attribute,并将标题赋值给它 lineedit.setFixedHeight(30) the_grid_layout.addWidget(label,grid_row,grid_column) the_grid_layout.addWidget(lineedit,grid_row,grid_column+1) num += 1 if num % 5 == 0: grid_row += 1 grid_column = 0 else: grid_column += 2 #控制列数,每次label和lineedit占用两列所以+2

上面代码实现效果

4,数据展示:好的,现在通过上面代码已经实现了搜索项的展示,下面需要将表格内容展示在主页面位置,在上面函数的尾部写上连接方法,代码实现如下:

def show_data(self):    self.on_text_changed(data)#将data传入到连接方法
def on_text_changed(self, *args): ''' 作为combox触发函数内调用 :param data: dataframe类型数 :return: ''' data = args[0] self.tableWidget.setColumnCount(0) self.tableWidget.setRowCount(0) num_rows, num_cols = data.shape[0], data.shape[1] # 设置QTableWidget的行数和列数 self.tableWidget.setRowCount(num_rows) self.tableWidget.setColumnCount(num_cols) extra_width = 170 # 设置额外宽度 self.tableWidget.resizeColumnsToContents() # 自适应每列宽度 for column in range(self.tableWidget.columnCount()): width = self.tableWidget.columnWidth(column) self.tableWidget.setColumnWidth(column, width + extra_width) self.tableWidget.horizontalHeader().setStretchLastSection(True) # 设置每列的列名 column_names = data.columns self.tableWidget.setHorizontalHeaderLabels(column_names) for i in range(num_rows): for j in range(num_cols): item = QTableWidgetItem(str(data.iloc[i, j])) self.tableWidget.setItem(i, j, item) self.tableWidget.setAlternatingRowColors(True) # 开启交错颜色

执行代码实现界面如下:

5.内容搜索,内容搜索,需要将所有标题输入框内的内容求合集后,展示在数据展示区,

#先写一个搜索函数,可以传入三个值表格数据dataframe,#列名,搜索项,就可以返回所有对应数据的行号。def search_term(self, df_data, column_name, find_key):    '''    根据条件 find_key 在指定列中查找对应的表格中的数据    :param DataFrame, column_name, find_key(条件):    :return: 返回符合条件的行的行号    '''    filtered_df = df_data[column_name].astype(str).str.contains(find_key, case=False)    indexes = filtered_df[filtered_df].index.tolist()    return indexes

编写搜索按钮的连接函数,即可实现搜索功能

self.pushButton.clicked.connect(self.on_pushButton_clicked)def on_pushButton_clicked(self): line_edits = self.groupBox_2.findChildren(QLineEdit)#获取groupBox_2子属性中全部QLineEdit控件 #将所有QLineEdit列表中每个控件内的值和自定义属性my_attribute的值组成元组(obj1,obj2)并放入到列表select_list中 select_list = [(item.text().strip(), item.property('my_attribute')) for item in line_edits] search_list = [] #定义一个空列表,用来接收所有的搜索到的值 dataframe = self.groupBox_2.property('my_attribute') #获取为self.groupBox_2 自定义的属性值,该值是上面代码放入的表格的dataframe类型数据 for item, line in select_list:#遍历self.groupBox_2控件内所有QLineEdit内的值 item_list = self.search_term(dataframe, line, item) #将表格数据df,列名line,搜索项item传入搜索函数,用来获取搜索到的行号 search_list.append(item_list)#将搜索到的行号全部追加到search_list 列表中 common_elements = set(search_list[0]).intersection(*search_list[1:]) new_df = dataframe.iloc[list(common_elements)] return self.on_text_changed(new_df)

6.信息导出,最后实现信息的导出,

首先写一个可弹出的对话框类:

# 弹出的对话框class MyDialog(QDialog):    def __init__(self, data):        super().__init__()        self.initUI()        self.data = data        self.dir = os.getcwd()    def initUI(self):        layout = QVBoxLayout()        label = QLabel('选择导出位置')        layout.addWidget(label)        self.textEdit = QLineEdit()        layout.addWidget(self.textEdit)        self.textEdit.mousePressEvent = self.on_mousePressEvent        button = QPushButton('导出表格')        button.clicked.connect(self.selectDirectory)        layout.addWidget(button)        self.setLayout(layout)    def on_mousePressEvent(self, event):        if event.button() == QtCore.Qt.LeftButton:            directory = QFileDialog.getExistingDirectory(self, '选择目录')            if directory:                self.textEdit.setText(directory)        self.dir = directory    def selectDirectory(self):        try:            self.data.to_excel(self.dir + '/output.xlsx', index=False)        except Exception as e:            # 创建错误弹窗并设置错误消息            msg_box = QMessageBox()            msg_box.setIcon(QMessageBox.Critical)            msg_box.setWindowTitle('错误')            msg_box.setText('输出表格已打开,请关闭表格或选择其他导出位置')            # 显示错误弹窗            msg_box.exec()        self.accept()
#为self.pushButton_2 添加连接方法,还是在原来的类Ui_MainWindow中写self.pushButton_2.clicked.connect(self.openDialog)def openDialog(self): rows = self.tableWidget.rowCount()#获取self.tableWidget的行数 cols = self.tableWidget.columnCount()#获取self.tableWidget的列数 if rows != 0: data = [[] for _ in range(rows)] #设置data列表 for row in range(rows): for col in range(cols): item = self.tableWidget.item(row, col) #获取self.tableWidget 中的内容 if item is not None: data[row].append(item.text()) else: data[row].append('') header = [] #设置表格标题 for col in range(cols): header.append(self.tableWidget.horizontalHeaderItem(col).text()) data = pd.DataFrame(data, columns=header) dialog = MyDialog(data) #初始化MyDialog 方法 dialog.setWindowTitle('导出表格') # 设置对话框的标题 dialog.setWindowFlags(dialog.windowFlags() & ~QtCore.Qt.WindowContextHelpButtonHint) # 移除问号图标 dialog.textEdit.setText(dialog.dir) dialog.exec_()#塞程序的执行,直至对话框关闭

最终软件界面如下

一个小工具就这些写完了, 还是蛮简单的哈

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多