|
|
|
# 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:
|
|
|
|
|
|
|
|
|
|
|
|
```sh
|
|
|
|
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:
|
|
|
|
|
|
|
|
```sh
|
|
|
|
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:
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
|
|
|
# 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:
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
|
|
|
# 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.
|
|
|
|
|
|
|
|
|