Generally speaking, how are projects (Python) structured?

I lost a little when it came to structuring my projects. I try to structure things in ways that make sense, but always finish restructuring everything at least twice a day. Of course, my projects are not very large, but I would not want to restructure everything and just decide something this time.

I will describe my current program to try to understand things. This is a graphical program with a database database for calculating sail prices. Not everything is written, but the user will be able to choose the category and model of the sail from two drop-down menus. Depending on the model-model combination, the program will present flags and spinbox. These flags and spinbox, when they are changed, extract information from the database and represent the price of the fact that this flag is set or has a certain number (for example, square meters) in the spinbox.

In the current form, the project is as follows:

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 

Further clarification. resources contains all .ui files made in Qt Designer. These are the XML files that describe the GUI. They can be converted to Python scripts using the terminal, which I built in generate_gui.py . The same goes for .qrc files. generate_gui.py places auto- generate_gui.py files in the gui folder with the ui_ prefix or the _rc suffix. database.db is currently empty, but will eventually be used to store prices and all.

MainWindow.py and OptionsWidget.py are Python files containing objects of the same name, minus the .py suffix. MainWindow contains OptionsWidget on the display surface. Both objects use the corresponding ui and rc files.

SailQt.pyw is a file that creates an instance of MainWindow , tells it to show itself, and then tells (Py) Qt to enter its loop and take it from there. It basically looks like a .exe large number of graphical applications, since it is a small file that launches a program.

My initial assumption was to place SailQt.pyw inside the sailqt folder. But then MainWindow.py suddenly needed access to the __version__ line. The only way I could figure out how to do this was to move SailQt.pyw to the root folder of my project and enable MainWindow.py import sailqt.__version__ . But, considering that the first time I had to shuffle things and redo the lines in most files to explain this tiny shuffle, I decided to just ask here.

My questions are clear enough:

  • How, in general, are Python projects structured? This pydoc link was useful, but for me it looks more like a module than what the user actually runs.
  • Did I get the above structuring correctly?
  • Bonus points for the answer, as this is a bit off topic. Why can I do import os and then do things like os.system("sudo rm -rf /") , but I can't do things like import sailqt and then do sailqt.gui.generate_gui.generate() ?
+13
source share
1 answer

Consider your last question first, because it is most important for structuring python projects. Once you figure out how to import imports into your project correctly, the rest becomes much easier to handle.

The main thing is to understand that the directory of the current script run is automatically added to the beginning of sys.path . Therefore, if you place the main.py script (what you are currently calling SailQt.pyw ) outside of your package in the top-level container directory, this ensures that importing packages will always work no matter where the script is executed from.

Thus, the minimum starting structure may look like this:

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

Now, since main.py should be outside the top-level python package directory, it should contain only minimal code (enough to run the program). Given the above structure, this will mean nothing more:

 if __name__ == '__main__': import sys from package import app sys.exit(app.run()) 

The app module will contain most of the actual code needed to initialize the program and configure gui, which will be imported as follows:

 from package.mainwindow import MainWindow 

and the same form of a complete import description can be used from anywhere in the package. So, for example, with this somewhat more complex structure:

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

The search module can import a function from the utils module as follows:

  from package.utils import myfunc 

For the specific issue of accessing the __version__ line: for PyQt, you can put the following at the top of the app module:

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

and then later enter the name / version as follows:

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

Other problems with your current structure are mainly related to maintaining the separation between code files and resource files.

First: the package tree should only contain code files (i.e. python modules). Resource files belong to the project directory (i.e., outside the package). Secondly: files containing code generated from resources (e.g. pyuic or pyrcc) should probably go into a separate subpackage (this also makes the exception control easier for your version control tool). This will lead to the creation of a common project structure as follows:

 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 

Here the Makefile (or equivalent) is responsible for creating ui / rc files, compiling python modules, installing / uninstalling a program, etc. The resources needed by the program at run time (for example, a database file) will need to be installed in a standard location that your program knows how to find (for example, something like /usr/share/progname/database.db on Linux). During the installation of the Makefile also need to generate a bash script executable (or equivalent) that knows where your program is and how to run it. That is, something like:

 #!/bin/sh exec 'python' '/usr/share/progname/main.py' " $@ " 

which obviously should be installed as /usr/bin/progname (or something else).

This may seem like a lot at first, but of course, the main advantage of finding a project structure that works well is that you can reuse it for all future projects (and start developing your own templates and tools for setting up and managing these projects )

+16
source

Source: https://habr.com/ru/post/1238173/


All Articles