Помощь в понимании иерархического наследования при использовании Python OOP для нескольких файлов и каталогов - PullRequest
1 голос
/ 31 мая 2019

Введение

Я учусь в университете по программированию.Мои курсы сосредоточены на Java, C # и C ++, поэтому у меня есть некоторый опыт работы с ООП.В настоящее время я занят своим кооперативом, и мне нужно создать графический интерфейс с использованием Python и tkinter (которые я изучаю по ходу работы).

Моя проблема связана именно с Python OOP инаследование нескольких файлов в нескольких каталогах, и я просто не могу понять это (прошло 3 дня)

Если вы не хотите просматривать несколько строк сокращенных файлов и примеров кода, вы можете найтиФактический код здесь:

https://github.com/bkleynhans/Landsat-Buoy-Calibration.git

Затем перейдите в конец страницы, чтобы перейти к списку вопросов.

Вы не сможете запуститьСистема калибровки, если у вас не установлен ModTran, который является дорогим лицензионным программным обеспечением.Тем не менее, графический интерфейс должен быть полностью работоспособным, единственное нестандартное требование - tkcalendar.

Что я сделал

Я работал с ссылками ниже, а также со ссылками, включенными воба других члена.

ImportError: невозможно импортировать имя

ImportError: Невозможно импортировать имя X

, а также

https://www.digitalocean.com/community/tutorials/understanding-class-inheritance-in-python-3

https://www.python -course.eu / python3_inheritance.php

https://chrisyeh96.github.io/2017/08/08/definitive-guide-python-imports.html

К сожалению, я не могуКажется, они не связаны с моей проблемой, будь то из-за того, что я недостаточно осведомлен или просто медлительный.

Структура папок

Моя структура папок выглядит следующим образом:

Z:.
│
│   tarca_gui
│
└───gui
    │
    │   __init__.py
    │   tarca_gui.py
    │
    │
    └───forms
        │
        │   input_notebook.py
        │   status_frame.py
        │   settings_frame.py
        │   example_date_picker_supplied.py
        │   settings_notebook.py
        │   input_frame.py
        │   help_menu.py
        │   header_frame.py
        │   progress_bar.py
        │   __init__.py
        │   menu_bar.py
        │   example_date_picker.py
        │   output_frame.py
        │   
        └─ 

Сводка файлов

tarca_gui в корне - это просто bash-скрипт, который переходит в рабочий каталог и выполняет графический интерфейс пользователя

cd ~/Landsat-Buoy-Calibration/gui
python3 tarca_gui.py

Размещение файлов и их кода здесьбудет большой беспорядок, однако я буду вставлять заголовки и структуры.Ссылка GitHub на проект находится в разделе введения выше.

Обязательные нестандартные библиотеки: tkcalendar - https://pypi.org/project/tkcalendar/

tarca_gui.py - запускает программу из терминала linux и создаетинтерфейс с использованием tkinter.Все остальные файлы получены на основе следующей структуры

Базовый класс

tarca_gui.py

from tkinter import *
from tkinter import messagebox
import inspect
import sys
import os
import pdb

class Tarca_Gui:

    def __init__(self, master):

        # Import gui paths
        from forms import progress_bar
        from forms import menu_bar
        from forms import header_frame
        from forms import input_frame
        from forms import output_frame
        from forms import status_frame

        # Create the root Tkinter object
        master.title('CIS Top Of Atmosphere Radiance Calibration System')
        master.geometry('800x600')
        master.resizable(False, False)
        #master.configure(background = '#FFFFFF')

        master.option_add('*tearOff', False)

        # Create the Progressbar window - accessed via master.progressbar_window.progress_bar
        progress_bar.Progress_Bar(master)

        # Create the Menubar - accessed via master.menu_bar
        menu_bar.Menu_Bar(master)

        # Create the Header - accessed via master.header_frame
        header_frame.Header_Frame(master)

        # Create the Input Frame - accessed via master.
        input_frame.Input_Frame(master)

        # Create the Input Frame - accessed via master.
        output_frame.Output_Frame(master)

        # Create the Input Frame - accessed via master.
        status_frame.Status_Frame(master)



