Ogólnie rzecz biorąc, jak są zorganizowane projekty (Python)?

Jestem trochę zagubiony, jeśli chodzi o strukturyzację mojego projektu. Staram się uporządkować rzeczy w sposób, który ma sens, ale zawsze kończy się restrukturyzacją całości co najmniej dwa razy dziennie. Przyznaję, że moje projekty nie są zbyt duże, ale chciałbym nie musieć wszystko restrukturyzować i po prostu się na czymś osiedlić.

Opiszę mój obecny program, aby spróbować nadać sens rzeczom. Jest to program graficzny z zapleczem bazy danych do obliczania ceny żagle. Nie wszystko jest jeszcze napisane, ale użytkownik będzie mógł wybrać kategorię żagla i model z dwóch rozwijanych menu. W zależności od kombinacji Kategoria-model, program wyświetli checkboxes i spinboxes. Te pola wyboru i spinboxy, po zmianie, pobierają informacje z bazy danych i przedstawiają cenę za zaznaczenie tego pola lub posiadanie określonej liczby (np. powierzchni w metrach kwadratowych) w spinboxie.

W obecnej formie projekt wygląda następująco to:

COPYING
README.md
SailQt.pyw                    (Should program be called from here ...)
sailqt/
    __init__.py               (This holds a __version__ string)
    SailQt.pyw                (... or here?)
    gui/
        __init__.py
        MainWindow.py         (This needs access to a __version__ string)
        MainWindow_rc.py
        OptionsWidget.py
        ui_MainWindow.py
        ui_OptionsWidget.py
    resources/
        __init__.py
        database.db
        generate_gui.py
        MainWindow.ui
        MainWindow.qrc
        OptionsWidget.ui
        icons/
            logo.png

Do dalszego wyjaśnienia. resources przechowuje wszystkie .ui pliki wykonane w Qt Designer. Są to pliki XML, które opisują GUI. Można je przekonwertować na Skrypty Pythona za pomocą narzędzia terminal, które wbudowałem w generate_gui.py. To samo dotyczy plików .qrc. generate_gui.py umieszcza automatycznie wygenerowane pliki w folderze gui z prefiksem ui_ lub sufiksem _rc. {[9] } jest obecnie pusty, ale w końcu będzie używany do przechowywania cen i wszystkiego.

MainWindow.py i OptionsWidget.py są plikami Pythona, które przechowuj obiekty o tej samej nazwie, bez przyrostka .py. MainWindow trzyma OptionsWidget na swojej powierzchni wyświetlacza. Oba obiekty używają odpowiednich plików ui i rc.

SailQt.pyw jest plikiem, który tworzy instancję MainWindow, mówi mu, aby się pokazał, a następnie mówi (Py)Qt, aby wszedł do jego pętli i przejął stamtąd. Jest to w zasadzie plik .exe z wieloma aplikacjami graficznymi, ponieważ jest to mały plik, który uruchamia program.

Moim wstępnym przypuszczeniem było umieszczenie SailQt.pyw wewnątrz folderu sailqt. Ale wtedy MainWindow.py nagle potrzebował dostępu do __version__ string. Jedynym sposobem, w jaki mogłem dowiedzieć się, jak to osiągnąć, było przeniesienie SailQt.pyw do folderu głównego mojego projektu i pozwolenie MainWindow.py zaimportować sailqt.__version__. Ale biorąc pod uwagę, że to był n-ty raz musiałem przetasować rzeczy i ponowić linie w większości plików, aby uwzględnić to małe przetasowanie, postanowiłem po prostu zapytać tutaj.

Moje pytania są dość jasne:

  • W jaki sposób sÄ… ogólnie zorganizowane projekty Pythona? ten link pydoc byÅ‚ pomocny, ale wydaje mi siÄ™ to bardziej moduÅ‚em niż czymÅ›, co jest faktycznie wykonywane przez użytkownika.
  • czy dobrze zrozumiaÅ‚em powyższÄ… strukturÄ™?
  • punkty bonusowe za odpowiedź na to pytanie, ponieważ jest to trochÄ™ off-topic. Jak to możliwe, że mogÄ™ robić import os, a potem robić takie rzeczy jak os.system("sudo rm -rf /"), ale nie mogÄ™ robić takich rzeczy jak import sailqt, a potem robić sailqt.gui.generate_gui.generate()?
Author: Ruben Bakker, 2014-03-04

1 answers

