Найти - Пользователи
Полная версия: Как предотвратить зависание кнопки tkinter при большой нагрузке?
Начало » Python для новичков » Как предотвратить зависание кнопки tkinter при большой нагрузке?
1 2 3
Oleg_Sizon
Здравствуйте, Уважаемые коллеги!
Написал игру крестики-нолики, графика кнопочная на tkinter. Все работает хорошо, кроме одного места. Дело в том, что первые ходы у меня заложены, дальше, между 2 и 3 ходом, если начинает игру компьютер, производятся вычисления. В этот момент кнопка tkinter, на которую нажимали, делая второй ход, виснет, пока вычисления не будут произведены. Я так понимаю, что это происходит от того, что нажатие-отжатие кнопки занимает около 0.1 секунды, а вычисления начинаются намного раньше, поэтому копал в сторону функций, делающих паузу, sleep, например, потом в сторону блокировок, но пока не помогает. Привожу здесь свой код, приму критику по поводу грязноты и избыточности. Нужное место - между 166 строкой создания кнопки и 167ой начала вычислений, отмечено множественными восклицательными знаками в комментариях. Подскажите, пожалуйста, где искать решение.
from random import choice as random
import sys
from threading import Timer
import time
from tkinter import *
this_game_code=''  
def start():
    global startg, startgfr
    startg=Tk()
    startg.resizable(height=FALSE, width=FALSE)
    startgfr=Frame(startg)
    startgfr2=Frame(startg)
    startg.title("Oleg x_and_zero")
    startg.minsize(100,20)
    ws=startg.winfo_screenwidth()
    hs=startg.winfo_screenheight()
    x=(ws/2)-105
    y=(hs/2)-66
    startg.geometry('%dx%d+%d+%d' %(210,70,x,y))
    Label(startgfr, text="Who will begin? ").grid(row=0, column=1, columnspan=2)
    Button(startgfr, text="Computer", command=lambda:beginner("1")).grid(row=1, column=1)
    Button(startgfr, text="Player", command=lambda:beginner("2")).grid(row=1, column=2)
    startgfr.pack(anchor=CENTER)
    startgfr2.pack(anchor=CENTER)
    startg.mainloop()
    
def result(code):
    number_of_lettercode = 0
    number_of_symbol = 0
    player1 = [None]*5
    player2 = [None]*5
    result = 0
    while number_of_lettercode<len(code):
        player1[number_of_symbol] = code[number_of_lettercode]
        number_of_symbol=number_of_symbol+1
        number_of_lettercode=number_of_lettercode+2
    number_of_lettercode = 1
    number_of_symbol = 0
    while number_of_lettercode<len(code):
        player2[number_of_symbol] = code[number_of_lettercode]
        number_of_symbol=number_of_symbol+1
        number_of_lettercode=number_of_lettercode+2 
    if {'1','4','7'}.difference(player1) == set():
        result = 1
    if {'2','5','8'}.difference(player1) == set():
        result = 1
    if {'3','6','9'}.difference(player1) == set():
        result = 1
    if {'1','2','3'}.difference(player1) == set():
        result = 1
    if {'4','5','6'}.difference(player1) == set():
        result = 1
    if {'7','8','9'}.difference(player1) == set():
        result = 1
    if {'1','5','9'}.difference(player1) == set():
        result = 1
    if {'3','5','7'}.difference(player1) == set():
        result = 1
    if {'1','4','7'}.difference(player2) == set():
        result = -1
    if {'2','5','8'}.difference(player2) == set():
        result = -1
    if {'3','6','9'}.difference(player2) == set():
        result = -1
    if {'1','2','3'}.difference(player2) == set():
        result = -1
    if {'4','5','6'}.difference(player2) == set():
        result = -1
    if {'7','8','9'}.difference(player2) == set():
        result = -1
    if {'1','5','9'}.difference(player2) == set():
        result = -1
    if {'3','5','7'}.difference(player2) == set():
        result = -1
    return(result)
        
