A computational geometry learning and experimentation tool.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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.