Почему функция tkinter after () останавливает мое окно? - PullRequest
0 голосов
/ 29 июня 2018

Я создаю копию Dodger, используя tkinter. У меня проблема с движением объекта. Мне сказали, что модуль времени не работает с tkinter, поэтому я должен использовать after(). Однако с функцией after() я сталкиваюсь с той же проблемой, что и с модулем времени. Вот мой код:

from tkinter import *
from random import randint


class Window(Frame):
    def __init__(self, master=None):
        Frame.__init__(self, master)

        self.master = master
        self.initWindow()

    def initWindow(self):
        self.master.title('Dodger')
        self.pack(fill=BOTH, expand=1)
        self.master.geometry('600x800')
        self.master.config(bg='black')

        menu = Menu(self.master)
        self.master.config(menu=menu)

        def clientExit():
            exit()

        file = Menu(menu)
        file.add_command(label='Exit', command=clientExit)
        file.add_command(label='Start', command=self.game)

        menu.add_cascade(label='File', menu=file)

    def game(self):
        canvas = Canvas(self.master, width='600', height='800', borderwidth='0', highlightthickness='0')
        canvas.pack()
        canvas.create_rectangle(0, 0, 600, 800, fill='black', outline='black')

        character = canvas.create_rectangle(270, 730, 330, 760, fill='magenta', outline='cyan', width='2')

        def left(event):
            cord = canvas.coords(character)
            if cord[0] <= 5:
                pass
            else:
                canvas.move(character, -10, 0)

        def right(event):
            cord = canvas.coords(character)
            if cord[2] >= 595:
                pass
            else:
                canvas.move(character, 10, 0)

        self.master.bind('<Left>', left)
        self.master.bind('<Right>', right)

        class variables:
            sizeMin = 10
            sizeMax = 80

            y = 10
            minX = 5
            maxX = 545

        def createShape():
            size = randint(variables.sizeMin, variables.sizeMax)

            x = randint(variables.minX, variables.maxX)
            topLeft = [x, variables.y]
            bottomRight = [x + size, variables.y + size]

            shape = canvas.create_rectangle(topLeft[0], topLeft[1], bottomRight[0], bottomRight[1],
                                            fill='red', outline='red')
            return shape

        def moveShape(shape):
            canvas.move(shape, 0, 800)

        for x in range(5):
            x = createShape()
            self.master.after(1000, moveShape(x))


root = Tk()
app = Window(root)
app.mainloop()

Как видите, в нижней части игрового экземпляра я создал квадрат и сдвинул его пять раз с интервалом в 1 секунду. Однако это не сработало; мое окно просто замерзло на отведенное время, а затем возобновилось. Я не уверен, что это потому, что мой компьютер отстой или я сделал что-то не так. Пожалуйста, запустите мой код в вашем редакторе и объясните мне, если я сделал что-то не так.

Ответы [ 2 ]

0 голосов
/ 29 июня 2018

Причина зависания в том, что вы звоните after неправильно.

Рассмотрим этот код:

self.master.after(1000, moveShape(x))

... он точно такой же, как этот код:

result = moveShape(x)
self.master.after(1000, result)

... что аналогично этому, поскольку moveShape возвращает None:

result  = moveShape(x)
self.master.after(1000, None)

... что так же, как это:

result = moveShape(x)
self.master.after(1000)

... что совпадает с

result = moveShape(x)
time.sleep(1)

Другими словами, вы говорите ему спать, чтобы он спал.

after требует вызываемого или ссылки на функцию. Вы можете передать дополнительные аргументы в качестве аргументов после. Таким образом, правильный способ назвать это так:

self.master.after(1000, moveShape, x)

Хотя я сомневаюсь, что это именно то, что вам нужно, поскольку все пять итераций будут пытаться выполнить код через 1000 мс после запуска цикла, а не через 1000 мсек. Это просто вопрос применения небольшой математики.

0 голосов
/ 29 июня 2018

Вам необходимо заменить sleep функцию и цикла на after. Попробуйте это:

def moveShape():
    if self.step < 5:
        canvas.move(shape, 0, 10)
        self.master.after(1000, moveShape)
        self.step += 1

self.step = 0
shape = createShape()
moveShape()

Кроме того, если вы переместите его на 800 пикселей, вы не увидите его после первого тика, поэтому я уменьшил количество для перемещения до 10 пикселей.

Редактирование: это плюс множество других исправлений и улучшений:

import tkinter as tk
from random import randint

class variables:
    sizeMin = 10
    sizeMax = 80

    y = 10
    minX = 5
    maxX = 545

class Window(tk.Frame):
    def __init__(self, master=None):
        tk.Frame.__init__(self, master)

        self.master = master
        self.initWindow()

    def initWindow(self):
        self.master.title('Dodger')
        self.pack(fill=tk.BOTH, expand=1)
        self.master.geometry('600x800')
        self.master.config(bg='black')

        menu = tk.Menu(self.master)
        self.master.config(menu=menu)

        file = tk.Menu(menu)
        file.add_command(label='Exit', command=self.quit)
        file.add_command(label='Start', command=self.game)

        menu.add_cascade(label='File', menu=file)

    def game(self):
        self.canvas = tk.Canvas(self.master, width='600', height='800', borderwidth='0', highlightthickness='0')
        self.canvas.pack()
        self.canvas.create_rectangle(0, 0, 600, 800, fill='black', outline='black')

        self.character = self.canvas.create_rectangle(270, 730, 330, 760, fill='magenta', outline='cyan', width='2')
        self.createShape()
        self.moveShape() # start the moving

        self.master.bind('<Left>', self.left)
        self.master.bind('<Right>', self.right)

    def left(self, event):
        cord = self.canvas.coords(self.character)
        if cord[0] <= 5:
            pass
        else:
            self.canvas.move(self.character, -10, 0)

    def right(self, event):
        cord = self.canvas.coords(self.character)
        if cord[2] >= 595:
            pass
        else:
            self.canvas.move(self.character, 10, 0)

    def createShape(self):
        size = randint(variables.sizeMin, variables.sizeMax)

        x = randint(variables.minX, variables.maxX)
        topLeft = [x, variables.y]
        bottomRight = [x + size, variables.y + size]

        self.shape = self.canvas.create_rectangle(topLeft[0], topLeft[1], bottomRight[0], bottomRight[1],
                                        fill='red', outline='red')

    def moveShape(self, x=0):
        if x < 5: # loop 5 times
            self.canvas.move(self.shape, 0, 10)
            self.after(1000, self.moveShape, x+1) # run this method again in 1,000 ms

root = tk.Tk()
app = Window(root)
app.mainloop()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...