2017-03-03 31 views
6

Próba wykonać prosty ruch Tkinter:Dlaczego ten kształt w Tkinter aktualizuje się powoli?

import tkinter as tk 

class GameApp(object): 
    """ 
    An object for the game window. 

    Attributes: 
     master: Main window tied to the application 
     canvas: The canvas of this window 
    """ 

    def __init__(self, master): 
     """ 
     Initialize the window and canvas of the game. 
     """ 

     self.master = master 
     self.master.title = "Game" 
     self.master.geometry('{}x{}'.format(500, 500)) 

     self.canvas = tk.Canvas(self.master) 
     self.canvas.pack(side="top", fill="both", expand=True) 

     self.start_game() 

    #----------------------------------------------# 


    def start_game(self): 
     """ 
     Actual loading of the game. 
     """ 

     player = Player(self) 

    #----------------------------------------------# 

#----------------------------------------------# 


class Player(object): 
    """ 
    The player of the game. 

    Attributes: 
     color: color of sprite (string) 
     dimensions: dimensions of the sprite (array) 
     canvas: the canvas of this sprite (object) 
     window: the actual game window object (object) 
     momentum: how fast the object is moving (array) 
    """ 


    def __init__(self, window): 

     self.color = "" 
     self.dimensions = [225, 225, 275, 275] 
     self.window = window 
     self.properties() 

    #----------------------------------------------# 

    def properties(self): 
     """ 
     Establish the properties of the player. 
     """ 

     self.color = "blue" 
     self.momentum = [5, 0] 

     self.draw() 
     self.mom_calc() 

    #----------------------------------------------# 

    def draw(self): 
     """ 
     Draw the sprite. 
     """ 

     self.sprite = self.window.canvas.create_rectangle(*self.dimensions, fill=self.color, outline=self.color) 

    #----------------------------------------------# 


    def mom_calc(self): 
     """ 
     Calculate the actual momentum of the thing 
     """ 

     self.window.canvas.move(self.sprite, *self.momentum) 
     self.window.master.after(2, self.mom_calc) 

    #----------------------------------------------# 

#----------------------------------------------# 


root = tk.Tk() 

game_window = GameApp(root) 

przypadku self.momentum to tablica zawierająca 2 całkowite: jeden dla ruchu X, a drugi dla ruchu y. Rzeczywisty ruch prostokąta jest jednak bardzo powolny (około 5 ruchów na sekundę), a czas self.window.master.after() wydaje się nie mieć żadnego wpływu.

Wcześniej w innym projekcie Tkintera udało mi się uzyskać naprawdę responsywny ruch tkintera, więc zastanawiam się, czy jest sposób, w jaki mogę zminimalizować czas aktualizowania ruchu w tym przypadku, używając innego stylu OOP, lub zupełnie inny kod.

AKTUALIZACJA: Wydaje się, że czas w metodzie .after() ma znaczenie, a w rzeczywistości zależy od czasu rzeczywistego metody. Po użyciu timeit do czasu wywołania metody, mam to wyjście:

>>> print(timeit.timeit("(self.window.master.after(2, self.mom_calc))", number=10000, globals={"self":self})) 
0.5395521819053108 

więc myślę, że prawdziwe pytanie brzmi: dlaczego jest to, że .after() metoda trwa tak długo?

AKTUALIZACJA 2: Testowany na wielu komputerach ruch na każdej platformie jest wciąż wolny.

+0

