GithubHelp home page GithubHelp logo

pyrkviewer's Introduction

SBcoyote: An Extensible Python-Based Reaction Editor and Viewer.

Introduction

SBcoyote, initially called PyRKViewer or Coyote, is a cross-platform visualization tool for drawing reaction networks written with the wxPython framework. It can draw reactants, products, reactions, and compartments, and its features include but are not limited to:

  • Support for floating and boundary species.
  • Reactions can be displayed using Bezier curves and straight lines.
  • Plugin support, with some plugin examples: arrow designer, random network, auto layout, etc.

Note that please make sure to save a copy of your SBML file before importing into SBcoyote.

Citing

If you are using any of the code, please cite the article (https://doi.org/10.1016/j.biosystems.2023.105001).

Installing SBcoyote

  • Install Python 3.8, 3.9 or 3.10 if not already in the system.
  • Go to the command line and type pip install SBcoyote.
  • If wxPython doesn't get installed automatically, please try to install wxPython 4.1.1 or 4.2.0 manually referring to https://wxpython.org/pages/downloads/index.html. Note wxPython 4.1.1 does not work with Python 3.10.
  • To run the application, simply type in the command line SBcoyote.

Documentation

The full documentation can be found at: https://sys-bio.github.io/SBcoyote/

Visualization Example

Here is a visualization example by SBcoyote for the large-scale Escherichia coli core metabolism network (King et al., 2015; Orth et al., 2010).

Installation Options for Developers

Installing with Poetry

  1. If you do not have poetry installed on your computer, follow the quick steps shown here.
  2. Once you have poetry installed, you will download SBcoyote. Click the green button at the top of this page that says “Code” and choose “Download ZIP”, then unzip the folder to your desired directory. Make a note of the directory location as you will need it for the next step.
  3. Open your terminal and navigate to the directory containing SBcoyote.
  4. Once inside the main folder of the application you can install the dependencies. To install the base dependencies simply run poetry install. To install the optional ones as well, run poetry install -E simulation. Note that this step may take a while. To learn more about which set of dependencies is right for you, refer to the Dependencies section below.
  5. Finally, you will run the application with the command poetry run SBcoyote.

After you have completed all of these steps, you will not have to repeat them every time you want to run the application. Once the setup is done you will only need to open the terminal, navigate into the folder that contains your SBcoyote application, and run the command poetry run SBcoyote.

Installing without Poetry

We strongly advise following the steps above as it makes the set-up process much faster and simpler. However, to install SBcoyote without Poetry, here is the process you will follow:

  1. First, download SBcoyote. Click the green button at the top of this page that says “Code” and choose “Download ZIP”, then unzip the folder to your desired directory. Make a note of the directory location as you will need it for the next step.
  2. Open your terminal and navigate to the directory containing SBcoyote.
  3. To install the base set of dependencies, you will run pip install -r requirements.txt. Then if you want to install the optional dependencies as well, run pip install -r requirements-simulation.txt. To learn more about which set of dependencies is right for you, refer to the Dependencies section below.
  4. Finally, you will run the application with the command python -m rkviewer.main. After you have completed all of these steps, you will not have to repeat them every time you want to run the application. Once the setup is done you will only need to open the terminal, navigate into the folder that contains your SBcoyote application, and run the command python -m rkviewer.main.

Running

  • If you have poetry, simply run poetry run SBcoyote.
  • Otherwise, in your virtual environment, run python -m rkviewer.main.
  • Then, check out the documentation.

Development Setup

Dependencies

We are using poetry for dependency management. If you are just looking to build and run, though, you can work solely with pip as well.

There are currently three dependency groups: "base", "development", and "simulation".

  • "base" is the bare minimum requirements to run the application without any plugins.
  • "development" includes the additional requirements for development, such as for documentation and testing.
  • "simulation" includes a large set of dependencies required for running simulation related plugins. (This is in addition to the base requirements).

The dependency groups are specified in pyproject.toml for poetry. There are additionally requirement.txt files generated by poetry, including requirements.txt, requirements-dev.txt, and requirements-simulation.txt. If you do not have poetry, you can opt for those as well. If you are using linux, extra work would need to be done on installing wxPython. Please refer to the "Linux Notes" section below.

Installing Dependencies

poetry is recommended for installing dependencies. Simply poetry install for the base dependencies and poetry install -E simulation to install the optional ones as well.

If you don't have poetry, you can simply run pip install -r <> for any of the aforementioned requirements.txt files.

Running locally

  • If you have poetry, simply poetry run SBcoyote.
  • Otherwise, in your virtual environment, run python -m rkviewer.main.

Development Distributing

  • Use poetry build and poetry publish. Refer to poetry docs for more detail.
  • To re-generate the requirements*.txt, run scripts/gen_requirements.py.

Bundling an Executable with PyInstaller

NOTE: This section is obsolete for now, as we are currently distributing with pip.

  • Always run pyinstaller rkviewer.spec when rkviewer.spec is present.
  • If somehow rkviewer.spec went missing or you want to regenerate the build specs, run pyinstaller -F --windowed --add-data ext/Iodine.dll;. main.py on Windows or pyinstaller -F -- windowed --add-data ext/Iodine.dll:. main.py on Linux/Mac to generate a file named main.spec. Note that if a main.spec file is already present it will be overwritten.

Development for Different Platforms

The python version for development was 3.7.7.

Mac Notes

  • Note that on MacOS, if you wish to use SBcoyote in a virtual environment, use venv instead of virtualenv, due to the latter's issues with wxPython.
  • pyinstaller and wxPython require a python built with enable-framework on. Therefore, one should do env PYTHON_CONFIGURE_OPTS="--enable-framework" pyenv install 3.7.7 and use that Python installation for building.
  • If the text is blurry in the app bundled by pyinstaller, one needs to add an entry in the pyinstaller settings as described here.

Linux Notes

  • To install wxPython on linux, see https://wxpython.org/blog/2017-08-17-builds-for-linux-with-pip/index.html. requirements-dev.txt and requirements.txt assume the user is on Ubuntu 18.04 for readthedocs. If you have a different distro and have trouble using requirements.txt, just install wxPython manually using the previous link.
  • Related to the last note, if readthedocs start having trouble building wxPython, understand that it might be because readthedocs updated its distro from Ubuntu 18.04. Go to requirements-dev.txt and change the line above wxPython to look in the appropriate link.
  • i.e. -f https://extras.wxpython.org/wxPython4/extras/linux/gtk3/ubuntu-18.04/ \n wxPython==4.1.1

Future Development

Testing and Profiling

  • To run all tests, go to project root and run python -m unittest discover.
  • To run a specific test suite, run e.g. python -m unittest test.api.test_node.
  • Or even more specific: python -m unittest test.api.test_node.TestNode.test_add_nodes.
  • To profile the application, run python -m cProfile -o rkviewer.stat main.py.
  • To visualize the profile result, run tuna rkviewer.stat.

Building Local Docs

  • Run sphinx-apidoc -f -o docs/source/rkviewer rkviewer rkviewer/plugin rkviewer/resources to regenerate the full reference doc source code if new files were added to the package rkviewer.
  • Run sphinx-build -b html docs\source docs\build.

Note on Style

Usually snake_case is used for function names. However, to retain some degree of backwards compatibility for wxPython, subclasses of wxPython classes use PascalCase for their methods, e.g. Canvas::RegisterAllChildren.

TODOs

  • ENHANCEMENT: Add support for multiple net IDs. Currently all net IDs are set to 0 by default.

Shapes TODOs

  • Events (NodeModified)

Roadmap for Shape Engine

A shape "engine" allows the user to specify custom composite shapes for nodes and compartments. Composite shapes are constructed out of primitives such as circles, (rounded) rectangles, polygons, etc.

RKViewer provides a default list of (composite) shapes, but the user may also create their own shapes out of primitives. A (composite) shape is formed out of one or many primitives, each scaled, rotated, and translated by certain amounts. User-created shapes will be associated with each model in the exported .json files.

A shape-creation plugin may be created in the future to facilitate the process of designing complex shapes.

Here is the roadmap for the shape engine:

  • Create preliminary list of primitives and a default list of shapes. Allow model loader/saver to reference that list.
  • Modify renderer to be able to render these default shapes.
  • Modify inspector to allow the user to change the properties of the primitives in the shape, such as colors, border thickness, etc.
  • Modify model loader/saver to allow users to create custom shape lists manually.
  • Write shape-creation plugin?

pyrkviewer's People

Contributors

cperena avatar dependabot[bot] avatar evilnose avatar felanbird avatar hsauro avatar ndnng avatar penavon avatar samuels342 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

pyrkviewer's Issues

Add "destructors" for CanvasElements and improve event handler

Currently the event manager (rkviewer.canvas.events) has no garbage collector, meaning bound handlers could not be removed from the list of handlers once added. This is restrictive, since the programmer would not be able to bind handlers inside CanvasElements, which are transient (deleted every controller update).

To achieve this, need to refactor bind_handler() to accept an additional target parameter of type CanvasElement. Supposedly this is the "target" of the event, and the handler should be valid only as long as target lives.

Now Python does not have destructor methods, and the closest thing one has is the context manager. So the solution is to add a close method (or equivalent) to the CanvasElement base class and call that manually in Canvas. The close method should be manually patched by the event manager to trigger the handler to be deleted, when it is called.

Setting and theme core features

Need the following features:

  • Popup window to edit settings and themes. This requires rewriting the settings and theme dict to something that holds type information
  • Allow plugins to edit settings/themes, and add their own setting/theme fields. E.g. arrow tip designer may have field "arrowTipDesigner.points" = [...]
  • Automatically save to/load from local setting and theme files. It might be a good idea to store those in a directory (e.g. .rkviewer) along with the logs, for organization.

Might be seeing a slight slowdown in performance

While using the latest version I think I'm seeing a slight slowdown in performance. I noticed a slight delay between clicking the node and the mode moving. Keep an eye on any inefficiencies in the code as you develop it.

Add new visual element to show modifiers

Add new visual element to show modifiers. Eg if a rate laws is kNode1Node0/Node2 and Node2 is not a reactant or product then show a line from C to the reaction with that rate law. See figure.
Allow users to select blunt end, arrow or round empty and filled ends.

image

Add cursor settings to CanvasElement system

The CanvasElement system is used in Canvas to emulate the widget system used in many GUI frameworks. It handles mouse events, draw events, layers, etc.

However it does not currently handle cursors, which are handled in a haphazard way. Add cursor support for CanvasElement, including functions that set the cursor for a specific CanvasElement.

Remember that a CanvasElement could vanish after a deletion or undo event. To deal with that, need to refresh the cursor (as well as mouse-in/out events) in the Reset method

Bug when pasting nodes

  1. Add some nodes

  2. Select a bunch

  3. Copy (Ctrl-C)

  4. Paste (Ctrl.V)

Crash:

PS D:\Documents\Tellurium\GitHubProjects\PyRKViewer> python main.py
Initializing RKViewer...
Traceback (most recent call last):
File "D:\Documents\Tellurium\GitHubProjects\PyRKViewer\rkviewer\view.py", line 393, in
lambda : canvas.Paste(), entries, key=(wx.ACCEL_CTRL, ord('V')))
File "D:\Documents\Tellurium\GitHubProjects\PyRKViewer\rkviewer\canvas\canvas.py", line 1273, in Paste
for id
in pasted_ids})
File "D:\Documents\Tellurium\GitHubProjects\PyRKViewer\rkviewer\canvas\utils.py", line 129, in set_item
self.notify()
File "D:\Documents\Tellurium\GitHubProjects\PyRKViewer\rkviewer\canvas\utils.py", line 107, in notify
observer.update(self._item)
File "D:\Documents\Tellurium\GitHubProjects\PyRKViewer\rkviewer\canvas\canvas.py", line 204, in
selection_obs = Observer(lambda _: self._SelectionChanged())
File "D:\Documents\Tellurium\GitHubProjects\PyRKViewer\rkviewer\canvas\canvas.py", line 1101, in _SelectionChanged
compartment_indices=comp_idx))
File "D:\Documents\Tellurium\GitHubProjects\PyRKViewer\rkviewer\events.py", line 364, in post_event
callback(evt)
File "D:\Documents\Tellurium\GitHubProjects\PyRKViewer\rkviewer\view.py", line 102, in OnSelectionDidUpdate
self.node_form.UpdateSelection(evt.node_indices, comps_selected=should_show_comps)
File "D:\Documents\Tellurium\GitHubProjects\PyRKViewer\rkviewer\forms.py", line 497, in UpdateSelection
self.UpdateAllFields()
File "D:\Documents\Tellurium\GitHubProjects\PyRKViewer\rkviewer\forms.py", line 738, in UpdateAllFields
nodes = get_nodes_by_idx(self._nodes, self._selected_idx)
File "D:\Documents\Tellurium\GitHubProjects\PyRKViewer\rkviewer\canvas\utils.py", line 17, in get_nodes_by_idx
assert len(ret) == len(indices)
AssertionError

