This guide outlines coding practices for developing Python code in the Animation Workbench project, including adherence to Python naming conventions, formatting styles, type declarations, and logging mechanisms.
- Consistency: Ensure consistent naming conventions, formatting, and structure throughout the codebase.
- Readability: Code should be clear and easy to read, with well-defined logical flows and separation of concerns.
- Robustness: Implement error handling to gracefully manage unexpected situations.
Follow the standard Python naming conventions as defined in PEP 8:
-
Variable and Function Names: Use
snake_case.def create_animation_frame(frame_number: int) -> QImage: ...
-
Class Names: Use
PascalCase.class AnimationController: ...
-
Constants: Use
UPPER_SNAKE_CASE.DEFAULT_FRAME_RATE = 30
-
Private Variables and Methods: Use a leading underscore.
def _calculate_frame_duration(self, total_frames: int) -> float: ...
Follow the standard conventions for PyQt widgets and properties, even when they do not adhere to typical Python naming conventions:
- Signals and Slots: Use
camelCase. - PyQt Widget Properties and Methods: Use the default
camelCaseas provided by PyQt.
Example:
self.frame_slider.valueChanged.connect(self.on_frame_changed)- Use Black: All Python code should be formatted with black, the opinionated code formatter.
- Configuration: Use a line length of 120 characters as configured in
pyproject.toml.
To format code with Black:
black .- Use 4 spaces per indentation level.
- Leave 1 blank line between functions and class definitions.
- Leave 2 blank lines before class definitions.
- Always declare types for variables, parameters, and return values to enhance code clarity and type safety.
Examples:
def render_frame(frame_number: int, output_path: str) -> bool:
frame_image: QImage = self.generate_frame(frame_number)
...layer: QgsVectorLayer = self.layer_combo.currentLayer()- Use type hints for all function parameters and return values.
- If a function does not return any value, use
-> None.
Example:
def start_animation(self) -> None:
...- Import types from
typingwhere necessary:Optional: To indicate optional parameters.List,Dict,Tuple: For more complex types.
Example:
from typing import List, Optional
def export_frames(frames: List[QImage], output_dir: str) -> None:
...- Do not use
print()statements for debugging or outputting messages. - Use
QgsMessageLogfor all logging to ensure messages are appropriately directed to QGIS's logging system.
- Tag all messages with
'AnimationWorkbench'to allow filtering in the QGIS log. - Use appropriate log levels:
Qgis.Info: For informational messages.Qgis.Warning: For warnings that do not interrupt the workflow.Qgis.Critical: For errors that need immediate attention.
Examples:
QgsMessageLog.logMessage("Animation started.", tag="AnimationWorkbench", level=Qgis.Info)
QgsMessageLog.logMessage("Warning: Frame skipped.", tag="AnimationWorkbench", level=Qgis.Warning)
QgsMessageLog.logMessage("Error rendering frame.", tag="AnimationWorkbench", level=Qgis.Critical)- Graceful Error Handling: Always catch exceptions and provide meaningful error messages through
QgsMessageLog. - Use
try/exceptBlocks: Wrap code that may raise exceptions intry/exceptblocks and log the error.
Example:
try:
self.render_animation()
except Exception as e:
QgsMessageLog.logMessage(f"Error rendering animation: {e}", tag="AnimationWorkbench", level=Qgis.Critical)- Follow the conventions required by PyQt5, using
camelCasewhere necessary for properties and methods. - Use descriptive variable names for widgets:
frame_sliderforQSlideroutput_path_editforQLineEditrender_buttonforQPushButton
- Maintain a consistent naming pattern throughout the user interface code.
- Class Methods Order:
__init__method- Public methods in the order of their usage
- Private (helper) methods prefixed with
_
- Add docstrings to all functions, classes, and modules using the
"""triple quotes"""format. - Include a brief description, parameters, and return values where applicable.
- Use Google-style docstrings.
Example:
def export_animation(self, output_path: str) -> bool:
"""
Exports the animation to a video file.
Args:
output_path: The path where the video file will be saved.
Returns:
True if the export was successful, False otherwise.
"""
...- Use inline comments sparingly and only when necessary to clarify complex logic.
- Use the
#symbol with a space to start the comment.
Example:
# Calculate the interpolated position between keyframes
position = self._interpolate(start_pos, end_pos, t)This project uses pre-commit hooks to ensure code quality. Before committing, run:
./scripts/checks.shOr install the hooks to run automatically:
pre-commit installThis project uses Nix flakes for reproducible development environments. After enabling direnv:
cd /path/to/QGISAnimationWorkbench
direnv allowThis will automatically set up your development environment with all required dependencies.
Use the provided scripts to launch QGIS with the correct profile:
./scripts/start_qgis.sh # Latest QGIS
./scripts/start_qgis_ltr.sh # LTR version
./scripts/start_qgis_master.sh # Development versionFor VSCode development with proper Python paths and extensions:
./scripts/vscode.sh- Naming: Use
snake_case,PascalCase, orcamelCaseas appropriate. - Formatting: Use
blackfor consistent code formatting. - Type Declarations: Declare types for all variables and function signatures.
- Logging: Use
QgsMessageLogwith the tag'AnimationWorkbench'. - Error Handling: Catch and log exceptions appropriately.
- PyQt5: Follow PyQt5's conventions for widget naming and handling.
- Docstrings and Comments: Use meaningful docstrings and comments to explain the code.
- Pre-commit: Run pre-commit hooks before committing code.
Following these guidelines ensures that code within the Animation Workbench project is clear, consistent, and maintainable.