一、任务描述
事情的起因是我舍友做的应力传感方向的研究,他的传感器是一个实时性的检测,且会在指定路径下持续不断的生成.csv文件,csv文件如下图:
他需要在指定的横坐标范围内,寻找具有最小y值的点,即下图红框位置
找到后将其横坐标带入一个公式进行计算得到最终所需数值,同时最好能实时绘图进行显示。
根据上述需求我编写了以下代码
二、代码编写
import os
import time
import threading
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
from pynput.keyboard import Key, Listener
# from matplotlib.animation import FuncAnimation
# 设置Matplotlib后端设置为交互式后端
plt.switch_backend('TkAgg')
# 设置Matplotlib全局字体属性
plt.rcParams['font.sans-serif'] = ['SimHei'] # 指定默认字体为黑体
plt.rcParams['axes.unicode_minus'] = False # 让Matplotlib使用减号而不是长破折号
# 程序终止标志位
End_Target = True
# 按键按下进程
def on_press(key):
try:
# 如果按下的是ESC键
if key == Key.esc:
# 设置标志位为False
global End_Target
End_Target = False
# print("ESC键被按下, 程序终止")
except AttributeError:
print(f'special key {key} pressed')
# 按键松开进程
def on_release(key):
# print(f'{key} released')
print("程序终止......")
# 创建一个键盘监听器线程
def keyboard_listener():
with Listener(on_press=on_press, on_release=on_release) as listener:
listener.join()
# 绘图横坐标
t = []
# 最小纵坐标对应的波长位置的列表
Ramda_list = []
# 目标波长差值的列表
DeltaRamda_list = []
# 应力的列表(即为所求)
Ipsilon_list = []
class MyHandler(FileSystemEventHandler):
def on_created(self, event, max_rows = 520):
if event.is_directory:
return None
elif event.src_path.endswith(".csv"):
# 读取CSV文件
try:
# 读取文件并截取有效数据(跳过前15行和最后的无用数据)
df = pd.read_csv(event.src_path, names=['WL', 'Power'] ,header=0, skiprows=16, nrows=max_rows - 15 + 1)
# 返回一个 DataFrame
# print(df)
# 筛选WL列中值位于[1530, 1540]范围内的数据
filtered_df = df[(df['WL'] >= 1530) & (df['WL'] <= 1540)]
# print(filtered_df)
# 找到第二列中具有最小值的行
min_value_row = filtered_df.loc[filtered_df['Power'].idxmin()]
# 返回对应的第一列的值
result_WL, result_Power = min_value_row['WL'], min_value_row['Power']
if ((result_Power>5000)&(result_Power<50000)):
# print("文件"+ event.src_path +"处理完毕!")
# print("位于波长范围[1530, 1540]区域内幅值最小处波长为:"+ str(result_WL)+" 对应的幅值为:"+ str(result_Power))
global t, Ramda_list, DeltaRamda_list, Ipsilon_list
Ramda_list.append(result_WL)
if len(Ramda_list) == 1:
# 初始应力取为0
t.append(1)
DeltaRamda_list.append(0.00491)
Ipsilon_list.append((DeltaRamda_list[-1]-0.00491)/0.000966)
else:
# λ2-λ1|
t.append(t[-1] + 1)
DeltaRamda_list.append(Ramda_list[-1]-Ramda_list[-2])
Ipsilon_list.append((DeltaRamda_list[-1]-0.00491)/0.000966)
# print(Ramda_list)
# print(DeltaRamda_list)
except Exception as e:
print(f"Error processing {event.src_path}: {e}")
def get_data():
'''
功能:通过计算指定目录下的文件来获取对应的应力数值
'''
path = '.' # 监控当前文件夹目录
event_handler = MyHandler()
observer = Observer()
observer.schedule(event_handler, path, recursive=False)
observer.start()
try:
while End_Target:
time.sleep(1)
except KeyboardInterrupt:
observer.stop()
observer.join()
# def draw_plt():
# '''
# 功能:实时数据绘图
# '''
# # 将绘图放置于主线程以避免警告,同时方便结束进程
# # Matplotlib的GUI组件(如窗口、工具栏等)通常需要与主线程(即启动Python程序时创建的第一个线程)紧密交互,
# # 以确保它们能够正确响应用户输入和事件。
# # 开启动态显示(交互)
def main():
# 启动工作线程来获取数据
csv_reader = threading.Thread(target=get_data)
csv_reader.start()
# 启动键盘监听进程来检测程序退出
listener_thread = threading.Thread(target=keyboard_listener)
listener_thread.start()
# 开启动态(交互)绘图
plt.ion()
while End_Target:
# print(t)
# print(Ipsilon_list)
# 清除之前画的图
plt.clf()
# 绘图
# plt.figure(figsize=(18, 6)) # 设置图像大小为宽8英寸,高6英寸
plt.plot(t, Ipsilon_list, label='应变',
marker='o', # 标记样式:圆点
linestyle='-', # 线条样式:实线
color='blue', # 线条颜色:蓝色
linewidth=2, # 线宽:2
markersize=5)
plt.xlabel("采样次数", labelpad=10, fontsize='large')
plt.ylabel("应变值(με)", labelpad=5, fontsize='large')
plt.title("实时应变监测", fontsize='large')
# 添加图例
plt.legend()
# 显示网格线
plt.grid(True)
# 暂停一段时间,避免卡住显示不出来图
plt.pause(0.001)
# 关闭画图窗口
plt.ioff()
plt.close("all")
os._exit(0)
if __name__ == "__main__":
main()
程序整体思路大体就是主线程通过交互模式进行动态更新绘图显示,两个工作线程:csv_reader和listener_thread,csv_reader线程通过watchdog库实时检测指定路径下的csv文件的生成,每有一个新的csv文件生成便进行读取,分析其数据并通过计算获取到所需数据放到一个全局变量列表中,主进程通过访问该列表进行动态绘图;listener_thread线程则是通过pynput库实现实时监测键盘上的ESC键是否按下,如按下,则通过将标志位置为False的方式来终止csv_reader和主线程
程序部分的相关参考如下:
python第三方库pynput全局键盘监听+demo“ikun键盘”
python程序主动退出进程的方式:五种方式总有一种适合你
https://blog.csdn.net/cxx654/article/details/105763129
Python绘制线图之plt.plot()的介绍以及使用
plt.ion()画动态图
在Matplotlib中动态更新绘图的3种方法
Matplotlib绘图中文乱码,完整解决方法总结!
python多线程详解
Python全局变量及Global关键字( Global variable in Python)
三、项目打包
1. 以管理员模身份进入Anaconda Prompt
2. 通过cd指令将目录切换到项目所在位置
3. 新建虚拟环境并安装项目所需包
pip install pyinstaller # 首先先下载pyinstaller
conda create -n 虚拟环境名字 python==3.9.16 # 创建虚拟环境,此处python版本我选择3.9.16
conda activate 虚拟环境名字 # 激活虚拟环境
conda install watchdog pandas matplotlib # 安装项目所需包
pip install pynput # 这玩意好像没法通过conda安装
参考:Python打包成exe
4. 执行打包操作
Pyinstaller -F -w -i stress.ico main.py
其中-F意为打包成单个可执行文件,如果你的代码都写在一个.py文件,则可以使用,如果是多个.py文件,建议不要使用。
-w意为使用Windows子系统执行.当程序启动的时候不会打开命令行(仅对Windows有效),-i则是为你打包的文件选取一个图标,这里我直接那ppt写了一个,截屏+改后缀为.ico并放置于当前目录下,main.py则是我的项目文件也就是上面那个,打包过程大概几分钟左右。
打包完成后会在当前路径下的dist文件夹里生成一个.exe文件,即为所得
5. 打包过程中遇到的问题
四、最终效果
批处理文件打包