# Calculate fully qualified path to location of program execution
def get_module_path():

    filename = inspect.getfile(inspect.currentframe())
    path = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))

    return path, filename


# Set environment variables to locate current execution path
def set_path_variables():

    path, filename = get_module_path()

    # find the Calibration program path
    path_index = path.rfind('/')

    # append gui paths
    sys.path.append(path[:path_index])
    sys.path.append(path)
    sys.path.append(path + "/forms")


def on_closing(root):

    if messagebox.askyesno("Quit", "Do you really wish to quit?"):
        root.destroy()


def main():

    set_path_variables()

    root = Tk()
    root.protocol("WM_DELETE_WINDOW", lambda: on_closing(root))
    tarca_gui = Tarca_Gui(root)
    root.mainloop()


if __name__ == "__main__": main()

progress_bar, menu_bar, header_frame, input_frame, output_frame и рамка состоянияполучены из (extends) targa_gui

Резюме производных классов

progress_bar.py extends tarca_gui.py

from tkinter import *
from tkinter import ttk
import tarca_gui

class Progress_Bar(tarca_gui.Tarca_Gui):
    def __init__(self, master):

menu_bar.py extends tarca_gui.py

from tkinter import *
from tkinter import ttk
import tarca_gui
import help_menu
from gui.forms import settings_frame

class Menu_Bar(tarca_gui.Tarca_Gui):
    def __init__(self, master):

header_frame.py extends tarca_gui.py

from tkinter import *
from tkinter import ttk
from forms import input_frame

class Header_Frame(input_frame.Input_Frame):
    def __init__(self, master):

input_frame.py extends tarca_gui.py

from tkinter import *
from tkinter import ttk
import tarca_gui
from gui.forms import input_notebook

class Input_Frame(tarca_gui.Tarca_Gui):
    def __init__(self, master):

output_frame.py extends tarca_gui.py

from tkinter import *
from tkinter import ttk
import tarca_gui

class Output_Frame(tarca_gui.Tarca_Gui):
    def __init__(self, master):

status_frame.py extends tarca_gui.py

from tkinter import *
from tkinter import ttk
import tarca_gui

class Status_Frame(tarca_gui.Tarca_Gui):
    def __init__(self, master):

Остальные классы: help_menu, settings_frame, settings_notebook, header_frame и input_notebook получены из (расширений) других классов

header_frame.py extendsinput_frame.py

from tkinter import *
from tkinter import ttk
from forms import input_frame.py
import pdb
class Header_Frame(input_frame.Input_Frame):
    def __init__(self, master):

input_notebook.py extends input_frame.py

from tkinter import *
from tkinter import ttk
from tkinter import filedialog
from gui.forms import input_frame
from tkcalendar import Calendar, DateEntry
from datetime import date

class Input_Notebook(input_frame.Input_Frame):
    def __init__(self, master):

help_menu.py extends menu_bar.py

from tkinter import *
from tkinter import ttk
import time
import threading
import menu_bar

class Help_Menu(menu_bar.Menu_Bar):
    def __init__(self, master):

settings_frame.py extends settings_notebook.py

from tkinter import *
from tkinter import ttk
from tkinter import messagebox
from gui.forms import menu_bar
from gui.forms import settings_notebook

class Settings_Frame(menu_bar.Menu_Bar):
    def __init__(self, master):

settings_notebook.py extends settings_frame.py

from tkinter import *
from tkinter import ttk
from tkinter import filedialog
from gui.forms import settings_frame

class Settings_Notebook(settings_frame.Settings_Frame):
    def __init__(self, master):

Проблема и вопрос

Во-первых, PYTHONPATH обновляется при запуске графического интерфейса через функцииВы можете просмотреть в базовом классе.

Во-вторых, я понимаю, что в настоящее время все классы импортируются не одинаково, они были и были изменены в моих попытках найти проблему и будут стандартизированы в ближайшее время.как проблема была решена.

Проблема

Весь графический интерфейс работает нормально, пока я не попытаюсь импортировать Settings_Frame в settings_notebook.py

Получена ошибка

