qtdesigner使用QTableWidget控件实现读取excel表
文章目录
前言
之前学习QTableWidget的时候一直是捏造虚假数据加到表格里面,但是现实中数据来源多种多样,于是想尝试读取excel的数据到表格里面,还真的可以,于是记录下来,方便以后参考。
一、构建界面
页面布局如下:
- 第一行由一个QLabel、和一个QLineEdit组成,其中QLineEdit的类名改成file_path_edt,水平布局
- 第二行由两个QButton和一个Horizontal Scaper组成,其中选择文件按钮类名改成choice_btn,打开按钮类名改成open_btn,水平布局
- 第三行由一个QTableWidget组成,类名改成data_table,因为只有一个控件,所以不需要布局
- 整个窗体设置成垂直布局
不懂如何布局的请看这里qtdesigner页面布局
页面设计完之后,输入命令将ui文件转换为py文件(我的ui文件名为table_demo)
pyuic5 -o table_ui.py table_demo.ui
然后编写代码使得界面能够展示
table_main.py
from PyQt5.QtWidgets import QMainWindow, QApplication
import sys
from ui.table_ui import Ui_MainWindow
class TableWin(QMainWindow, Ui_MainWindow):
def __init__(self):
super().__init__()
self.setupUi(self)
if __name__ == '__main__':
app = QApplication(sys.argv)
table_win = TableWin()
table_win.show()
sys.exit(app.exec_())
附上项目文件目录
二、逻辑代码编写
完整代码在最后,现在先写代码编写思路
2.1、拖曳文件获取文件路径
这是个很常见的功能,就是将文件拖进窗口里面之后,读取文件内容。我这里只读取文件的绝对路径,之后再根据文件路径打开文件。
首先,要设置一个参数保存文件路径,在初始化函数里面初始化该参数。同时因为要有拖曳动作,所以要设置窗口接受拖曳动作。
class TableWin(QMainWindow, Ui_MainWindow):
def __init__(self):
super().__init__()
self.setupUi(self)
self.setAcceptDrops(True) # 设置接受拖曳动作
self.file_path = '' # 定义文件路径
然后,需要重写拖曳方法,就是拖曳文件到窗口之后,保存文件文件路径,并且设置输入框文本为文件路径
'''重写拖曳方法'''
def dragEnterEvent(self, event):
if event.mimeData().hasUrls:
event.accept()
else:
event.ignore()
def dragMoveEvent(self, event):
if event.mimeData().hasUrls:
try:
event.setDropAction(Qt.CopyAction)
except Exception as e:
print(e)
event.accept()
else:
event.ignore()
def dropEvent(self, event):
if event.mimeData().hasUrls:
event.setDropAction(Qt.CopyAction)
event.accept()
links = []
for url in event.mimeData().urls():
links.append(str(url.toLocalFile()))
self.file_path = links[0] # 获取文件绝对路径
self.set_file_path_edt(self.file_path) # 设置文件绝对路径
else:
event.ignore()
# 设置输入框里面内容
def set_file_path_edt(self, file_path):
self.file_path_edt.setText(file_path)
现在拖曳文件到窗口里面就可以获取文件路径啦。
2.2、选择文件获取文件路径
虽然拖曳文件获取文件数据是个常见功能,但也会有部分人喜欢打开文件夹选择文件,因此这功能也要实现。
首先,先实现打开文件夹选择文件的方法
def choice_file(self):
now_path = os.getcwd() # 获取当前路径
choice_file_path = QFileDialog.getOpenFileName(self, '选择文件', now_path, 'Excel files(*.xlsx , *.xls)')
# 如果路径存在,设置文件路径和输入框内容
if choice_file_path[0]:
self.file_path = choice_file_path[0]
self.set_file_path_edt(choice_file_path[0])
该方法在点击选择文件按钮之后实现,所以将按钮点击事件跟该方法关联起来,而且窗口初始化的时候就要关联,所以代码如下
class TableWin(QMainWindow, Ui_MainWindow):
def __init__(self):
super().__init__()
self.setupUi(self)
self.setAcceptDrops(True) # 设置接受拖曳动作
self.file_path = '' # 定义文件路径
self.listener() # 调用监听函数
# 监听函数都写在里面
def listener(self):
self.choice_btn.clicked.connect(self.choice_file)
2.3、打开文件读取数据
1. 获取了文件路径之后,就可以根据路径打开文件了,我们这里使用的是pandas来打开excel文件。
2. 打开的文件格式也要有限制,只打开excel文件,不是excel文件则警告。
3. 该方法在点击打开按钮之后实现,所以要将打开按钮和该方法关联起来。
具体代码如下:
class TableWin(QMainWindow, Ui_MainWindow):
def __init__(self):
super().__init__()
self.setupUi(self)
self.setAcceptDrops(True) # 设置接受拖曳动作
self.file_path = '' # 定义文件路径
self.listener() # 调用监听函数
# 监听函数都写在里面
def listener(self):
self.choice_btn.clicked.connect(self.choice_file)
self.open_btn.clicked.connect(self.open_file)
# 打开文件
def open_file(self):
# 判断文件路径是否有值(就是有没有选择了文件)
if self.file_path:
# 获取文件后缀
file_format = self.file_path.split('.')[-1]
# 判断文件格式是否是'xls'或者'xlsx'
if file_format == 'xls' or file_format == 'xlsx':
# 使用pandas提取excel数据
excel_data = pd.read_excel(self.file_path)
# 获取excel数据行数
rows = excel_data.shape[0]
# 获取excel数据列数
columns = excel_data.shape[1]
# print(rows, columns)
# 设置总列数
self.data_table.setColumnCount(columns)
# 设置总行数
self.data_table.setRowCount(rows)
# 设置表头(excel_data.columns可以获取表头列表,默认第一列为表头)
self.data_table.setHorizontalHeaderLabels(excel_data.columns)
'''遍历excel中的元素,添加到tableWidget里面'''
# 设置写数据的当前索引(excel中的第二行对应tableWidget中的第一行,索引并不一样,所以要设置新索引跟踪tableWidget中的索引)
data_table_current = 0
# 插入值
for i in range(0, rows):
for j in range(columns):
# 创建QTableWidgetItem对象,并设置值
new_item = QTableWidgetItem(str(excel_data.iloc[i, j]))
# 设置QTableWidgetItem对象启用、可以选中和可以编辑
new_item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemIsEditable)
# 设置QTableWidgetItem对象里面的值水平,垂直居中
new_item.setTextAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
# 将新建的QTableWidgetItem添加到tableWidget中,参数分别是(行索引,列索引,QTableWidgetItem对象)
self.data_table.setItem(data_table_current, j, new_item)
# 写数据的当前索引加1
data_table_current += 1
else:
QMessageBox.warning(self, '警告', '请选择xlsx或者xls文件!')
else:
QMessageBox.warning(self, '警告', '请选择文件!')
综上所述,完整代码如下:
table_main.py
from PyQt5.QtWidgets import QMainWindow, QApplication, QFileDialog, QMessageBox, QTableWidgetItem
from PyQt5.QtCore import Qt
import sys
import os
import pandas as pd
from ui.table_ui import Ui_MainWindow
class TableWin(QMainWindow, Ui_MainWindow):
def __init__(self):
super().__init__()
self.setupUi(self)
self.setAcceptDrops(True) # 设置接受拖曳动作
self.file_path = '' # 定义文件路径
self.listener() # 调用监听函数
# 监听函数都写在里面
def listener(self):
self.choice_btn.clicked.connect(self.choice_file)
self.open_btn.clicked.connect(self.open_file)
def choice_file(self):
now_path = os.getcwd() # 获取当前路径
choice_file_path = QFileDialog.getOpenFileName(self, '选择文件', now_path, 'Excel files(*.xlsx , *.xls)')
# 如果路径存在,设置文件路径和输入框内容
if choice_file_path[0]:
self.file_path = choice_file_path[0]
self.set_file_path_edt(choice_file_path[0])
# 打开文件
def open_file(self):
# 判断文件路径是否有值(就是有没有选择了文件)
if self.file_path:
# 获取文件后缀
file_format = self.file_path.split('.')[-1]
# 判断文件格式是否是'xls'或者'xlsx'
if file_format == 'xls' or file_format == 'xlsx':
# 使用pandas提取excel数据
excel_data = pd.read_excel(self.file_path)
# 获取excel数据行数
rows = excel_data.shape[0]
# 获取excel数据列数
columns = excel_data.shape[1]
# print(rows, columns)
# 设置总列数
self.data_table.setColumnCount(columns)
# 设置总行数
self.data_table.setRowCount(rows)
# 设置表头(excel_data.columns可以获取表头列表,默认第一列为表头)
self.data_table.setHorizontalHeaderLabels(excel_data.columns)
'''遍历excel中的元素,添加到tableWidget里面'''
# 设置写数据的当前索引(excel中的第二行对应tableWidget中的第一行,索引并不一样,所以要设置新索引跟踪tableWidget中的索引)
data_table_current = 0
# 插入值
for i in range(0, rows):
for j in range(columns):
# 创建QTableWidgetItem对象,并设置值
new_item = QTableWidgetItem(str(excel_data.iloc[i, j]))
# 设置QTableWidgetItem对象启用、可以选中和可以编辑
new_item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemIsEditable)
# 设置QTableWidgetItem对象里面的值水平,垂直居中
new_item.setTextAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
# 将新建的QTableWidgetItem添加到tableWidget中,参数分别是(行索引,列索引,QTableWidgetItem对象)
self.data_table.setItem(data_table_current, j, new_item)
# 写数据的当前索引加1
data_table_current += 1
else:
QMessageBox.warning(self, '警告', '请选择xlsx或者xls文件!')
else:
QMessageBox.warning(self, '警告', '请选择文件!')
# 设置输入框里面内容
def set_file_path_edt(self, file_path):
self.file_path_edt.setText(file_path)
'''重写拖曳方法'''
def dragEnterEvent(self, event):
if event.mimeData().hasUrls:
event.accept()
else:
event.ignore()
def dragMoveEvent(self, event):
if event.mimeData().hasUrls:
try:
event.setDropAction(Qt.CopyAction)
except Exception as e:
print(e)
event.accept()
else:
event.ignore()
def dropEvent(self, event):
if event.mimeData().hasUrls:
event.setDropAction(Qt.CopyAction)
event.accept()
links = []
for url in event.mimeData().urls():
links.append(str(url.toLocalFile()))
self.file_path = links[0] # 获取文件绝对路径
self.set_file_path_edt(self.file_path) # 设置文件绝对路径
else:
event.ignore()
if __name__ == '__main__':
app = QApplication(sys.argv)
table_win = TableWin()
table_win.show()
sys.exit(app.exec_())
运行结果:
大功告成!有什么问题欢迎在评论区留言。
2022/08/22更新
保存功能
只有打开和更改功能还不太够,加上保存功能才算完美。
首先,如下图添加一个保存按钮,且类名为save_btn
之后,执行命令覆盖之前的界面代码
pyuic5 -o table_ui.py table_demo.ui
保存代码逻辑如下
1. 使用pandas创建一个DataFrame,用来保存要存储的数据
2. 循环遍历QTableWidget里面的数据,存储到DataFrame里面
3. 将DataFrame保存到excel里面
因为创建DataFrame的时候需要行索引和列索引,所以之前的代码要加上获取列索引的代码(加上两行代码):
table_main.py
class TableWin(QMainWindow, Ui_MainWindow):
def __init__(self):
....
self.table_header = '' # 存储表头
....
# 打开文件
def open_file(self):
......
# 设置表头(excel_data.columns可以获取表头列表,默认第一列为表头)
self.data_table.setHorizontalHeaderLabels(excel_data.columns)
# 保存表头数据
self.table_header = excel_data.columns
......
实现保存文件方法:
# 保存文件
def save_file(self):
# 判断文件路径是否有值(就是有没有选择了文件)
if self.file_path:
save_file_path = QFileDialog.getSaveFileName(self, '选择保存路径', '', 'xlsx(*.xlsx)')
# 选择了路径则保存
if save_file_path[0] == '':
return
else:
try:
# 获取当前表的行数列数
columns = self.data_table.columnCount()
rows = self.data_table.rowCount()
index_data = [] # 存储行索引数据,[1, 2, 3, .....]
for i in range(rows):
index_data.append(i)
# 创建空的DataFrame
save_data = pd.DataFrame(columns=self.table_header, index=index_data)
# 赋值
for i in range(rows):
for j in range(columns):
save_data.iloc[i, j] = self.data_table.item(i, j).text()
# 创建excel对象
excel = pd.ExcelWriter(save_file_path[0])
# 将数据保存到excel中
save_data.to_excel(excel, index=None, sheet_name="数据")
excel.save()
# 提示保存成功
QMessageBox.information(self, "成功", "保存成功!", QMessageBox.Yes)
except:
QMessageBox.critical(self, "成功", "保存成功!", QMessageBox.Yes)
else:
QMessageBox.warning(self, "警告", "请先打开文件!", QMessageBox.Yes)
完整代码如下:
table_main.py
from PyQt5.QtWidgets import QMainWindow, QApplication, QFileDialog, QMessageBox, QTableWidgetItem
from PyQt5.QtCore import Qt
import sys
import os
import pandas as pd
from ui.table_ui import Ui_MainWindow
class TableWin(QMainWindow, Ui_MainWindow):
def __init__(self):
super().__init__()
self.setupUi(self)
self.setAcceptDrops(True) # 设置接受拖曳动作
self.table_header = '' # 存储表头
self.file_path = '' # 定义文件路径
self.listener() # 调用监听函数
# 监听函数都写在里面
def listener(self):
self.choice_btn.clicked.connect(self.choice_file)
self.open_btn.clicked.connect(self.open_file)
self.save_btn.clicked.connect(self.save_file)
def choice_file(self):
now_path = os.getcwd() # 获取当前路径
choice_file_path = QFileDialog.getOpenFileName(self, '选择文件', now_path, 'Excel files(*.xlsx , *.xls)')
# 如果路径存在,设置文件路径和输入框内容
if choice_file_path[0]:
self.file_path = choice_file_path[0]
self.set_file_path_edt(choice_file_path[0])
# 打开文件
def open_file(self):
# 判断文件路径是否有值(就是有没有选择了文件)
if self.file_path:
# 获取文件后缀
file_format = self.file_path.split('.')[-1]
# 判断文件格式是否是'xls'或者'xlsx'
if file_format == 'xls' or file_format == 'xlsx':
# 使用pandas提取excel数据
excel_data = pd.read_excel(self.file_path)
# 获取excel数据行数
rows = excel_data.shape[0]
# 获取excel数据列数
columns = excel_data.shape[1]
# print(rows, columns)
# 设置总列数
self.data_table.setColumnCount(columns)
# 设置总行数
self.data_table.setRowCount(rows)
# 设置表头(excel_data.columns可以获取表头列表,默认第一列为表头)
self.data_table.setHorizontalHeaderLabels(excel_data.columns)
# 保存表头数据
self.table_header = excel_data.columns
'''遍历excel中的元素,添加到tableWidget里面'''
# 设置写数据的当前索引(excel中的第二行对应tableWidget中的第一行,索引并不一样,所以要设置新索引跟踪tableWidget中的索引)
data_table_current = 0
# 插入值
for i in range(0, rows):
for j in range(columns):
# 创建QTableWidgetItem对象,并设置值
new_item = QTableWidgetItem(str(excel_data.iloc[i, j]))
# 设置QTableWidgetItem对象启用、可以选中和可以编辑
new_item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemIsEditable)
# 设置QTableWidgetItem对象里面的值水平,垂直居中
new_item.setTextAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
# 将新建的QTableWidgetItem添加到tableWidget中,参数分别是(行索引,列索引,QTableWidgetItem对象)
self.data_table.setItem(data_table_current, j, new_item)
# 写数据的当前索引加1
data_table_current += 1
else:
QMessageBox.warning(self, '警告', '请选择xlsx或者xls文件!')
else:
QMessageBox.warning(self, '警告', '请选择文件!')
# 设置输入框里面内容
def set_file_path_edt(self, file_path):
self.file_path_edt.setText(file_path)
# 保存文件
def save_file(self):
# 判断文件路径是否有值(就是有没有选择了文件)
if self.file_path:
save_file_path = QFileDialog.getSaveFileName(self, '选择保存路径', '', 'xlsx(*.xlsx)')
# 选择了路径则保存
if save_file_path[0] == '':
return
else:
try:
# 获取当前表的行数列数
columns = self.data_table.columnCount()
rows = self.data_table.rowCount()
index_data = [] # 存储行索引数据,[1, 2, 3, .....]
for i in range(rows):
index_data.append(i)
# 创建空的DataFrame
save_data = pd.DataFrame(columns=self.table_header, index=index_data)
# 赋值
for i in range(rows):
for j in range(columns):
save_data.iloc[i, j] = self.data_table.item(i, j).text()
# 创建excel对象
excel = pd.ExcelWriter(save_file_path[0])
# 将数据保存到excel中
save_data.to_excel(excel, index=None, sheet_name="数据")
excel.save()
# 提示保存成功
QMessageBox.information(self, "成功", "保存成功!", QMessageBox.Yes)
except:
QMessageBox.critical(self, "成功", "保存成功!", QMessageBox.Yes)
else:
QMessageBox.warning(self, "警告", "请先打开文件!", QMessageBox.Yes)
'''重写拖曳方法'''
def dragEnterEvent(self, event):
if event.mimeData().hasUrls:
event.accept()
else:
event.ignore()
def dragMoveEvent(self, event):
if event.mimeData().hasUrls:
try:
event.setDropAction(Qt.CopyAction)
except Exception as e:
print(e)
event.accept()
else:
event.ignore()
def dropEvent(self, event):
if event.mimeData().hasUrls:
event.setDropAction(Qt.CopyAction)
event.accept()
links = []
for url in event.mimeData().urls():
links.append(str(url.toLocalFile()))
self.file_path = links[0] # 获取文件绝对路径
self.set_file_path_edt(self.file_path) # 设置文件绝对路径
else:
event.ignore()
if __name__ == '__main__':
app = QApplication(sys.argv)
table_win = TableWin()
table_win.show()
sys.exit(app.exec_())
运行结果(张三的年龄改成了25):