前言
大家好,我是北京科技大学的某个学生,因为出差的缘故不得不学习python这门语言,说实话一开始头晕脑胀的,连文件都不会创立,在ChatGPT这位老师(真的很好用,你可以给出输入预期和输出预期,直接帮你生成可行的代码)和某位北航大佬的无私解答下,从零开始构建了一个读取Excel表格数据画图的GUI界面,然后将其打包成为一个exe文件,经过实验可以在没有安装python的电脑上正常运行,鉴于我在打包exe时遇见了种种问题,在网络上搜查资料时也没有找到比较简单详细的教程,就把我自己的一个经历写出来供大家参考吧,希望各位大佬轻喷QAQ。
一、打包工具
python打包工具我用的是pyinstaller,至于介绍的话就不讲了,之前在网上浏览的文章有提到高版本python可能不适用的问题,我也没有遇见(用的3.7版本),下面就介绍打包流程
二、打包流程
1.引入代码
代码如下(我自己写的,比较菜,仅用来举例,大佬勿喷):
import pandas as pd
import matplotlib.pyplot as plt
import tkinter as tk
from tkcalendar import DateEntry
from tkinter import filedialog
from datetime import date
#几个按钮的触发事件设计
#region my code block
# Excel文件打开函数
def import_button_click():
global df
file_path = filedialog.askopenfilename(filetypes=[("Excel files", "*.xlsx;*.xls")])
if file_path:
# 使用pandas读取Excel文件
df = pd.read_excel(file_path, engine='openpyxl')
# print(df)
# 两天绘图按钮2点击
def print_button2_click():
global df
df2 = df
# 得到日历信息
calenda_data3 = root.date_entry3.get_date()
calenda_data4 = root.date_entry4.get_date()
calenda_data3 = pd.to_datetime(calenda_data3)
calenda_data4 = pd.to_datetime(calenda_data4)
if calenda_data3 <= calenda_data4:
NUM = 0
if NUM == 0:
dates = pd.to_datetime(df['日期'])
# print(dates)
# 绘制每个元素的折线图
i = -1
for date in dates:
i = i + 1
if (date != calenda_data3 and date != calenda_data4):
df2 = df2.drop(i)
# dates = dates[(dates == calenda_data3) | (dates == calenda_data4)]
df2['日期'] = pd.to_datetime(df2['日期'])
# 设置日期列为索引
df2.set_index('日期', inplace=True)
# 使用 plot 绘制折线图,交换 x 和 y 的位置
ax=df2.T.plot(marker='o', linestyle='-')
# 添加标题和标签
ax.set_xticks(range(0, len(df2.columns)))
ax.set_xticklabels(df2.columns) # 设置 x 轴标签为元素的列名
plt.rcParams['font.sans-serif'] = ['SimHei'] # 设置中文显示
plt.rcParams['axes.unicode_minus'] = False # 解决负号显示问题
plt.title('日期下不同元素的值的变化',fontproperties='SimHei')
plt.xlabel('元素',fontproperties='SimHei')
plt.ylabel('日期',fontproperties='SimHei')
# 显示图形
plt.show()
NUM = NUM + 1
else:print("日期输入错误")
# 一段时间绘图1按钮点击
def print_button_click():
global df
df2 = df
# 得到下拉栏信息
option_info = selected_option.get()
# 得到日历信息
calenda_data1 = root.date_entry1.get_date()
calenda_data2 = root.date_entry2.get_date()
calenda_data1 = pd.to_datetime(calenda_data1)
calenda_data2 = pd.to_datetime(calenda_data2)
if calenda_data1 <= calenda_data2:
if option_info == "全元素":
NUM = 0
if NUM == 0:
dates = pd.to_datetime(df['日期'])
# print(dates)
# 绘制每个元素的折线图
i = -1
for date in dates:
i = i + 1
if (date < calenda_data1 or date > calenda_data2):
df2 = df2.drop(i)
# print(df2)
# for element in df2.columns[1:]:
dates = dates[dates >= calenda_data1]
dates = dates[dates <= calenda_data2]
for element in df2.columns[1:]:
plt.plot(dates, df2[element], label=element)
# 添加标题、标签和图例,精简x轴数据显示
unique_x_values = list(set(dates))
plt.xticks(unique_x_values)
plt.rcParams['font.sans-serif'] = ['SimHei'] # 设置中文显示
plt.rcParams['axes.unicode_minus'] = False # 解决负号显示问题
plt.title(option_info + '折线图', fontproperties='SimHei')
plt.xlabel('日期', fontproperties='SimHei')
plt.ylabel('元素值', fontproperties='SimHei')
plt.legend(loc='upper right')
# 格式化日期显示
plt.gca().xaxis.set_major_formatter(plt.matplotlib.dates.DateFormatter('%Y-%m-%d'))
# 自动调整日期标签的显示以防止重叠
plt.gcf().autofmt_xdate()
# 显示图形
plt.show()
NUM = NUM + 1
if option_info != "全元素":
NUM = 0
if NUM == 0:
dates = pd.to_datetime(df['日期'])
# print(dates)
# 绘制每个元素的折线图
i = -1
for date in dates:
i = i + 1
if (date < calenda_data1 or date > calenda_data2):
df2 = df2.drop(i)
# print(df2)
# for element in df2.columns[1:]:
dates = dates[dates >= calenda_data1]
dates = dates[dates <= calenda_data2]
for element in df2.columns[1:]:
if(element==option_info):
plt.plot(dates, df2[element], label=element)
# 添加标题、标签和图例,精简x轴数据显示
unique_x_values = list(set(dates))
plt.xticks(unique_x_values)
plt.rcParams['font.sans-serif'] = ['SimHei'] # 设置中文显示
plt.rcParams['axes.unicode_minus'] = False # 解决负号显示问题
plt.title(option_info + '折线图', fontproperties='SimHei')
plt.xlabel('日期', fontproperties='SimHei')
plt.ylabel('元素值', fontproperties='SimHei')
plt.legend(loc='upper right')
# 格式化日期显示
plt.gca().xaxis.set_major_formatter(plt.matplotlib.dates.DateFormatter('%Y-%m-%d'))
# 自动调整日期标签的显示以防止重叠
plt.gcf().autofmt_xdate()
# 显示图形
plt.show()
NUM = NUM + 1
else:print("日期输入错误")
#endregion
# 页面设计
#region My Code Block
df = "文件内的信息"
sof = "下拉栏中的信息"
root = tk.Tk()
root.title("导入Excel文件")
# 添加标签
label = tk.Label(root, text="请点击按钮选择Excel文件")
label.pack(pady=20)
# 添加导入按钮
import_button = tk.Button(root, text="导入Excel文件", command=import_button_click)
import_button.pack()
# 添加绘图按钮
# 显示一段时间
print_button = tk.Button(root, text="一段时间绘图", command=print_button_click)
print_button.pack(side=tk.LEFT, padx=5)
# 显示两天的对比
print_button2 = tk.Button(root, text="两天比较绘图", command=print_button2_click)
print_button2.pack(side=tk.LEFT, padx=5)
# 添加下拉框
# 创建下拉框选项
options = ["全元素", "元素1", "元素2", "元素3", "元素4", "元素5", "元素6", "元素7", "元素8", "元素9", "元素10"]
# 创建变量以存储选中的选项
selected_option = tk.StringVar(root)
selected_option.set(options[0]) # 设置默认选项
# 创建下拉框
option_menu = tk.OptionMenu(root, selected_option, *options)
option_menu.pack(pady=20)
# selected_option.trace_add("write", option_selected)
# 添加标签
label2 = tk.Label(root, text="左边为一段时间,右边为只看这两天")
label2.pack(pady=20)
# 创造日历选择
# 添加四个日期选择器到你的现有界面
root.date_entry1 = DateEntry(root, width=12, background='darkblue',
foreground='white', borderwidth=2)
root.date_entry1.pack(side=tk.LEFT, padx=10, pady=10)
root.date_entry2 = DateEntry(root, width=12, background='darkblue',
foreground='white', borderwidth=2)
root.date_entry2.pack(side=tk.LEFT, padx=10, pady=10)
root.date_entry3 = DateEntry(root, width=12, background='darkblue',
foreground='white', borderwidth=2)
root.date_entry3.pack(side=tk.LEFT, padx=10, pady=10)
root.date_entry4 = DateEntry(root, width=12, background='darkblue',
foreground='white', borderwidth=2)
root.date_entry4.pack(side=tk.LEFT, padx=10, pady=10)
# 启动主循环
root.mainloop()
#endregion
2.安装pyinstaller并运行打包
1.在pycharm的终端这里点击一下
2.在打开的终端页面输入以下代码下载pyinstaller
如果下载不了的话可以看看网上解决方法,我的是蛮顺利的。
代码如下(示例):
pip install pyinstaller
3.输入你文件的路径,
路径可以在这里看到,直接复制就行。
还是在终端输入cd+空格+粘贴你刚才复制的路径
4.打包你用到的库和模板
库和模板可以在开头看见,虽然有些库和模板没有写在开头,但是我们后面可以补上去(就跟有人没票坐火车一样,先挤上去,等后面再补票),先把这些写在开头的打包掉(这是有票的乘客),代码形式:pyinstaller --hidden-import 库A–hidden-import --hidden-import 库B python文件名+.py,同样写在终端里。
我的代码如下:
pyinstaller --hidden-import pandas --hidden-import matplotlib --hidden-import tkinter --hidden-import tkcalendar --hidden-import openpyxl --hidden-import babel main.py
这里我的两个库并没有在开头出现,但是我在代码中使用了,这里忘记了自己导入的库也不要害怕,先把开头的导进去,后面有补票的办法。
这一步成功的话会生成一个dist文件夹,里面是我们的exe文件,还有一个SPEC文件,它的用处就是我们用来补票的,这个我们后面的步骤再聊。
5.打包代码
pyinstaller -F -w+空格+python文件名+.py,在这个命令中,pyinstaller 是打包工具的名称,-F 表示生成单个可执行文件(打包为单个文件),-w 表示在运行时不显示控制台窗口(仅限 Windows 系统),而文件名则是你的 Python 脚本文件的名称,同样我们把指令写到终端里,代码如下
pyinstaller -F -w main.py
在dist文件夹下(文件夹在你的python文件位置,忘了的话回步骤3看)我们就能找到生成的exe文件(应用程序),我们打开它
如果可以正常运行,那么恭喜你已经完成所有步骤,如果想在其他电脑上使用,只需要把exe文件(应用程序)这个东西复制粘贴到其他电脑中就行,如果出现如图所示的报错
是因为之前导入的库或模块不全导致了不能识别代码中的一部分(相当于没买票上车的人被乘务员发现了),那么请看接下来的报错处理部分(乖乖补票)。
6.报错处理
我们发现报错是因为缺少一个名字为’babel.numbers’的模块(这个坏同志没买票),我们现在来手动把它添加,回到之前的文件夹。
我们用笔记本打开SPEC文件,这个文件就是乘务员让我们补票的地方。
找到标蓝的地方,把刚才警告中显示缺少的库或者模板添加进去,添加的格式如下:
hiddenimports=['babel.numbers'],
多项的话就这么写:
hiddenimports=['U','S','T','B'],
保存后关闭文本。
回到pycharm的终端,输入pyinstaller python文件名.spec
等待成功改写后,我们再次打开exe文件(应用程序),发现已经可以运行了(还不行的话看看还有没有落下的库,重复步骤6,不然我也无能为力了)
这时候我们就可以把exe文件复制出来发给别的电脑用了。
7.删除文件
如果删除dist文件夹和SPEC文件的话,对原来代码的运行是没有影响的,这只是我们打包时产生的输出,如果看着碍眼,打包成功后可以直接扔到垃圾桶里,在我们整个打包过程中也可以随时删除,反正每次打包的时候如果有这个文件夹就把旧的改了写在里面,没有的话系统会自动创建一个写在里面。
结尾
希望新的一年里北交和北科合并成,成为985大学,直接进行一个学历升值。
祝大家新年快乐,写的代码永远一次通过,永远没有BUG!!