% ./tarca_gui
Traceback (most recent call last):
  File "tarca_gui.py", line 104, in <module>
    if __name__ == "__main__": main()
  File "tarca_gui.py", line 100, in main
    tarca_gui = Tarca_Gui(root)
  File "tarca_gui.py", line 31, in __init__
    from forms import menu_bar
  File "/cis/otherstu/bxk8027/Landsat-Buoy-Calibration/gui/forms/menu_bar.py", line 20, in <module>
    import help_menu
  File "/cis/otherstu/bxk8027/Landsat-Buoy-Calibration/gui/forms/help_menu.py", line 21, in <module>
    import menu_bar
  File "/cis/otherstu/bxk8027/Landsat-Buoy-Calibration/gui/forms/menu_bar.py", line 21, in <module>
    from gui.forms import settings_frame
  File "/cis/otherstu/bxk8027/Landsat-Buoy-Calibration/gui/forms/settings_frame.py", line 21, in <module>
    from gui.forms import settings_notebook
  File "/cis/otherstu/bxk8027/Landsat-Buoy-Calibration/gui/forms/settings_notebook.py", line 25, in <module>
    class Settings_Notebook(settings_frame.Settings_Frame):
AttributeError: module 'gui.forms.settings_frame' has no attribute 'Settings_Frame'

Выводы

Я полагаю, что это связано с циклическим импортом, поскольку settings_frame импортирует settings_notebook и наоборот.

Путаница

Я понимаю, что по возможности следует избегать циклических ссылок, однако, как я сейчас понимаю Python OOP, мне нужно импортировать settings_notebook в settings_frame, чтобы построить его, но в то же время мне нужно импортировать settings_frame в settings_notebook для того, чтобырасширить его (settings_notebook должен быть потомком settings_frame)

Вопрос

Я не знаю, что я делаю неправильно.

  • Это импортили я так структурировал свои классы?
  • Если я неправильно понимаю структуру классов (с импортом), пожалуйста, объясните, что я сделал неправильно, чтобы я мог учиться и больше не повторять ту же ошибку.
  • Буду очень признателен за любые другие предложения, касающиеся контейнера.

В заключение

Если вы сделали это далеко, во-первых, большое спасибо.

Как уже упоминалось, я учусь в университете и изучаю C ++, C #, Java, Python, HTML, CSS, JavaScript, PHP, SQL, Unity иd Нереальный движок в то же время (иногда я путаюсь с понятиями между ними), поэтому любые предложения, касающиеся какой-либо части моего кода, будут с благодарностью.

1 Ответ

0 голосов
/ 02 июня 2019

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

Когда B наследует от A, это не означает, что B совместно использует данные A, это означает, что B является точной копией A с некоторыми улучшениями.По сути, у вас есть два А - оригинал и новый, который имеет некоторые дополнительные функции.

Например, у вас есть класс с именем Tarca_Gui.Это кажется основным классом.У него есть строка состояния, строка меню и так далее.При наследовании от Tarca_Gui этот новый класс также имеет строку состояния, строку меню и т. Д.Два класса не делятся никакими данными.

Вместо наследования необходимо использовать состав .Разница в том, что с наследованием Menu_Bar является Tarca_Gui, но вы действительно хотите, чтобы Tarca_Gui до имели Menu_Bar.

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

Другими словами:

class Tarca_Gui():
    def __init__(self, master):
        ...
        self.menu_bar = Menu_Bar(self)
        self.header_frame.Header_Frame(self)
        self.input_frame = Input_Frame(self)
        self.output_frame = Output_Frame(self)
        self.status_frame = Status_Frame(self)
        ...

class Menu_Bar():
    def __init__(self, tarca_gui):
        self.tarca_gui = tarca_gui
        ...
class Progress_Bar():
    def __init__(self, tarca_gui):
        self.tarca_gui = tarca_gui
        ...
class Header_Frame():
    def __init__(self, tarca_gui):
        self.tarca_gui = tarca_gui
        ...
... and so on ...

Затем, внутри каждого из этих объектов, если им нужно что-то, что управляетсяTarca_Gui, они могут использовать self.tarca_gui для ссылки на него.Например, если вам нужен доступ к виджету master, вы можете использовать self.tarca_gui.master.

Аналогично, ничто не должно наследоваться от Menu_Bar или Input_Frame или Settings_Frame.

Для получения дополнительной информации о наследовании и составе см. Разница между наследованием и составом

...