def no_same_letter(code):
    count1 = 0
    while count1 < len(code):
        count2 = count1 + 1
        while count2 < len(code):
            if code[count1] == code[count2]:
                return('no')
            count2 = count2 + 1
        count1 = count1 + 1
    return('yes')
def make_tree(code):
    global element, number_of_element_in_spisok
    Spisok_dereva[1] = int(code)
    Slovar_dereva[str(code)] = 0
    while True:
        if Spisok_dereva[element] != None:
            if Slovar_dereva[str(Spisok_dereva[element])] == 0:
                may_be_next_letter = 1
                while may_be_next_letter < 10:
                    if no_same_letter(str(Spisok_dereva[element])+str(may_be_next_letter)) == 'yes':
                        Spisok_dereva[number_of_element_in_spisok] = int(str(Spisok_dereva[element])+str(may_be_next_letter))
                        Slovar_dereva[(str(Spisok_dereva[element])+str(may_be_next_letter))] = result(str(Spisok_dereva[element])+str(may_be_next_letter))
                        number_of_element_in_spisok = number_of_element_in_spisok + 1
                    may_be_next_letter = may_be_next_letter + 1
            element = element + 1
        else:
            break
    while True:
        if Spisok_dereva[-1] == None:
            Spisok_dereva.remove(None)
        else:
            break
    #print(Spisok_dereva[-100:])    
#print('done')
length_of_hod = 9
def player1_choice(length_of_hod):
    global number_of_element_in_spisok
    while len(str(Spisok_dereva[number_of_element_in_spisok-1])) == length_of_hod:
        main_vetka = str(Spisok_dereva[number_of_element_in_spisok-1])[:-1]
        bestresult = Slovar_dereva[str(Spisok_dereva[number_of_element_in_spisok-1])]
        while str(Spisok_dereva[number_of_element_in_spisok-1])[:-1] == main_vetka:
            if Slovar_dereva[str(Spisok_dereva[number_of_element_in_spisok-1])] > bestresult:
                bestresult = Slovar_dereva[str(Spisok_dereva[number_of_element_in_spisok-1])]
            Slovar_dereva[main_vetka] = bestresult
            number_of_element_in_spisok = number_of_element_in_spisok - 1
  
def player2_choice(length_of_hod):
    global number_of_element_in_spisok
    while len(str(Spisok_dereva[number_of_element_in_spisok-1])) == length_of_hod:
        main_vetka = str(Spisok_dereva[number_of_element_in_spisok-1])[:-1]
        bestresult = Slovar_dereva[str(Spisok_dereva[number_of_element_in_spisok-1])]
        while str(Spisok_dereva[number_of_element_in_spisok-1])[:-1] == main_vetka:
            if Slovar_dereva[str(Spisok_dereva[number_of_element_in_spisok-1])] < bestresult:
                bestresult = Slovar_dereva[str(Spisok_dereva[number_of_element_in_spisok-1])]
            Slovar_dereva[main_vetka] = bestresult
            number_of_element_in_spisok = number_of_element_in_spisok - 1
def testfunction(code):
    may_be_next_letter = 1
    
    while True:
        if no_same_letter(str(code)+str(may_be_next_letter)) == 'yes':
                          if Slovar_dereva[str(code)+str(may_be_next_letter)] == Slovar_dereva[str(code)]:
                              code = str(code)+str(may_be_next_letter)
                              break
        may_be_next_letter = may_be_next_letter + 1
    return(code)
#who_first = input('Who will begin? Press 1 for computer or 2 for player   ')
    
