Python Forum
[Tkinter] GUI keeps freezing
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
[Tkinter] GUI keeps freezing
#1
Good morning,

Right now, im totally confused with tkinter, and the GUI freezing problem ...

First of all, I read this post from Yoritz, and tried to work after it.

What should my program do ?

I've 2 buttons, where I can buy and send adventures in a browsergame.
Since the process behind that ( login, checklogin, buyadv , sendadv ) takes time, I'm working with a ThreadPoolExecutor, so the gui ( shouldnt ) freeze.

I have several problems / missunderstandings right now.
1. why can't I use the thread_pool_executor.submit function directly in the button command ?
Right now I need a not rly needed method for that ( ?! )

2. The Buttons still lock down, so I added the .after() method.
As it seems I do need a new method for that aswell ? Because I need to call the blocking function with that ?

Well. I do not really know how to place the .after() method.
I havent found any good explainations in com,bination with the thread_pool_executor(), which should avoid the gui freezing.

Here is my code. Messy as it is, since it should only be a test, if the gui freezing issue still appears.
(Yes it does )
( Please dont blame me on the imports, maybe wrong method/var names. As mentioned I just wanted to test how, and if the freeze appears.

To simplify the coide underneath here are the methods which cause troubles:
- buyAdventure()
- sendAdventure() // splitted into sendAdventure() and test() for the .after() method ?!

import time
import tkinter as tk
import requests
from bs4 import BeautifulSoup
from concurrent import futures
import time
import logging
import re

thread_pool_executor = futures.ThreadPoolExecutor(max_workers=3)

# create logger 
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
# create file handler 
file_handler = logging.FileHandler('user/log.log')
file_handler.setLevel(logging.INFO)
# create console handler
stream_handler = logging.StreamHandler()
stream_handler.setLevel(logging.ERROR)
# create formatter and add it to the handlers
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
file_handler.setFormatter(formatter)
stream_handler.setFormatter(formatter)
# add the handlers to the logger
logger.addHandler(file_handler)
logger.addHandler(stream_handler)

class TTWarsBot(tk.Frame):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        
        app.title('TTWarsBot')
        app.geometry('500x400')

        self.session = requests.Session()
        username = 'Fre3k1'
        password = '1324'
        BASE_URL = 'https://vip4.ttwars.com/'

        self.login(username, password, BASE_URL)

        self.userLabel = tk.Label(self, text=username)
        self.userLabel.pack()
        self.passLabel = tk.Label(self, text=password)
        self.passLabel.pack()
        self.gameworldLabel = tk.Label(self, text=BASE_URL)
        self.gameworldLabel.pack()

        # why cant I just use the thread_pool_executer in the command  ?
        self.buyAdventureButton = tk.Button(self, text='buy adventure', command=lambda: self.on_button(1)).pack(pady=15)
        self.sendAdventureButton = tk.Button(self, text='send adventure', command=self.on_button_2).pack(pady=15)
        self.pack()


    def checkLoginStatus(self, gameworld):
        logger.info('CALL checkLoginStatus()')
        html = self.sendRequest(gameworld+"dorf1.php")
        
        try:
            #check GoldAmount
            parsedHtml = BeautifulSoup(html.content, 'html.parser')
            checkGold = parsedHtml.find('span', {'class':'ajaxReplaceableGoldAmount'}).text.strip()
            print(checkGold)
            if checkGold:
                self.loggedIn = True
                logger.info(' checkLoginStatus: LOGGED IN')            
                return True

            else:
                return False
        except:
            self.loggedIn = False
            logger.info(' checkLoginStatus: NOT LOGGED IN')  
            return False

    def login(self, username, password, gameworld):
        #check Login Status
        if self.checkLoginStatus(gameworld):
            logger.info(' STATUS: ALREADY LOGGED IN')

        else:
            print(gameworld)
            logger.info(' RUN login script')
            html =  self.sendRequest('https://vip4.ttwars.com/')
            parsedHtml = BeautifulSoup(html.content, 'html.parser')

            s1 = parsedHtml.find('button', {'name':'s1'})['value']
            login = parsedHtml.find('input', {'name':"login"})['value']
            
            # Login post request
            data = {'user' : username, 'pw' : password, 's1' : s1,'w' : "1920:1080", 'login' : login}
            #html = self.sendRequest(self.config['server'] + 'dorf1.php', data)
            
            html = self.sendRequest((gameworld+'dorf1.php'), data)

    # ??!! why the hell i need to create 2 useless methods for the thread_pool_executor ?
    def on_button(self,amount):
        thread_pool_executor.submit(self.buyAdventure(amount))

    def on_button_2(self):
        thread_pool_executor.submit(self.sendAdventure())

    def buyAdventure(self, amount):
        print('buyADV method CALL')
        counter = 0
                
        advUrl = "https://vip4.ttwars.com/ajax.php?cmd=premiumFeature"
        adventureHtml = self.sendRequest("https://vip4.ttwars.com/hero.php?t=3")
        parsedHtml = BeautifulSoup(adventureHtml.content, 'html.parser')
        ajaxToken = re.search("window.ajaxToken = '(.*)'", str(parsedHtml)).group(1)
            #with open('user/content.txt', 'w', encoding="utf-8") as f:
            #    f.write(adventureHtml.text)
            
        data = {"cmd":"premiumFeature", "featureKey":"buyAdventure", "context":"ExtraModules", "ajaxToken":ajaxToken }

        while counter < amount:  
            counter += 1
            request = self.sendRequest(advUrl, data)

    # here is a blocking code which takes as long as many adventures I have.
    # I call test() with the .after method ?! ( not sure if this is even done correctly. guess not, since the gui keeps freezing )
    def sendAdventure(self):
        self.after(0, self.test)

    def test(self):
        advUrl = "https://vip4.ttwars.com/hero.php?t=3"
        advHtml = self.sendRequest(advUrl)
        parsedHtml = BeautifulSoup(advHtml.content, 'html.parser')

        
        # get adventureId's
        for x in parsedHtml.find_all('a', {'class':'gotoAdventure arrow'}):
            print("total adventure's: xxx")
            # adventures will only be updated, if you refresh the webpage. it wont be update when you send a normal request !
            # print("current available adventures: " + (int(self.checkAdventure())-1)
            href = x['href']         
            kid = href.split("=")[-1]
            # create data for post request
            data = {"skip":"1", "from":"list","kid":kid}
            
            url = "https://vip4.ttwars.com/"+ href
            response = self.sendRequest(url, data)
            
            time.sleep(2.1)

# ================= sendRequest() ===============
    def sendRequest(self, url, data={}):
        headers= {
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:76.0) Gecko/20100101 Firefox/76.0",
            "Connection": "keep-alive"
        }
        try:              
            if len(data) == 0:
                # this is a GET request
                print('GET-REQUEST')
                html = self.session.get(url, headers=headers)
                return html

            else:
                # this is a POST request
                print('POST-REQUEST')
                html = self.session.post(url, headers=headers, data=data)              
                return html

        except:
            logger.warning('Request could not be sent ! check url -> '+url)
            return False