To wygląda o wiele bardziej niż to konieczne kodu do zilustrowania probem. Przeczytaj i postępuj zgodnie ze wskazówkami tutaj: [Jak utworzyć przykład minimalny, pełny i możliwy do zweryfikowania] (http://stackoverflow.com/help/mcve) lub przenieś pytanie na http://codereview.stackexchange.com/ –

+1

Zminimalizowano to teraz. – Dova

+0

Zapomniałeś o "Kompletnej" części "Minimalna, kompletna i weryfikowalna". –

Odpowiedz

1

Nie widzę problemu zgłaszanego w systemie Windows 10 przy użyciu co najmniej wersji Python 3.6. Testowałem program tak, jak pokazano i musiałem dodać na końcu końcówkę root.mainloop(). Nie pokazano prostokąta, ponieważ obiekt zbyt szybko przesunął się poza obszar roboczy, aby go zobaczyć.

Więc zmodyfikowałem to, aby odbijać się między ścianami i dodałem licznik, aby wydrukować liczbę wywołań mom_calc na sekundę. Po upływie limitu czasu wynoszącym 20ms otrzymuję 50 wywołań ruchu na sekundę, zgodnie z oczekiwaniami. Ustawienie tego na 2ms, tak jak w twoim poście, daje około 425 na sekundę, więc jest tu mały błąd i zajmuje on około 2,3 lub 2,4 ms na połączenie. Jest to trochę zmienna, ponieważ inne procesy mogą zająć trochę czasu w tej granulacji.

Oto kod (nieznacznie) modyfikowany:

import tkinter as tk 

class GameApp(object): 
    """ 
    An object for the game window. 

    Attributes: 
     master: Main window tied to the application 
     canvas: The canvas of this window 
    """ 

    def __init__(self, master): 
     """ 
     Initialize the window and canvas of the game. 
     """ 

     self.master = master 
     self.master.title = "Game" 
     self.master.geometry('{}x{}'.format(500, 500)) 

     self.canvas = tk.Canvas(self.master, background="white") 
     self.canvas.pack(side="top", fill="both", expand=True) 

     self.start_game() 

    #----------------------------------------------# 


    def start_game(self): 
     """ 
     Actual loading of the game. 
     """ 

     player = Player(self) 

    #----------------------------------------------# 

#----------------------------------------------# 


class Player(object): 
    """ 
    The player of the game. 

    Attributes: 
     color: color of sprite (string) 
     dimensions: dimensions of the sprite (array) 
     canvas: the canvas of this sprite (object) 
     window: the actual game window object (object) 
     momentum: how fast the object is moving (array) 
    """ 


    def __init__(self, window): 

     self.color = "" 
     self.dimensions = [225, 225, 275, 275] 
     self.window = window 
     self.movement = 0 
     self.movement_last = 0 
     self.properties() 

    #----------------------------------------------# 

    def properties(self): 
     """ 
     Establish the properties of the player. 
     """ 

     self.color = "blue" 
     self.momentum = [5, 0] 

     self.draw() 
     self.mom_calc() 
     self.velocity() 

    #----------------------------------------------# 

    def draw(self): 
     """ 
     Draw the sprite. 
     """ 

     self.sprite = self.window.canvas.create_rectangle(*self.dimensions, fill=self.color, outline=self.color) 

    #----------------------------------------------# 


    def mom_calc(self): 
     """ 
     Calculate the actual momentum of the thing 
     """ 

     pos = self.window.canvas.coords(self.sprite) 
     if pos[2] > 500: 
      self.momentum = [-5, 0] 
     elif pos[0] < 2: 
      self.momentum = [5, 0] 

     self.window.canvas.move(self.sprite, *self.momentum) 
     self.window.master.after(2, self.mom_calc) 
     self.movement = self.movement + 1 

    def velocity(self): 
     print(self.movement - self.movement_last) 
     self.movement_last = self.movement 
     self.aid_velocity = self.window.master.after(1000, self.velocity) 

    #----------------------------------------------# 

#----------------------------------------------# 


if __name__ == '__main__': 
    root = tk.Tk() 
    game_window = GameApp(root) 
    root.mainloop() 
+0

'root.mainloop () "Poczuj się jak idiota, zapominając o tym, a to wydaje się być kicker. Dzięki. – Dova

3

"Domyślna rozdzielczość timera systemu Windows wynosi ~ 15 ms. Próba uruchomienia timera co 1 ms prawdopodobnie nie będzie działać tak, jak chcesz, a dla gry jest prawdopodobnie zupełnie niepotrzebna (wyświetlacz z aktualizacjami 60FPS tylko co ~ 16 ms Patrz: Why are .NET timers limited to 15 ms resolution? "

Znaleziono rozwiązanie pod adresem Python - tkinter call to after is too slow, gdzie Andrew Medico udzielił dobrej odpowiedzi (w komentarzu).