线程/Tkinter Python [英] Thread / Tkinter Python

查看:72
本文介绍了线程/Tkinter Python的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想制作一个脚本或一个小程序,给我我的cps(每秒点击数),但是我发现一个小问题使计时器达到10秒,同时单击左键单击.我尝试了模块线程,但不适用于tkinter我尝试了所有方法(使函数内的计时器执行多个函数以增加变量ex ...中的计时器),但我从未设法同时进行.我的程序应该看起来像可以创建此站点的内容:www.mcrpg.com/kohi-click-test

I would like to make a script or a small program which gives me my cps (clicks per second) but I find a small problem to make a timer of 10 seconds and at the same time click on the button left click. I tried the module threading but it does not work with tkinter I have try everything (make the timer inside the function execute several functions to increment the timer in a variable ex ...) but I never manage to do it at the same time. My program should look like what can makes this site: www.mcrpg.com/kohi-click-test

Ps:要测试我的问题,请单击开始"而不是测试".

Ps: to test my problem click on starting not test Start.

import time
import os
from tkinter import *
from tkinter.constants import *
from threading import Thread

class Interface(Frame):

def run(self):
    thread1 = Thread(target = self.Démarrer )
    thread2 = Thread(target = self.timer)
    thread1.start()
    thread2.start()

    thread1.join()
    thread2.join()
    fenetre.update()

def timer(self):
    length = 10
    for i in range(1,(length+1)):
        print(i)
        self.Label2["text"] = "Le temps est {}".format(i)
        fenetre.update()
        time.sleep(1)

def MaApp(self):

    self.nb_clic += 1      
    self.cps = (self.nb_clic / 10)
    self.Label["text"]="Le Nombre de clic est de {}".format(self.nb_clic)
    self.Label1["text"] = "Votre cps est de {}".format(self.cps)
    fenetre.update()

def Démarrer(self):

    self.bouton_cliquer["text"]= "Clic Gauche"
    self.bouton_cliquer["command"] = self.MaApp
    fenetre.update()



def __init__(self, fenetre, **kwargs):

    Frame.__init__(self, fenetre, **kwargs)
    self.pack(fill=BOTH) 

    fenetre.geometry("400x200+300+300")

    fenetre.title("ClickTest")

    #Variable


    self.nb_clic = 0

    self.cps = (self.nb_clic / 10)

    self.temps = 0

    # Création de nos widgets



    self.Label = Label(self , text="Le Nombre de clic est de {}".format(self.nb_clic))
    self.Label.pack()

    self.Label1 = Label(self, text="Votre cps est de {}".format(self.cps))
    self.Label1.pack()


    self.Label2 = Label(self , text=("Le temps est {}").format(self.temps))
    self.Label2.pack()


    self.bouton_quitter = Button(self, text="Quitter",
                                 command=self.quit
                                 )
    self.bouton_quitter.pack(side="left")

    self.bouton_cliquer = Button(self, text="Démarrer" ,
                             command=self.run
                             )
    self.bouton_cliquer.pack(side="right")
    fenetre.update()


    # Bouton de Test
    self.bouton_cliquer2 = Button(self, text="Test Démarrer",
                                  command = self.Démarrer
                                  )
    self.bouton_cliquer2.pack()

    self.bouton_cliquer3 = Button(self, text="Test MaApp",
                                  command = self.MaApp
                                  )
    self.bouton_cliquer3.pack()

    self.bouton_cliquer4 = Button(self, text="Test Timer",
                                  command = self.timer
                                  )
    self.bouton_cliquer4.pack()
if __name__ == '__main__':
    fenetre = Tk()
    interface = Interface(fenetre)
    interface.mainloop()
    interface.destroy()

推荐答案

您的代码的第一个问题是,尽管tkinter是线程安全的,但触摸除主线程之外的任何tkinter小部件都是非法的.这意味着试图创建和修改并单击这些小部件的线程是非法的.尝试时实际发生的情况取决于您的平台,Python和Tcl/Tk的版本以及它们的配置方式等,但这从来都不是一件好事,因为它可能会挂起GUI线程,使程序崩溃,并给您带来垃圾字符串或者,最糟糕的是,大约95%的时间都可以工作,但偶尔会神秘地做一些无法调试的错误.

