структурирование приложения / повторный импорт модулей с помощью tkinter python3 - PullRequest
0 голосов
/ 27 марта 2020

Я реструктурирую этот вопрос: я чувствовал, что исходные вопросы давно сложены и не особенно полезны для кого-либо еще.

У меня есть основное приложение (запускается как __main__), которое запускается напрямую. У меня есть модуль (simple_module.py), который я могу использовать только sh для импорта. Я понимаю, что могу запустить его автономно, если это необходимо (через if__name__), и включить его в код модулей, только для демонстрации.

Когда пользователь нажимает кнопку «start» в main.py, он должен открыть новый уровень. окно со всеми классами и виджетами из simple_module, которые находятся в одном классе с именем Page. (все, пока главное окно приложения остается открытым.)

Я хочу, чтобы модуль повторно импортировался (или эквивалент) при каждом нажатии кнопки. Когда кнопка «закрыть» модулей или X нажата, я хочу закрыть это окно. Окно main.py остается открытым в течение всего этого, и нажатие кнопки должно повторно открывать это окно модуля, бесконечно, как al oop.

Я добавил, если name == ' main 'просто чтобы подчеркнуть, что я понимаю, что это делает (это обычно происходит во всех моих приложениях main.py) и почему я все еще не могу получить желаемый результат. Насколько я вижу, это ничего не меняет, я сейчас только импортирую класс, но «новый» все еще не распознается. Это та же проблема, что и в предыдущем примере.

У меня есть main.py

import tkinter as tk

# audio module works as expected
import audio_module as am

# I want this window to open and close on command
import simple_module as sm



class GUI(tk.Frame):

    def __init__(self, *args, **kwargs):
        tk.Frame.__init__(self, *args, **kwargs)

        #self.new = tk.Toplevel(self) # auto loads a second, unwanted window

        self.session_counter = 0
        self.start_btn = tk.Button(root, text="start", command=self.start)
        self.start_btn.grid(row=4,column=0,sticky="nsew",pady=30, padx=30, ipady=18)


    def start(self):
        am.spell() # these audio imports work like a charm, every btn press - single functions call OK

        self.session_counter += 1
        print(self.session_counter)

        #import simple_module - if used here, my usual 'illegal' import style (works great, once only,
        # unless in same script as __main__ in which case all re-imports work fine)

        # Import attempts
        #import simple_module as sm
        #page = Page(new) # Page not defined
        #sm.Page() #missing parent arg (new)

        # error: 'new' not defined
        #sm.Page(new)


if __name__ == '__main__':
    print('running as __main__')
    root = tk.Tk()
    #sm.Page = tk.Toplevel(new) # a desperate attempt NO
    #page = sm.Page(tk.TopLevel) NO
    # qualify and USE module here! sm is not required if you use 'from simple_module import Page' !!
    page = sm.Page(root)
    #page.pack(fill='both', expand=True)
    page.grid(row=0,column=0,sticky='nsew')
    main = GUI(root)
    root.mainloop()

Наконец, у нас есть simple_module.py:

import tkinter as tk
import audio_module as am

# this module works exactly as expected IF run directly...

class Page(tk.Frame):

    def __init__(self, parent, *args, **kwargs):
        tk.Frame.__init__(self, parent, *args, **kwargs)
        # super().__init__(*args, **kwargs)

        self.back_btn = tk.Button(parent, text="close", command=self.back)
        self.back_btn.grid(row=4,column=0,sticky="nsew",pady=30, padx=30, ipady=18)


    def back(self):
        am.click()
        # close this page BUT have it ready to re-open IF user re-presses button.
        new.destroy()


if __name__ == "__main__":
    print('running as __main__ directly')
    new = tk.Tk()
    #new = tk.Toplevel() # this loads an unwanted additional blank window. IF run directly.
    page = Page(new)
    # the missing line to self contain module!
    #page.pack(fill='both', expand=True)
    page.grid(row=0,column=0,sticky='nsew')
    new.mainloop()

else:
    print('running as import with __name__ ==',__name__)

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

1 Ответ

0 голосов
/ 27 марта 2020

Проблема, с которой вы сталкиваетесь, заключается в том, что ваш класс Page не предназначен для повторного использования. Это полагается на глобальные переменные и знания о коде, который его вызывает. Для возможности повторного использования класс Page должен быть автономным.

Короче говоря, это означает, что каждый виджет, созданный Page, должен находиться внутри класса Page, а не в root окно. Кроме того, код, который создает экземпляр Page, должен отвечать за вызов pack, place или grid для экземпляра.

Итак, первым шагом является изменение Page, чтобы его можно было повторно использовать. Ключом к этому является требование, чтобы вызывающая сторона передавала родительский виджет. Вы делаете это, но не используете переданное значение.

Page должно выглядеть следующим образом. Обратите внимание, что он явно объявляет parent в качестве первого позиционного аргумента и передает его на tk.Frame.__init__:

class Page(tk.Frame):

    def __init__(self, parent, *args, **kwargs):
        tk.Frame.__init__(self, parent, *args, **kwargs)
        self.back_btn = tk.Button(parent, text="close", command=self.back)
        ...

Обратите внимание, что кнопка «назад» является дочерним элементом parent, а не дочерним элементом некоторая глобальная переменная. Это важный шаг, который вы упускаете, и именно это позволяет Page быть автономным и не тесно связанным с кодом, который его создает.

Как только Page должным образом автономен, это код, который создает экземпляр Page для вызова pack, place или grid для экземпляра.

Например, simple_module.py может иметь этот блок в конце:

if __name__ == "__main__":
    new = tk.Tk()
    page = Page(new)
    page.pack(fill="both", expand=True)
    new.mainloop()

В main.py, так как вы импортируете simple_module в целом, вам необходимо полностью квалифицировать использование Page:

import simple_module as sm
...
root = tk.Tk()
page = sm.Page(root)
page.pack(fill="both", expand=True)
root.mainloop()

В качестве альтернативы, вы можете просто импортировать Page и опустить sm.:

from simple_module import Page
...
page = Page(root)
...

Обратите внимание, как один файл может использовать root и можно использовать new, но ваш код будет работать в любом случае, потому что он не зависит от глобальной переменной. Внутри страницы она всегда будет parent независимо от того, как ее назвал вызывающий абонент.

Кроме того, вам не нужно импортировать simple_module внутри start - вам нужно только импортировать его один раз в начале программы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...