Can figure out of an unselect node with green dotted line

I got into a state where I couldn't remove the green dotted line around a node. After I pressed all the buttons I found the hitting products removed it. However, there should be an easier way to unselect this, for example:

  1. User hits escape key and dotted lines disappear
  2. User clicks on an empty canvas area and dotted lines disappear

At the moment it's difficult to get out of this mode unless you know what you're doing.

Add missing form options

The following settings have not been implemented:

  • reaction line thickness
  • node font options

For the node fonts, we also want to decide if we want to use the wxPython built-in fontStyles, which are more compatible, easier to implement, but offers significantly less variety. Another option is to let the user specify the faceName directly.

Centroid needs to be movable

The current centroid should be movable by the user using the mouse. Currently, this is called the centroid but should be renamed to something like arcCenter, reactionCenter etc,

When a reaction is first crated, tits arcCenter should be set to the centroid. Ater that a user can move this point.

Not possible to delete reactions

There currently isn't a way to delete a reaction. Add delete functions to menu and add allow the leyboard delete key to be used to delete a selected (s) reaction.

Missalignment of selected rectange around node

I noticed that the selected rectangle around a node is not aligned correctly, see the image which shows a gap on the lower side but no gap on the upper side.

image

At higher zoom settings, the misalignment goes away, see the next image.