def game(player_motion):
    global this_game_code, x, y, computer_first_motion, who_first
    if who_first == '1':  # Computer goes first
        if len(this_game_code)<1:
            computer_first_motion = random('1379')
            this_game_code = computer_first_motion
            Button(rootf, text=computer_sym, height=2, width=5).grid(row=int((int(computer_first_motion)-0.1)/3),column=int((int(computer_first_motion)-0.1)%3))
            root.mainloop()
        elif len(this_game_code) == 1:
            this_game_code = this_game_code+str(player_motion)
            Button(rootf, text=player_sym, height=2, width=5).grid(row=int((int(player_motion)-0.1)/3),column=int((int(player_motion)-0.1)%3))   #!!!!!!!!!!!!!!!!!!
            make_tree(this_game_code)#!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
            player1_choice(9)
            player2_choice(8)
            player1_choice(7)
            player2_choice(6)
            player1_choice(5)
            player2_choice(4)
            player1_choice(3)
            player2_choice(2)
            this_game_code = testfunction(this_game_code)
            Button(rootf, text=computer_sym, height=2, width=5).grid(row=int((int(this_game_code[-1])-0.1)/3),column=int((int(this_game_code[-1])-0.1)%3))
        elif len(this_game_code) < 7:
                Button(rootf, text=player_sym, height=2, width=5).grid(row=int((int(player_motion)-0.1)/3),column=int((int(player_motion)-0.1)%3))
                this_game_code = testfunction(str(this_game_code)+str(player_motion))
                Button(rootf, text=computer_sym, height=2, width=5).grid(row=int((int(this_game_code[-1])-0.1)/3),column=int((int(this_game_code[-1])-0.1)%3))
                if result(this_game_code) != 0:
                    root.geometry('%dx%d+%d+%d' %(210,222,x,y))
                    Label(rootf, text="  Computer win! ").grid(row=3, column=0, columnspan=3)
                    Label(rootf, text="   Play again or quit? ").grid(row=4, column=0, columnspan=3)
                    Button(rootf, text="Again", height=2, width=5, command=lambda:again()).grid(row=5,column=0)
                    Button(rootf, text="Quit", height=2, width=5, command=lambda:root.withdraw()).grid(row=5, column=2)
        elif len(this_game_code) == 7:
                Button(rootf, text=player_sym, height=2, width=5).grid(row=int((int(player_motion)-0.1)/3),column=int((int(player_motion)-0.1)%3)) 
                this_game_code = testfunction(str(this_game_code)+str(player_motion))
                Button(rootf, text=computer_sym, height=2, width=5).grid(row=int((int(this_game_code[-1])-0.1)/3),column=int((int(this_game_code[-1])-0.1)%3))
                if result(this_game_code) == 0:
                    Label(rootf, text="  Draw! ").grid(row=3, column=0, columnspan=3)
                else:
                    Label(rootf, text="  Computer win! ").grid(row=3, column=0, columnspan=3)
                root.geometry('%dx%d+%d+%d' %(210,222,x,y))
                Label(rootf, text="   Play again or quit? ").grid(row=4, column=0, columnspan=3)
                Button(rootf, text="Again", height=2, width=5, command=lambda:again()).grid(row=5,column=0)
                Button(rootf, text="Quit", height=2, width=5, command=lambda:root.withdraw()).grid(row=5, column=2)
    else:            # Player goes first
        #player_motion = input('your motion, please  ')
        if len(this_game_code)<2: #Beginning of the game
            if player_motion == '5':
                second_motion = random('1379')
            else:
                second_motion = '5'
            this_game_code = str(player_motion)+str(second_motion)    
            Button(rootf, text=player_sym, height=2, width=5).grid(row=int((int(player_motion)-0.1)/3),column=int((int(player_motion)-0.1)%3))  #print(this_game_code)
            Button(rootf, text=computer_sym, height=2, width=5).grid(row=int((int(second_motion)-0.1)/3),column=int((int(second_motion)-0.1)%3))
        elif len(this_game_code) == 2:
            this_game_code = this_game_code + str(player_motion)
            Button(rootf, text=player_sym, height=2, width=5).grid(row=int((int(player_motion)-0.1)/3),column=int((int(player_motion)-0.1)%3))
            make_tree(this_game_code)
            player1_choice(9)
            player2_choice(8)
            player1_choice(7)
            player2_choice(6)
            player1_choice(5)
            player2_choice(4)
            player1_choice(3)
            player2_choice(2)
            this_game_code = testfunction(this_game_code)
            Button(rootf, text=computer_sym, height=2, width=5).grid(row=int((int(this_game_code[-1])-0.1)/3),column=int((int(this_game_code[-1])-0.1)%3))
        elif len(this_game_code) <8:
            this_game_code = testfunction(str(this_game_code)+str(player_motion))
            Button(rootf, text=player_sym, height=2, width=5).grid(row=int((int(player_motion)-0.1)/3),column=int((int(player_motion)-0.1)%3))
            if result(this_game_code) == 0:
                Button(rootf, text=computer_sym, height=2, width=5).grid(row=int((int(this_game_code[-1])-0.1)/3),column=int((int(this_game_code[-1])-0.1)%3))
            else:
                Button(rootf, text=computer_sym, height=2, width=5).grid(row=int((int(this_game_code[-1])-0.1)/3),column=int((int(this_game_code[-1])-0.1)%3))
                root.geometry('%dx%d+%d+%d' %(210,222,x,y))
                Label(rootf, text="  Computer win! ").grid(row=3, column=0, columnspan=3)
                Label(rootf, text="   Play again or quit? ").grid(row=4, column=0, columnspan=3)
                Button(rootf, text="Again", height=2, width=5, command=lambda:again()).grid(row=5,column=0)
                Button(rootf, text="Quit", height=2, width=5, command=lambda:root.withdraw()).grid(row=5, column=2)
        elif len(this_game_code) == 8:
            this_game_code = this_game_code + str(player_motion)
            Button(rootf, text=player_sym, height=2, width=5).grid(row=int((int(player_motion)-0.1)/3),column=int((int(player_motion)-0.1)%3))
            if result(this_game_code) == 0:
                Label(rootf, text="  Draw! ").grid(row=3, column=0, columnspan=3)
            else:
                Label(rootf, text="  Computer win! ").grid(row=3, column=0, columnspan=3)
            root.geometry('%dx%d+%d+%d' %(210,222,x,y))
            Label(rootf, text="   Play again or quit? ").grid(row=4, column=0, columnspan=3)
            Button(rootf, text="Again", height=2, width=5, command=lambda:again()).grid(row=5,column=0)
            Button(rootf, text="Quit", height=2, width=5, command=lambda:root.withdraw()).grid(row=5, column=2)