# ================= END of sendRequest() =================

if __name__ == '__main__':
    app = tk.Tk()
    main_frame = TTWarsBot()
    app.mainloop()
Well. Hopefully someone can explain that to me, or give me any hints ?

I would even pay someone , to explain that issue to me, if its to much work, to dig through the code for free !

Thanks.
Reply
#2
Have a look at this
import time
import tkinter as tk
from concurrent import futures
import random

thread_pool_executor = futures.ThreadPoolExecutor(max_workers=1)


class TTWarsBot(tk.Frame):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        app.title('TTWarsBot')
        app.geometry('500x400')

        username = 'Fre3k1'
        password = '1324'
        BASE_URL = 'https://vip4.ttwars.com/'

        self.userLabel = tk.Label(self, text=username)
        self.userLabel.pack()
        self.passLabel = tk.Label(self, text=password)
        self.passLabel.pack()
        self.gameworldLabel = tk.Label(self, text=BASE_URL)
        self.gameworldLabel.pack()

        self.buyAdventureButton = tk.Button(
            self, text='buy adventure', command=lambda: self.on_button(1))
        self.buyAdventureButton.pack(pady=15)
        self.sendAdventureButton = tk.Button(
            self, text='send adventure', command=self.on_button_2)
        self.sendAdventureButton.pack(pady=15)
        self.status_label = tk.Label(self, text='stating')
        self.status_label.pack(pady=15)
        self.pack()

        thread_pool_executor.submit(self.login, username, password, BASE_URL)

    def update_status(self, text):
        self.status_label['text'] = text

    def update_buttons_state(self, enabled=True):
        state = tk.NORMAL
        if not enabled:
            state = tk.DISABLED
        self.buyAdventureButton['state'] = state
        self.sendAdventureButton['state'] = state

    def login(self, username, password, gameworld):
        # code here is in the executer thread, any calls to the gui use after
        self.after(0, self.update_buttons_state, False)
        time.sleep(1)
        self.after(0, self.update_status, 'begining login')
        time.sleep(2)
        logged_in = random.choice((True, False))
        if logged_in:
            self.after(0, self.update_status, 'logged in')
            self.after(0, self.update_buttons_state, True)
        else:
            self.after(0, self.update_status, 'Failed to login')

    def on_button(self, amount):
        thread_pool_executor.submit(self.buyAdventure, amount) # dont use () on the callable

    def on_button_2(self):
        thread_pool_executor.submit(self.sendAdventure)

    def buyAdventure(self, amount):
        # code here is in the executer thread, any calls to the gui use after
        self.after(0, self.update_buttons_state, False)
        self.after(0, self.update_status, f'busy buying adventure {amount}')
        time.sleep(2)
        self.after(0, self.update_status, 'completed buying adventure')
        self.after(0, self.update_buttons_state, True)

    def sendAdventure(self):
        # code here is in the executer thread, any calls to the gui use after
        self.after(0, self.update_buttons_state, False)
        self.after(0, self.update_status, f'busy sending adventure')
        time.sleep(2)
        self.after(0, self.update_status, 'completed sending adventure')
        self.after(0, self.update_buttons_state, True)



if __name__ == '__main__':
    app = tk.Tk()
    main_frame = TTWarsBot()
    app.mainloop()
Reply
#3
(May-17-2020, 03:28 PM)Yoriz Wrote: Have a look at this

Hey !

Really sorry for the late response. ... Really busy right now at work cuz of corona.

Many thanks for that reply ! This helped me A LOT to understand the .after and the thread_pool_executer.

Currently everything is working as it should :)
Also messed arround with stop methodes ( checkboxes ) ( especially during the while loops and so on ).


I'll read through the doc of ThreadPoolExecutor to find some stuff for getting the current queue and stuff like that.

As told, many many thanks for the help !
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  [PyQt] How to open a program with python without freezing LavaCreeperKing 9 8,240 Aug-17-2019, 08:48 PM
Last Post: LavaCreeperKing
  [Tkinter] How to create a delay for AI without freezing the GUI kom2 8 6,183 May-04-2019, 02:32 PM
Last Post: Yoriz
  Gi module window freezing problem loss 0 2,236 May-05-2018, 04:42 PM
Last Post: loss

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020