4.5 KiB
Contributing to ClusterView
PR Requirements
Before a PR is accepted it must pass the following requirements in CircleCI:
- All tests must pass
- The code must be lint free as determined by
flake8
Additionally, any new code included in the PR must be tested fully.
Developer Requirements
You will want to make sure that all developer requirements are installed. To
do this after creating a new virtualenv
environment:
pip3 install -r requirements-dev.txt
This will install all normal requirements as well as testing requirements.
Linting
We use flake8
for linting.
Run python -m flake8
in order to get a lint report.
Running Tests
We use pytest
as our testing framework.
To run tests with test coverage use pytest --cov=clusterview tests/
.
Updating the UI Design File
In order to load the UI and be able to interact with the various parts you will need to convert the UI file to a python file. To do so:
pyuic5 <ui_file>.ui -o <python_file>.py
For example, to be able to use changes you've made to the main UI file:
pyuic5 clusterview.ui clusterview_ui.py
Style
Import Organization
Import organization is a critical part of good software engineering. In large projects considerable time can be wasted looking for a specific import. The imports must be organized as follows:
# System imports first
# Third party imports second
# UNLV/ClusterView modules external to the current module third
# Modules local to the current module last
Additionally, each imported module will be alphabetized by the module name
inside of the import X
(X being the module name) or the from X import Y
(X being the module name).
If you are importing more than one thing from a module, alphabetize those as well
from x import foo, bar, baz
should be
from x import bar, baz, foo
.
Prefer Private Methods
In Python, private methods don't exist in the way that they do in C++, Java, etc.
To declare a method private we must tell the compiler to garble the name. To do this add two underscores to the prefix of the method name:
# This method can be seen by anyone on import.
def hello_world():
print("Hello, world!")
# This method can only be seen by the module/class it is defined
# in.
def __hello_module():
print("Hello, module!")
We only want to expose the absolute bare minimum number of functions to the consumers of our modules.
Overview of Function
In order to understand how things are drawn and maintained there are some classes you need to know.
Modes
Lambas, partially applied functions, and function passing are used throughout the application to not only improve
readability but also improve modularity of the code. You can see modes in clusterview/mode.py
. These modes are used
to put the application in a global state for adding, editing, moving, and deleting things.
In order to improve readability main_window.py
, the main application class module, uses some neat function passing
tricks to determine which modes to use in clusterview/mode_handlers.py
. The mode_handlers.py
module is fairly
complicated but defines basically all of the mode handling code used for each mode in the application.
Point
The Point
class is the base class for all drawn points. It contains important details for calculating location
in space such as it's containing viewport, it's x and y coordinates in the viewport, it's drawn size, it's color,
and a list of optional attributes.
PointSet
A PointSet
is a convenient holder for all points on a screen. It contains methods that simplify adding to, removing,
moving, and deleting points from the canvas. It maintains a constant viewport state, point size, and other useful
attributes so that you do not need to concern yourself with them during use.
PointManager
The PointManager
is a global singleton that is shared across the entire application. You can think of the
PointManager
as the absolute source of truth for the state of points in the application. In addition to maintaining
a global PointSet
, it also maintains the save
and load
configuration features so that they are consistent
each time they are used.
Grouping Points
Each point can be grouped by color, defined in clusterview/colors.py
in the global PointSet
. This way you
do not need to explicitly maintain sets of points representing groups, and can instead call the groups
function
on the PointSet
to return to you a dictionary from color.Color
to Point
representing each group and their
member points.