def beginner(who):
    global who_first, startg, startgfr
    who_first = who
    startg.withdraw()
    startg=Tk()
    startg.resizable(height=FALSE, width=FALSE)
    startgfr=Frame(startg)
    startgfr2=Frame(startg)
    startg.title("Oleg x_and_zero")
    startg.minsize(100,20)
    ws=startg.winfo_screenwidth()
    hs=startg.winfo_screenheight()
    x=(ws/2)-105
    y=(hs/2)-66
    startg.geometry('%dx%d+%d+%d' %(210,70,x,y))
    Label(startgfr, text="Choose your symbol ").grid(row=0, column=1, columnspan=2)
    Button(startgfr, text="X", command=lambda:choosesym("X")).grid(row=1, column=1)
    Button(startgfr, text="O", command=lambda:choosesym("O")).grid(row=1, column=2)
    startgfr.pack(anchor=CENTER)
    startgfr2.pack(anchor=CENTER)
    startg.mainloop()#
    main()
def choosesym(sym):
    global player_sym, computer_sym
    if sym == 'X':
        player_sym = 'X'
        computer_sym = 'O'
    else:
        player_sym = 'O'
        computer_sym = 'X'
    main()
def main():
    global b1 , b2 , b3 , b4 ,b5 , b6, b7 , b8 , b9, sys , sym , root, rootf, x, y
    global Slovar_dereva, Spisok_dereva, element, number_of_element_in_spisok, player_sym, computer_sym
    Slovar_dereva = {}
    Spisok_dereva = [0]+[None]*9000
    element = 1    
    number_of_element_in_spisok = 2
    startg.withdraw()
    root=Tk()
    root.minsize(120,140)
    root.resizable(height=False, width=False)
    root.title("Oleg x_and_zero")
    rootf=Frame(root)
    ws=root.winfo_screenwidth()
    hs=root.winfo_screenheight()
    x=(ws/2)-105
    y=(hs/2)-66
    root.geometry('%dx%d+%d+%d' %(210,132,x,y))
    b1=Button(rootf, text="", height=2, width=5, command=lambda:game('1'))
    b2=Button(rootf, text="", height=2, width=5, command=lambda:game('2'))
    b3=Button(rootf, text="", height=2, width=5, command=lambda:game('3'))
    b4=Button(rootf, text="", height=2, width=5, command=lambda:game('4'))
    b5=Button(rootf, text="", height=2, width=5, command=lambda:game('5'))
    b6=Button(rootf, text="", height=2, width=5, command=lambda:game('6'))
    b7=Button(rootf, text="", height=2, width=5, command=lambda:game('7'))
    b8=Button(rootf, text="", height=2, width=5, command=lambda:game('8'))
    b9=Button(rootf, text="", height=2, width=5, command=lambda:game('9'))
    b1.grid(row=0,column=0)
    b2.grid(row=0,column=1)
    b3.grid(row=0,column=2)
    b4.grid(row=1,column=0)
    b5.grid(row=1,column=1)
    b6.grid(row=1,column=2)
    b7.grid(row=2,column=0)
    b8.grid(row=2,column=1)
    b9.grid(row=2,column=2)
    rootf.pack(anchor=CENTER)
    if who_first == '1':
        game('')
    root.mainloop()
    
