0

Oke i got the problem days ago and someone helped me with treading but my code was really ugly (im totaly new to coding) now i try to make it better and on an smarter way but now my gui get a frezze all time. i tryed to do it on the way like my last code but it dosent work this time. What have i to do this time i cant understand it but want understand it. some Helpful Tips and tricks ? Or better ways to do it smart, faster, and more powerfull, or mybae the gui more Beautyfule ?

import time
import sys
from tkinter import *
import threading


root = Tk()

root.geometry("600x400")


global start
start = 1


def startUp():
    user_input()
    thr = threading.Thread(target=user_input)
    thr.start()



def user_input():
    global nameInput
    global start
    nameInput = textBox.get("1.0","end-1c")
    start = 0
    if start < 1:
        while True:
            apex = ApexLegends("APIKey")
            player = apex.player(nameInput)
            print("Gesamt Kills: " + player.kills + "\n" + 'Gesamt Damage: ' + player.damage)
            time.sleep(3)
    else:
        print("stop")


anleitung=Label(text="Gib einen Spielernamen ein und drücke Start")
anleitung.pack()

textBox=Text(root, height=1, width=30)
textBox.pack()

startButton=Button(root, height=1, width=10, text="Start", command=lambda:startUp())
startButton.pack()



Trason
  • 3
  • 2
  • 1
    What is `ApexLegends`? Is it a class or a function? When you do `apex = ApexLegends("APIKey")`, does that take a long time (where "long" means "greater than 300ms or so)? – Bryan Oakley Apr 02 '19 at 15:30
  • it is an access to a lib that was made for an easyer way to use the api of the Game Apex Legends – Trason Apr 02 '19 at 15:47
  • I still don't know what that means. Is it a class or a function call? Is it fast or slow? If it's fast, there's no reason to use threading. – Bryan Oakley Apr 02 '19 at 15:49
  • i dont really know that look at this would it help to find it out ? http://prntscr.com/n6hjig – Trason Apr 02 '19 at 15:51

3 Answers3

1

Tkinter isn't designed to be accessed by more than one thread. Here is an excellent answer by one of the guys who has a very deep understainding of how tcl & tk works (the libraries that tkinter depends on), explaining why this is so.

Callback to python function from Tkinter Tcl crashes in windows

This is the first of the two paragraphs in that answer:

Each Tcl interpreter object (i.e., the context that knows how to run a Tcl procedure) can only be safely used from the OS thread that creates it. This is because Tcl doesn't use a global interpreter lock like Python, and instead makes extensive use of thread-specific data to reduce the number of locks required internally. (Well-written Tcl code can take advantage of this to scale up very large on suitable hardware.)

Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
  • Informative link. I was aware that modifying widgets from a different thread was a Bad Idea, but I wasn't sure whether a read-only operation like `Text.get` was dangerous. Danal doesn't seem to make a distinction between mutating and non-mutating, so I guess the prudent approach is to avoid both. – Kevin Apr 02 '19 at 15:44
0
def startUp():
    user_input()
    thr = threading.Thread(target=user_input)
    thr.start()

This doesn't look right. You're calling user_input() in both the main thread and the child thread. If you only want it to run in the child thread, don't call it that first time.

def startUp():
    thr = threading.Thread(target=user_input)
    thr.start()
Kevin
  • 74,910
  • 12
  • 133
  • 166
0

Hi @Trason I've played with your code and I suggest an oo approach.

In the code below I've try to adapt a functional script to your code.

First of all I've use a variable as

self.nameInput = tk.IntVar()

to store the user input on

tk.Entry(w, bg='white', textvariable=self.nameInput).pack()

I've use an Entry widget instead of Text but it should be the same.

Furthermore I use a class to manage thread start and stop operation.

Look, I changed your 'start' variable with 'check' because start is a reserved word

in python thread module.

I tried to recreate the functionality of your code.

Try to import and use your ApexLegends.

import tkinter as tk

import threading
import queue
import datetime
import time

class MyThread(threading.Thread):

    def __init__(self, queue,nameInput):
        threading.Thread.__init__(self)

        self.queue = queue
        self.nameInput = nameInput
        self.check = True

    def stop(self):
        self.check = False

    def run(self):

        while self.check:
            # apex = ApexLegends("APIKey")
            #player = apex.player(self.nameInput.get())
            x = "Gesamt Kills: " + "player.kills" + "\n" + 'Gesamt Damage: ' + "player.damage"+ "\n"
            s = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            msg = "{} datetime: {} nameInput {}".format(x,s,self.nameInput.get())
            time.sleep(3)
            self.queue.put(msg)


class App(tk.Frame):

    def __init__(self,):

        super().__init__()

        self.master.title("Hello World")
        self.master.protocol("WM_DELETE_WINDOW",self.on_close)

        self.queue = queue.Queue()

        self.my_thread = None
        self.nameInput = tk.IntVar()

        self.init_ui()

    def init_ui(self):

        self.f = tk.Frame()

        w = tk.Frame()

        tk.Label(w, text = "Gib einen Spielernamen ein und drücke Start").pack()
        tk.Entry(w, bg='white', textvariable=self.nameInput).pack()

        w.pack(side=tk.LEFT, fill=tk.BOTH, expand=0)

        w = tk.Frame()

        tk.Button(w, text="Start", command=self.startUp).pack()
        tk.Button(w, text="Stop", command=self.stop_thread).pack()
        tk.Button(w, text="Close", command=self.on_close).pack()

        w.pack(side=tk.RIGHT, fill=tk.BOTH, expand=0)
        self.f.pack(side=tk.LEFT, fill=tk.BOTH, expand=0)

    def startUp(self):

        if (threading.active_count()!=0):

            self.my_thread = MyThread(self.queue,self.nameInput)
            self.my_thread.start()
            self.periodiccall()

    def stop_thread(self):

     if(threading.active_count()!=1):
         self.my_thread.stop()


    def periodiccall(self):

        self.checkqueue()
        if self.my_thread.is_alive():
            self.after(1, self.periodiccall)
        else:
            pass

    def checkqueue(self):
        while self.queue.qsize():
            try:
                ret = self.queue.get(0)
                msg = "%s"%(ret)
                print(msg)
            except queue.Empty:
                pass                    

    def on_close(self):
        if(threading.active_count()!=0):
            if self.my_thread is not None:
                self.my_thread.stop()

        self.master.destroy()

if __name__ == '__main__':
    app = App()
    app.mainloop()
1966bc
  • 1,148
  • 1
  • 6
  • 11