Python Tkinter – запустить окно перед циклом

Справочная информация

Ниже приведен код, над которым я сейчас работаю, чтобы создать программу, которая по существу печатает слово и просит вас правильно написать слово. Слово может быть напечатано только один раз, так как после его использования оно помещается в словарь usedWords, чтобы его нельзя было использовать повторно. Когда все слова будут израсходованы, цикл while завершается. Все это прекрасно работает, однако теперь я пытаюсь интегрировать это с графическим интерфейсом.

В чем конкретно моя проблема

Проблема в том, что я хотел бы, чтобы окно Tkinter имело метку и обновляло метку каждый раз, когда печатается новое слово. Однако в настоящее время я получаю сообщение об ошибке AttributeError: объект «NoneType» не имеет атрибута «_root». Я полагаю, что это происходит, потому что я пытался добавить метку до определения определения главного окна, но я не могут сделать это каким-либо другим способом, если окно определено до цикла, окно не открывается до завершения цикла, что, очевидно, противоречит тому, что я хочу на самом деле сделать.

После всего сказанного – мой вопрос

Это так просто - как мне заставить это работать? Я хочу создать окно перед циклом и обновлять метку в окне каждый раз, когда новое слово выбирается из списка.

import random
import time
from tkinter import *
from tkinter.messagebox import *

words = ['reflection', 'attitude', 'replicate', 'accomodate', 'terrain',
         'arguemental', 'stipulate', 'stimulation', 'latitude', 'marginal',
         'thedubester', 'security', 'documentation', 'ukulele',
         'terminal', 'exaggeration', 'declaration', 'apptitude', 'absence',
         'aggressive', 'acceptable', ' allegiance', 'embarass', 'hierarchy',
         'humorous', 'existence']

usedWords = []

while len(usedWords) < len(words):
    chooseWord = words[random.randrange(len(words))]

    var = StringVar()
    display = Label(textvariable = var)
    display.place(x=1,y=1)

    if chooseWord not in usedWords:


        var.set(chooseWord)
        userSpelt = input("Spelling: ")
        usedWords.append(chooseWord)

        if userSpelt in words:
            print("Correctly spelt!")
        else:
             print("Incorrectly spelt!")



master = Tk()
master.geometry("500x600")
master.title("Minesweeper V 1.0")
master.configure(background="#2c3e50")

person Csarg    schedule 22.09.2016    source источник


Ответы (1)


Я думаю, что одна из проблем, с которыми вы сталкиваетесь, — это попытка создать диалоговое окно из консольного ввода. Я уверен, что есть способ сделать это, но в данный момент я не могу придумать хороший способ.

Еще одна проблема: вы на самом деле не создаете окно (главное в своем сценарии) до тех пор, пока не просмотрите количество слов, которые есть в вашем списке слов.

Tkinter немного отличается от консольных сценариев. Он основан на событиях, поэтому, если вы напишете цикл while в приложении, tkinter будет «приостанавливаться» до тех пор, пока цикл while не завершится.

Вместо этого вы можете захотеть, чтобы цикл выполнялся с помощью tkinter и кода вокруг этого. Вот простая реализация, которую я придумал. Ему все еще нужна работа, но он выполняет свою работу (я предполагаю, что вы используете python 3, потому что вы используете «tkinter» во всех строчных буквах):

import random
from tkinter import *
from tkinter.messagebox import *

words = ['reflection', 'attitude', 'replicate', 'accomodate', 'terrain',
         'arguemental', 'stipulate', 'stimulation', 'latitude', 'marginal',
         'thedubester', 'security', 'documentation', 'ukulele',
         'terminal', 'exaggeration', 'declaration', 'apptitude', 'absence',
         'aggressive', 'acceptable', ' allegiance', 'embarass', 'hierarchy',
         'humorous', 'existence']

usedWords = []

class App(Tk):
    def __init__(self):
        super().__init__()

        self._frame = Frame(self)
        self._frame.pack()

        self._label = Label(self, text="")
        self._label.pack()

        self._entry_var = StringVar()
        self._entry = Entry(self, textvariable=self._entry_var)
        self._entry.pack()

        self._button = Button(
            self,
            text="Check",
            command=self.check_callback
        )
        self._button.pack()

        self.get_new_word()

    def check_callback(self):
        if self._current_word == self._entry.get():
            showinfo("Correct", "Correctly spelled.")
            usedWords.append(self._current_word)
        else:
            showwarning("Incorrect", "Incorrectly spelled.")
            words.append(self._current_word)
        self.get_new_word()

    def get_new_word(self):
        self._current_word = words.pop(random.randrange(len(words)))
        self._label.configure(text=self._current_word)
        self._entry_var.set("")
        self.title(self._current_word)

app = App()
app.mainloop()

Это перемещает часть tkinter в класс, который наследуется от класса Tk. На мой взгляд, это облегчает задачу, когда ваша программа становится более сложной.

person Matt W    schedule 22.09.2016
comment
Спасибо, это идеально. Хорошая отправная точка для того, что я хочу сделать. Спасибо, что нашли время прочитать мое эссе. Один вопрос, я новичок и наивен в классах. Вы случайно не знаете какие-нибудь классы, которые объяснят все функции, связанные с классами, которые вы использовали в этом? Например, super().__init__() - person Csarg; 23.09.2016
comment
Кроме того, есть ли способ использовать функцию привязки, чтобы кнопка RETURN использовалась вместо кнопки self._, которую вы настроили? - person Csarg; 23.09.2016
comment
Для начала просмотрите документацию по Python для классов. Super — это способ вызова методов родительского класса, и он подробнее объясняется здесь. . Кстати, ознакомьтесь с бесплатной электронной книгой Packt по Python. - person Matt W; 23.09.2016
comment
Спасибо - есть идеи, как ответить на мой второй вопрос? (кнопка RETURN для обратного вызова) - person Csarg; 23.09.2016
comment
Да, вы можете использовать клавишу ввода для нажатия кнопки... В этом случае вам нужно добавить привязку к полю ввода self._entry.bind('<Return>', self.check_callback) (в методе init) и добавить массив аргументов к функции обратного вызова def check_callback(self, *args): - person Matt W; 23.09.2016
comment
Объект «tkapp» не имеет атрибута «_entry» - person Csarg; 23.09.2016