def again():
    global this_game_code, Slovar_dereva, Spisok_dereva, element, number_of_element_in_spisok
    root.withdraw()
    Slovar_dereva = {}
    Spisok_dereva = [0]+[None]*9000
    this_game_code = ''
    element = 1    
    number_of_element_in_spisok = 2
    start()
start()
4kpt_III
Ух. Код, конечно, мрак.

По Вашему вопросу:
Попробуйте использовать инструкцию .after(). Ну а вообще у Вас дискретная последовательная задача. Как я понимаю, следующий ход невозможен, пока не закончится предыдущий. Поэтому ожидание может быть связано с тяжелыми расчетами. Рекомендую простым time сделать профилировку. Ну или полноценно.
Oleg_Sizon
Так тоже пробовал, вместо make_tree(this_game_code) писать rootf.after(5000, make_tree(this_game_code)), в результате то же самое, только зависание нажатой кнопки на 5 секунд дольше, также пробовал time.sleep(), тот же эффект
4kpt_III
Попытался разобраться в Вашем коде… Возможно у Вас несколько операций выполняются в одном цикле. В этом случае и происходит зависание. Необходимо эти операции разделить на две чередующиеся одна за другой.

P.S. Попробуйте вызвать такую ситуацию локально. На маленьком кусочке кода. Тогда точно смогу помочь.
Oleg_Sizon
4kpt_III
Попытался разобраться в Вашем коде… Возможно у Вас несколько операций выполняются в одном цикле.
Разбираться в коде в том виде, в котором он сейчас - занятие дешифровальщика Этого делать не стоит, код приведен только для того, чтоб можно было запустить игру и посмотреть, как она работает, а она работает, только подвисает на несколько секунд в одном месте! Может, не совсем понятно пишу, но, если запустить код, станет понятнее. Несколько операций совершенно точно есть в одном цикле в функции, вычисляющей дерево ходов make_tree(), но это необходимо, и, даже если реализовать в отдельных циклах отдельные операции, вычислительная нагрузка в 5000 комбинаций, с вычислением результата, просчетом оценок ветвей, все равно будет такой же.
Попробуйте вызвать такую ситуацию локально. На маленьком кусочке кода. Тогда точно смогу помочь.
Все очень локально! Если запустить код, выбрать первый ход-компьютер, то зависшая кнопка это 166 строка, а тяжелая нагрузка это 167, отмечена большим количеством восклицательных знаков в комментах Пытаясь решить задачу, между ними я и впихивал time , sleep, блокировки, а также after на 167 строку
4kpt_III
Я запускал Ваш код. Еще раз. Давайте эту ошибку на 15-20 строчках реализуйте. Чтобы приложение было 15-20 строк всего. Без указания строк в Вашем приложении. У Вас там столько всего, что ошибку может вызывать все, что угодно, а не только тот диапазон, который Вы указали.