Najpierw zajmijmy się ostatnim pytaniem, ponieważ jest ono najważniejsze jeśli chodzi o strukturyzację projektów Pythona. Gdy już ustalisz, jak poprawnie zaimportować import w ramach projektu, reszta stanie się znacznie łatwiejsza do rozwiązania.

Kluczową rzeczą do zrozumienia jest to, że katalog aktualnie uruchomionego skryptu jest automatycznie dodawany do start z sys.path. Więc jeśli umieścisz swój main.py skrypt (co aktualnie wywołujesz SailQt.pyw) poza Twojego pakietu w katalogu najwyższego poziomu kontenera, zagwarantuje, że import pakietów będzie zawsze działał, bez względu na to, skąd skrypt jest wykonywany.

Więc minimalna struktura początkowa może wyglądać tak:

project/
    main.py
    package/
        __init__.py
        app.py
        mainwindow.py

Teraz, ponieważ main.py musi znajdować się poza najwyższego poziomu katalogu pakietów Pythona, powinien on zawierać tylko minimalną ilość kodu (wystarczającą do uruchomienia programu). Biorąc pod uwagę powyższą strukturę, oznaczałoby to niewiele więcej niż to:

if __name__ == '__main__':

    import sys
    from package import app
    sys.exit(app.run())

Moduł app zawierałby większość kodu niezbędnego do zainicjowania programu i skonfigurowania gui, który byłby zaimportowany w następujący sposób:

from package.mainwindow import MainWindow

I ta sama forma w pełni kwalifikowanego Oświadczenia importowego może być używana z dowolnego miejsca z pakietem. Tak więc, na przykład, z tą nieco bardziej skomplikowaną strukturą:

project/
    main.py
    package/
        __init__.py
        app.py
        mainwindow.py
        utils.py
        dialogs/
            search.py

Moduł search może zaimportować funkcję z modułu utils w następujący sposób:

 from package.utils import myfunc

W sprawie szczególnej aby uzyskać dostęp do łańcucha __version__: dla programu PyQt, możesz umieścić na górze modułu app następujący tekst:

    QtGui.QApplication.setApplicationName('progname')      
    QtGui.QApplication.setApplicationVersion('0.1')

A następnie uzyskaj dostęp do nazwy / wersji później w następujący sposób:

    name = QtGui.qApp.applicationName()
    version = QtGui.qApp.applicationVersion()

Inne problemy z bieżącą strukturą dotyczą głównie utrzymywania separacji między plikami kodu i plikami zasobów.

Po pierwsze: drzewo pakietów powinno zawierać tylko pliki kodu (np. Moduły Pythona). Pliki zasobów należą do katalogu projektu (tzn. poza pakiet). Po drugie: pliki zawierające kod wygenerowany Z zasobów (np. przez pyuic lub pyrcc) powinny prawdopodobnie trafić do osobnego podpakietu (dzięki temu narzędzie kontroli wersji łatwo je wykluczyć). Wynikałoby to z ogólnej struktury projektu, takiej jak ta:

project/
    db/
        database.db
    designer/
        mainwindow.ui
    icons/
        logo.png
    LICENSE
    Makefile
    resources.qrc
    main.py
    package/
        __init__.py
        app.py
        mainwindow.py
        ui/
            __init__.py
            mainwindow_ui.py
            resources_rc.py

Tutaj Makefile (lub odpowiednik) jest odpowiedzialny za generowanie plików ui/ RC, kompilację modułów Pythona, instalację / odinstalowanie programu itp. Zasoby potrzebne przez program w runtime (taki jak plik bazy danych), będzie musiał być zainstalowany w standardowej lokalizacji, którą program wie, jak znaleźć (np. coś w rodzaju /usr/share/progname/database.db w Linuksie). W czasie instalacji Makefile będzie również musiał wygenerować wykonywalny skrypt bash (lub jego odpowiednik), który wie, gdzie znajduje się twój program i jak go uruchomić. Czyli coś w stylu:

#!/bin/sh

exec 'python' '/usr/share/progname/main.py' "$@"

Który oczywiście musiałby być zainstalowany jako /usr/bin/progname (lub cokolwiek innego).

Na początku może się to wydawać sporym problemem, ale oczywiście główną korzyścią ze znalezienia struktury projektu, która działa dobrze, jest to, że można ją ponownie wykorzystać we wszystkich przyszłych projektach (i zacząć opracowywać własne szablony i narzędzia do konfigurowania i zarządzania tymi projektami).

 34
Author: ekhumoro,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-10 20:56:19