The first problem with your code is that, while tkinter is thread-safe, it's illegal to touch any tkinter widgets from anything but the main thread. Which means your threads, which are trying to create and modify and click those widgets, are illegal. What actually happens when you try depends on your platform, your versions of Python and Tcl/Tk and how they're configured, and so on, but it's never good—it may hang the GUI thread, crash the program, give you garbage strings or, worst of all, work about 95% of the time but occasionally mysteriously do something wrong that's impossible to debug.

围绕这的方法有很多,如这是Effbot Tkinter书的辅助内容,其中包括一个名为mtTkinter的库,该库可以为您神奇地打包所有内容,但自从Python 2.4左右以来就没有更新(尽管有如果要捡起一个3.x叉子,然后将其锤成形状.

There are ways around that, as discussed in this adjunct to the Effbot Tkinter book, including a library called mtTkinter that magically wraps everything up for you except that it hasn't been updated since Python 2.4 or so (although there are unmaintained 3.x forks if you want to pick one up and hammer it into shape.

代码的第二个问题是主run函数会阻塞主线程,直到两个线程都完成为止.如果要阻止主线程,则该线程不会运行主事件循环,这意味着您的应用程序没有响应.您所做的任何GUI更改都不会显示.任何鼠标单击和拖动都不会得到处理.最终,操作系统举起了沙滩球或沙漏之类的东西.

The second problem with your code is that your main run function blocks the main thread until both threads are done. If you're blocking the main thread, it isn't running your main event loop, which means your app is not responsive. Any GUI changes you make don't show up. Any mouse clicks and drags don't get handled. Eventually, the OS puts up a beachball or an hourglass or whatever.

但是您的代码实际上并不需要一开始就使用线程.

But your code doesn't really need threads in the first place.

让我们看看您的timer函数:

def timer(self):
    length = 10
    for i in range(1,(length+1)):
        print(i)
        self.Label2["text"] = "Le temps est {}".format(i)
        fenetre.update()
        time.sleep(1)

它需要在后台线程上运行的唯一原因是,它可以循环length秒,一次休眠一秒钟.但是我们可以改为将其转换为length回调,每个回调都计划下一个回调并退出:

The only reason it needs to run on a background thread is so that it can loop for length seconds, sleeping for a second at a time. But we can instead turn it into length callbacks, each of which schedules the next callback and quits:

def timer(self, length=10, i=1):
    print(i)
    self.Label2["text"] = "Le temps est {}".format(i)
    fenetre.update()
    if i < length:
        tkinter.after(timer, length, i+1)

现在,您的run程序不必在后台线程上生成它,它可以直接调用它:

Now, your run program doesn't have to spawn it on a background thread, it can just call it directly:

self.timer()

您的其他功能甚至更简单:

Your other function is even simpler:

def Démarrer(self):
    self.bouton_cliquer["text"]= "Clic Gauche"
    self.bouton_cliquer["command"] = self.MaApp
    fenetre.update()

它根本不需要在线程上;您可以直接按原样运行它.

It doesn't need to be on a thread at all; you can just run it directly as-is.

但是您如何做与join相同的事情?好吧,您还没有告诉我们您要呼叫run的位置,所以我不知道join之后应该发生什么.但是无论发生什么事情,只需将其拉到一个单独的函数中,您就可以安排它在两个使用after的反向循环完成之后运行.

But how do you do the equivalent of that join? Well, you haven't shown us where you're calling run, so I don't know what's supposed to happen after the join. But whatever it is that's supposed to happen there, just pull it out into a separate function, and you can schedule that to run after the two after-using inverted loops are done.

在此示例中,由于一个函数几乎立即返回,而另一个函数将自身重新安排为length秒,因此我们可以仅在timer之后调用它.所以:

In this example, since one function returns almost instantly, while the other reschedules itself for length seconds, we can probably just call that after the timer. So:

def run(self):
    self.timer()
    self.Démarrer()

def timer(self, length=10, i=1):
    print(i)
    self.Label2["text"] = "Le temps est {}".format(i)
    fenetre.update()
    if i < length:
        tkinter.after(timer, length, i+1)
    else:
        self.do_whatever_you_wanted_after_the_join()

这篇关于线程/Tkinter Python的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