P.S. Я действительно хочу помочь, но столько времени у меня просто нет
Oleg_Sizon
Ладно, ладно

from tkinter import *
def start():
    global startg, startgfr
    startg=Tk()
    startg.resizable(height=FALSE, width=FALSE)
    startgfr=Frame(startg)
    startgfr2=Frame(startg)
    startg.minsize(100,20)
    ws=startg.winfo_screenwidth()
    hs=startg.winfo_screenheight()
    x=(ws/2)-105
    y=(hs/2)-66
    startg.geometry('%dx%d+%d+%d' %(210,70,x,y))
    Button(startgfr, text="Hard work", command=lambda:hard_work()).grid(row=1, column=1)
    startgfr.pack(anchor=CENTER)
    startgfr2.pack(anchor=CENTER)
    startg.mainloop()
def hard_work():
    global startg, startgfr, x
    Button(startgfr, text="Work Done!", command=lambda:hard_work()).grid(row=1, column=1)
    x = 0
    while x < 10000000:
        x = x+1
start()
Обратите внимание - в цикле одна операция, но грузит нормально!
4kpt_III
Уже лучше. И что нужно поправить?
Oleg_Sizon
На несколько секунд подвисает кнопка “Work Done!”. В то время, как пр нажатии кнопки “Hard work” сначала должна быть создана кнопка “Work Done!”, а потом даваться нагрузка, в реальности, пока x не будет посчитан, кнопка “Work Done!” не создается
4kpt_III
Я уже написал ответ. after(). Хорошо. Держите на примере Вашего кода.

from Tkinter import *
import time
#
def go():
    for x in xrange(10):
        time.sleep(1)
        print "x = {}".format(x)
#
def start():
    global startg, startgfr
    startg=Tk()
    startg.resizable(height=FALSE, width=FALSE)
    startgfr=Frame(startg)
    startgfr2=Frame(startg)
    startg.minsize(100,20)
    ws=startg.winfo_screenwidth()
    hs=startg.winfo_screenheight()
    x=(ws/2)-105
    y=(hs/2)-66
    startg.geometry('%dx%d+%d+%d' %(210,70,x,y))
    Button(startgfr, text="Hard work", command=lambda: hard_work()).grid(row=1, column=1)
    startgfr.pack(anchor=CENTER)
    startgfr2.pack(anchor=CENTER)
    startg.mainloop()
def hard_work():
    global startg, startgfr, x
    Button(startgfr, text="Work Done!", command=lambda:hard_work()).grid(row=1, column=1)
    startg.after(1, go)
#
start()

P.S. По коду:
1. Не используйте инструкцию global. Или передавайте явно или ООП.
2. Не используйте конструкцию from tkinter import*. Никогда!!!
3. Базовое окно все же лучше называть root. Сильно путаете все
4. В программе должен быть только 1 mainloop(). Вариант, когда их нужно несколько пока не для Вас. root оставляйте одним, а меняйте виджеты на нем. Можно менять комплексно, упаковывая их в рамки.
5. Засилие while сильно пугает.
6. Ну и желательно все же разнести логику и отображение.
7. PEP8. Обязательно. До следующего кода.

P.S.S. Ну и тема должна быть не здесь а в разделе GUI.
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB