在Tkinter中使用停止按钮打破一会儿循环

时间:2018-09-16 16:31:23

标签: python tkinter

我编写了一个 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()

2 个答案:

答案 0 :(得分:1)

使用tkinter这样的GUI工具包时,程序的工作方式与普通的python脚本不同。

GUI依赖于事件循环进行更新。因此,您的代码必须适合事件循环以回调或超时函数的形式。此类回调不应花费太长时间,因为它们是从事件循环执行的。如果他们花费足够长的时间,鼠标和键盘事件将会堆积。由于GUI没有响应,因此这很明显。

有几种解决方法。

最简单的方法是将更新过程分成几小段,例如一行。您将当前行的索引保留为全局变量。 在一个函数中,您将索引行写入文件,增加索引。该功能注册为超时功能(使用after的{​​{1}}方法)。函数应做的最后一件事是再次重新注册自身(使用tkinter.Tk),除非 after。在A1 == 1按钮的回调中,使用Start安排更新功能。

另外两个选项是使用多线程进行多处理。但是,这些要复杂得多。对于新手或相对简单的任务,我都不推荐使用它们。

让我们谈谈在不同线程中进行更新。这可能很复杂,因为after不是线程安全的;您应该从第二个线程调用tkinter。由于两个线程都可以看到和更改相同的全局变量,因此必须谨慎使用它们。您应该使用锁来保护可以从两个线程读取或更新的变量(例如互斥锁)。也就是说,在两个线程中,您都应该在更改变量之前获取锁,并在进行更改后释放锁。如果变量是可变数据结构,则即使从锁中读取时也应谨慎使用。此外,Python3在划分不同线程之间的处理器时间方面比Python2更好。因此,如果您使用后者,则可能无法按预期工作。

第三个选项是在不同的过程中进行编写。这意味着您必须使用进程间通信,该通信也必须平稳地放入事件循环中。

下面是我编写的使用tkinter的示例程序。这是用于ms-windows的简单查找和替换实用程序。原始文件托管在github上。

几句话:

  1. 我正在定义一个从after继承的类作为用户界面。这使得正确封装数据变得容易。所有回调方法都会自动访问该对象的属性。您可以在不使用类的情况下执行tkinter程序,但是这样会更加混乱。

  2. tk.Tk方法创建了对象(和必要的属性),但是我将创建窗口分成了__init__方法。

  3. create_window方法正在完成工作的一步。

  4. 回调方法的名称以replace_step结尾。这只是使它们更易于查找的约定。

  5. _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()