我编写了一个 python GUI ,它应通过单击名为“开始” 的按钮来创建和更新 .csv 文件,并应停止while循环,通过单击另一个名为“停止” 的按钮来更新.csv。但是,每当我运行GUI并单击开始时,它就会冻结。虽然,我看到 .csv 文件正在不断更新,但是我无法阻止.csv更新新行。我只是简单地使用python 2.7和ubuntu终端运行代码,编写 python filename.py 。 任何人都可以检查我的代码有什么问题吗?
from Tkinter import *
import datetime
import sys
import time
import csv
import math
A1 = 0
def csv_write(label):
global A1
A1 = 0
A = str(datetime.datetime.now()) + ".csv"
start = time.time()
elapsed = 0
with open(A, 'wt') as filename:
csv_writer = csv.writer(filename, delimiter=',')
csv_writer.writerow(('IMU', 'Time', 'A.Sensor.X', 'A.Sensor.Y', 'A.Sensor.Z', 'G.Sensor.X', 'G.Sensor.Y',
'G.Sensor.Z', 'M.Sensor.X', 'M.Sensor.Y', 'M.Sensor.Z',
'IMU', 'Time', 'A.Sensor.X', 'A.Sensor.Y', 'A.Sensor.Z', 'G.Sensor.X', 'G.Sensor.Y',
'G.Sensor.Z', 'M.Sensor.X', 'M.Sensor.Y', 'M.Sensor.Z',
'IMU', 'Time', 'A.Sensor.X', 'A.Sensor.Y', 'A.Sensor.Z', 'G.Sensor.X', 'G.Sensor.Y',
'G.Sensor.Z', 'M.Sensor.X', 'M.Sensor.Y', 'M.Sensor.Z',
'IMU', 'Time', 'A.Sensor.X', 'A.Sensor.Y', 'A.Sensor.Z', 'G.Sensor.X', 'G.Sensor.Y',
'G.Sensor.Z', 'M.Sensor.X', 'M.Sensor.Y', 'M.Sensor.Z',
'IMU', 'Time', 'A.Sensor.X', 'A.Sensor.Y', 'A.Sensor.Z', 'G.Sensor.X', 'G.Sensor.Y',
'G.Sensor.Z', 'M.Sensor.X', 'M.Sensor.Y', 'M.Sensor.Z',
'IMU', 'Time', 'A.Sensor.X', 'A.Sensor.Y', 'A.Sensor.Z', 'G.Sensor.X', 'G.Sensor.Y',
'G.Sensor.Z', 'M.Sensor.X', 'M.Sensor.Y', 'M.Sensor.Z'))
while (A1==0):
elapsed = str(time.time() - start)
label['text']=elapsed
csv_writer.writerow((1, 1, 2, 3,
4, 5, 6,
7,8, 9,
1, 2, 3,
4, 5, 6,
7, 8,
9, 1, 2,
3, 4, 5,
6, 7, 8,
9, 0, 1,
2, 3, 4,
5, 6, 7,
8, 9, 0,
1, 2,
3, 4, 5,
6, 7, 8,
9, 0, 1,
2, 3,
4, 5, 6,
7, 8, 9,
0, 1, 2,
3, 4,
5, 6, 7))
def stop():
global A1
A1 = 1
root = Tk()
frame = Frame(root)
frame.pack()
root.title("connect and get sensor data")
root.geometry("500x500") # You want the size of the app to be 500x500
root.resizable(0, 0) # Don't allow resizing in the x or y direction
label = Label(root, text="Welcome!", fg="black", font="Verdana 15 bold")
label.pack(side=TOP, padx=5 )
button = Button(root, text='Start', width=25, command=lambda: csv_write(label))
button1 = Button(root, text='Stop', width=25, command=lambda: stop())
button1.pack(side=BOTTOM, pady=10)
button.pack(side=BOTTOM, pady=10)
root.mainloop()
答案 0 :(得分:1)
使用tkinter这样的GUI工具包时,程序的工作方式与普通的python脚本不同。
GUI依赖于事件循环进行更新。因此,您的代码必须适合事件循环以回调或超时函数的形式。此类回调不应花费太长时间,因为它们是从事件循环执行的。如果他们花费足够长的时间,鼠标和键盘事件将会堆积。由于GUI没有响应,因此这很明显。
有几种解决方法。
最简单的方法是将更新过程分成几小段,例如一行。您将当前行的索引保留为全局变量。
在一个函数中,您将索引行写入文件,增加索引。该功能注册为超时功能(使用after
的{{1}}方法)。函数应做的最后一件事是再次重新注册自身(使用tkinter.Tk
),除非 after
。在A1 == 1
按钮的回调中,使用Start
安排更新功能。
另外两个选项是使用多线程进行多处理。但是,这些要复杂得多。对于新手或相对简单的任务,我都不推荐使用它们。
让我们谈谈在不同线程中进行更新。这可能很复杂,因为after
不是线程安全的;您应该不从第二个线程调用tkinter
。由于两个线程都可以看到和更改相同的全局变量,因此必须谨慎使用它们。您应该使用锁来保护可以从两个线程读取或更新的变量(例如互斥锁)。也就是说,在两个线程中,您都应该在更改变量之前获取锁,并在进行更改后释放锁。如果变量是可变数据结构,则即使从锁中读取时也应谨慎使用。此外,Python3在划分不同线程之间的处理器时间方面比Python2更好。因此,如果您使用后者,则可能无法按预期工作。
第三个选项是在不同的过程中进行编写。这意味着您必须使用进程间通信,该通信也必须平稳地放入事件循环中。
下面是我编写的使用tkinter
的示例程序。这是用于ms-windows的简单查找和替换实用程序。原始文件托管在github上。
几句话:
我正在定义一个从after
继承的类作为用户界面。这使得正确封装数据变得容易。所有回调方法都会自动访问该对象的属性。您可以在不使用类的情况下执行tkinter程序,但是这样会更加混乱。
tk.Tk
方法创建了对象(和必要的属性),但是我将创建窗口分成了__init__
方法。
create_window
方法正在完成工作的一步。
回调方法的名称以replace_step
结尾。这只是使它们更易于查找的约定。
_cb
函数在启动GUI之前处理命令行参数。
这是代码。我希望您觉得它有用。
main
答案 1 :(得分:0)
在使用Thread和全局变量时它对我有用。没有那么复杂,只有几行。您的代码已被修改。希望对您有所帮助:-
from tkinter import *
import datetime
import sys
import time
import csv
import math
from threading import Thread
def start_thread(label):
global A1
A1 = 0
# Create and launch a thread
t = Thread(target = csv_write, args = (label, ))
t.start()
def csv_write(label):
global A1
A1 = 0
A = str(datetime.datetime.now()) + ".csv"
start = time.time()
elapsed = 0
with open(A, 'wt') as filename:
csv_writer = csv.writer(filename, delimiter=',')
csv_writer.writerow(('IMU', 'Time', 'A.Sensor.X', 'A.Sensor.Y', 'A.Sensor.Z', 'G.Sensor.X', 'G.Sensor.Y',
'G.Sensor.Z', 'M.Sensor.X', 'M.Sensor.Y', 'M.Sensor.Z',
'IMU', 'Time', 'A.Sensor.X', 'A.Sensor.Y', 'A.Sensor.Z', 'G.Sensor.X', 'G.Sensor.Y',
'G.Sensor.Z', 'M.Sensor.X', 'M.Sensor.Y', 'M.Sensor.Z',
'IMU', 'Time', 'A.Sensor.X', 'A.Sensor.Y', 'A.Sensor.Z', 'G.Sensor.X', 'G.Sensor.Y',
'G.Sensor.Z', 'M.Sensor.X', 'M.Sensor.Y', 'M.Sensor.Z',
'IMU', 'Time', 'A.Sensor.X', 'A.Sensor.Y', 'A.Sensor.Z', 'G.Sensor.X', 'G.Sensor.Y',
'G.Sensor.Z', 'M.Sensor.X', 'M.Sensor.Y', 'M.Sensor.Z',
'IMU', 'Time', 'A.Sensor.X', 'A.Sensor.Y', 'A.Sensor.Z', 'G.Sensor.X', 'G.Sensor.Y',
'G.Sensor.Z', 'M.Sensor.X', 'M.Sensor.Y', 'M.Sensor.Z',
'IMU', 'Time', 'A.Sensor.X', 'A.Sensor.Y', 'A.Sensor.Z', 'G.Sensor.X', 'G.Sensor.Y',
'G.Sensor.Z', 'M.Sensor.X', 'M.Sensor.Y', 'M.Sensor.Z'))
while (A1==0):
elapsed = str(time.time() - start)
label['text']=elapsed
csv_writer.writerow((1, 1, 2, 3,
4, 5, 6,
7,8, 9,
1, 2, 3,
4, 5, 6,
7, 8,
9, 1, 2,
3, 4, 5,
6, 7, 8,
9, 0, 1,
2, 3, 4,
5, 6, 7,
8, 9, 0,
1, 2,
3, 4, 5,
6, 7, 8,
9, 0, 1,
2, 3,
4, 5, 6,
7, 8, 9,
0, 1, 2,
3, 4,
5, 6, 7))
def stop():
global A1
A1 = 1
root = Tk()
frame = Frame(root)
frame.pack()
root.title("connect and get sensor data")
root.geometry("500x500") # You want the size of the app to be 500x500
root.resizable(0, 0) # Don't allow resizing in the x or y direction
label = Label(root, text="Welcome!", fg="black", font="Verdana 15 bold")
label.pack(side=TOP, padx=5 )
button = Button(root, text='Start', width=25, command=lambda: start_thread(label))
button1 = Button(root, text='Stop', width=25, command=lambda: stop())
button1.pack(side=BOTTOM, pady=10)
button.pack(side=BOTTOM, pady=10)
root.mainloop()