image

Lazy handling of events

Right now event handlers are called as soon as events are posted. This is undesirable for two reasons

  1. Function call stack may get too large and overflow, or there may be infinite loops in the future
  2. The timing may not be well-balanced, since the main loop may spend too much time on a heavily-handled event

Lazy event handling may mitigate these issues. To implement that, when post_event() is called, the event is pushed into a queue. The queue is popped in some time interval (e.g. in OnIdle()) and events are handled then. I'm not sure yet whether the queue should be emptied each time, or perhaps events should stop being popped once a certain time limit has been reached.

This is not urgent since right now the app seems to run smoothly enough. Also, implementing this well may be difficult.

Investigate performance improvements from partial repainting

wxPython offers the function refreshRect to use in place of refresh for updating only a rectangular region of a window, for performance.

This could boost performance in most situations, but to implement this correctly would take a good amount of time (considering how when a node is moved, the reaction(s) that it is in may also improve, increasing the size of the rectangle). It is also hard to say whether the work done finding bounding boxes would outweigh the drawing work saved.

It is also unclear whether we're already benefitting from ScrolledWindow's own optimizations, i.e. not drawing items out of the scrolled area.

Therefore we should investigate the potential gains from this optimizations using profilers and plan out the necessary architecture before starting to implement it. Possibly one may keep track of a list of CanvasElements that were moved, and in Canvas::OnPaint retrieve the bounding rectangles for all of them.

Bezier handles may go out of bounds

Since Bezier handles now move with its associated node, it is possible for them to go out of bounds. Fix this bug by clipping their positions.

Should plugin have access to canvas?

Should a plugin have access to the canvas so that it can add additional visual aspects to the canvas? eg a plugin might draw a bar graph next to a node to indicate the concentration of the node.

Need to store last position and size of main window

When the user positions and sizes the main window, if the program exits it should remember this size and position so that when the user loads the app again they will see the main window with the same size and position.

Need to have a movable 'centroid'

I just built the following model, however the two reactinos are competely separate uniuni reactions. It only appears to be bibi. This is beause the centroid is the same for each reaction. We will need to allow the user to move the 'centroid'. Obviously the centroid itself can't be moved but so you'll have to have a variable that stores the value where the edges meet. By default this value would be assigned the centroid but a user should be able to move the meeting point using the mouse.

image

Add compartments to the application

Allow users to add and delete compartments. These are just rounded rectangle boxes with stroke and fill colors as well as edge (stroke) thickness. Other properties include an Id and a volume (double value, default to 1.0). Compartments can be resized. Compartments may overlap or even appear to be inside another compartment, however this is just a visual effect, in the object model, compartment won't be allowed to be inside compartments

Compartments can be moved. Nodes that are moved into a compartment become part of the compartment and vice versa. A compartment can be moved over nodes and nodes become part of the compartment. When I compartment is moved, any nodes and reactions in the compartment will also move with the compartments. Compartments will have their own property sheet.

api.add_* and api.update_* functions are not atomic

For example, if add_node() fails because there is a duplicate ID and the error is caught, a node is still added with partially initialized attributes, since controller.add_node_g() is not atomic.

This is not very urgent since plugins are not likely to need to catch add_node exceptions, but still probably useful to have.

Are reactions really deleted?

Create network with two nodes and one reaction. Select all and delete. Add the two nodes and the reaction again (make sure they have the same Id as before, you'll have to do this from a plugin). and the following error might occur. At least this is what happens from the random network plugin.

image

Need new model in the file menu

We need a new model option in the File menu. In general, the file menu also needs:

New
Load
Reopen.... (or Load Recent)

Save
Save As...

Import (Importing the model from an SBML file)
Export.... (this would export the model in other formats, eg SBML, PNG, and PDF)
Print

Preferences

Exit

Save image (png, svg, and pdf if possible)

Save a network to file as png, svg, or pdf if possible. Allow the user to select the image type.

Don't save the entire canvas but locate the extremities of the current network and save that portion.

Nodes should have a lock flag

Nodes should have a lock flag. If a node is locked it cannot be moved by the user using the mouse.

Update the plugin API to give access to the lock flag, set or get.

Color selelected plugin crash

I noticed that if I try to change the color of a selected reaction using the color selection plugin, rkviewer crashes, see the crash report which I think explains the issue.

2020-09-11 15:38:33,120 root INFO MainProcess Initializing RKViewer...
2020-09-11 15:53:44,376 root ERROR MainProcess Traceback (most recent call last):
File "plugins\color_selected.py", line 58, in color_callback
api.set_reaction_fill(api.cur_net_index(), index, color)
AttributeError: module 'rkplugin.api' has no attribute 'set_reaction_fill'

Plugin API Tests

Need to have unit tests for the plugin API, even though mostly it just invokes iodine functions.

One related issue: we probably want to modify update_node so that it does not allow for duplicate ID. We likely don't want to expose iodine exceptions to the client.

Drawing bug in arrow orientation

Looks like there is a bug in the arrow tracking the bezier curve. Take two nodes with a reaction between them. Move the bezier handle on the arc center and notice that even though the bezier slope changes at the node, the arrow does not respond by following. See the image below, where the bezier handle is to the left or to the right (circled) has changed but the arrow hasn't changed orientation at all. Need to update arrow position when inner bezier handles are changed.

image

Need method to translate a network

It would be nice to have a method that can translate a network by a given amount to another position. This would require all components to be translated, reaction handles and reaction center, nodes, and compartments.

This would allow for example someone to center a network in a given bounding box, eg the viewable canvas.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.