Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

type checking, pyside6 support, updated packages #191

Merged
merged 15 commits into from
May 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions .github/workflows/publish-ryven.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Publish Ryven wheel to TestPyPi and PyPi
name: Publish Ryven wheel to PyPi
on:
create:
tags:
Expand All @@ -18,12 +18,12 @@ jobs:
- name: Build binary wheel and source tarball
run: python -m build --sdist --wheel --outdir dist/
working-directory: ./ryven-editor
- name: Publish distribution to TestPyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
password: ${{ secrets.GH_AC_RYVEN_TEST_PYPI_API_TOKEN }}
repository-url: https://test.pypi.org/legacy/
packages-dir: ryven-editor/dist/
# - name: Publish distribution to TestPyPI
# uses: pypa/gh-action-pypi-publish@release/v1
# with:
# password: ${{ secrets.GH_AC_RYVEN_TEST_PYPI_API_TOKEN }}
# repository-url: https://test.pypi.org/legacy/
# packages-dir: ryven-editor/dist/
- name: Publish distribution to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
Expand Down
14 changes: 7 additions & 7 deletions .github/workflows/publish-ryvencore-qt.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Publish ryvencore-qt wheel to TestPyPi and PyPi
name: Publish ryvencore-qt wheel to PyPi
on:
create:
tags:
Expand All @@ -18,12 +18,12 @@ jobs:
- name: Build binary wheel and source tarball
run: python -m build --sdist --wheel --outdir dist/
working-directory: ./ryvencore-qt
- name: Publish distribution to TestPyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
password: ${{ secrets.GH_AC_RCQT_TEST_PYPI_API_TOKEN }}
repository-url: https://test.pypi.org/legacy/
packages-dir: ryvencore-qt/dist/
# - name: Publish distribution to TestPyPI
# uses: pypa/gh-action-pypi-publish@release/v1
# with:
# password: ${{ secrets.GH_AC_RCQT_TEST_PYPI_API_TOKEN }}
# repository-url: https://test.pypi.org/legacy/
# packages-dir: ryvencore-qt/dist/
- name: Publish distribution to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
Expand Down
39 changes: 39 additions & 0 deletions .github/workflows/type-checking.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: Type-check Ryven and ryvencore-qt using mypy
on:
push:
branches:
- main
- dev
pull_request:
branches:
- '*'
workflow_dispatch:
jobs:
Type-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: 3.10.x
architecture: x64
# if we are not on the main branch, install ryvencore from the dev branch from github
# otherise, install ryvencore from the main branch from github
- if: github.ref != 'refs/heads/main'
run: python -m pip install git+https://github.com/leon-thomm/ryvencore.git@dev
- if: github.ref == 'refs/heads/main'
run: python -m pip install git+https://github.com/leon-thomm/ryvencore.git@main
- name: Install ryvencore-qt dependencies
run: python -m pip install . --user
working-directory: ./ryvencore-qt
- name: Install Ryven dependencies
run: python -m pip install . --user
working-directory: ./ryven-editor
- name: Uninstall ryvencore-qt and ryven (keep dependencies)
run: python -m pip uninstall ryven ryvencore-qt --yes
working-directory: ./ryven-editor
- name: Install type-checking dependencies
run: python -m pip install mypy pyside2 pyside6 PySide6-stubs types-Pygments --user
- name: Typecheck
run: mypy
working-directory: .
54 changes: 25 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,18 +111,21 @@ For more information, visit https://leon-thomm.github.io/ryvencore/
<details>
<summary>quick start into to developing node packages</summary>

A Ryven nodes package is simply a typical Python package which contains at least a `nodes.py` file, and calls the Ryven node API to expose node definitions.

Navigate to `~/.ryven/nodes/` and create a sub-directory of the following structure

```
~/.ryven/nodes
└── your_nodes_pkg_1
├── __init__.py
├── nodes.py
└── gui.py
```

With the following contents:
with the following contents:

`nodes.py`
`nodes.py`:

```python
from ryven.node_env import *
Expand All @@ -132,21 +135,25 @@ from ryven.node_env import *
export_nodes([
# list your node classes here
])


@on_gui_load
def load_gui():
# import gui sources here only
from . import gui
```

`gui.py`
and `gui.py`:

```python
from ryven.gui_env import *

# your node gui definitions go here
from . import nodes

export_guis([
# list your node gui classes here
])
# your node gui definitions go here
```

You can now start defining your own nodes. Let's define two basic nodes. One which generates random numbers
You can now start defining your own nodes. Let's define two basic nodes. One which generates random numbers...

```python
from random import random
Expand All @@ -165,7 +172,7 @@ class RandNode(Node):
)
```

and another one which prints them
...and another one which prints them

```python
class PrintNode(Node):
Expand All @@ -185,22 +192,24 @@ export_nodes([
])
```

That's it! You can import your nodes package in Ryven (`File -> Import Nodes`), place the nodes in the graph, and wire them up. Now add a `val` node and connect it to the `Rand` node, to feed its input with data. If you type a number into the widget of the `val` node and hit enter, it will send the number to the `Rand` node, which will send a scaled random number to the `Print` node, which will print it to the standard output.
That's it! You can import your nodes package in Ryven (`File -> Import Nodes`), place the nodes in the graph, and wire them up. Add a `val` node and connect it to the `Rand` node, to feed its input with data. If you type a number into the widget of the `val` node and hit enter, it will send the number to the `Rand` node, which will send a scaled random number to the `Print` node, which will print it to the standard output.

Notice that the standard output is by default the in-editor console, which you can access at the very bottom of the editor window (drag the blue handle up to make it visible).

### Adding GUI

You can now spice up your nodes with some GUI. Ryven runs on Qt, using the [qtpy](https://github.com/spyder-ide/qtpy) library. You can configure the GUI of your nodes in a separate `gui.py` file, and add custom Qt widgets to your nodes. Make sure to always clearly separate the node logic from the GUI. The `nodes.py` file should NOT have any dependency to Qt. One of the central features of Ryven is to run projects headless (on ryvencore) without any GUI dependencies, if your node packages obey the rules.
You can now spice up your nodes with some GUI. Ryven runs on Qt, using either PySide2 or PySide6 (through the [qtpy](https://github.com/spyder-ide/qtpy) library). You can configure the GUI of your nodes in a separate file, and add custom Qt widgets to your nodes. Make sure to always clearly separate the node logic from the GUI components. One of the central features of Ryven is to run projects headless (on ryvencore) without any GUI dependencies. In order for this to work, your `nodes.py` files should never depend on Qt directly. Instead, you can attach custom GUI to your nodes from the GUI files as shown below.

Let's give them some color and add a slider to the `Rand` node, in `gui.py`:

```python
from ryven.gui_env import *

from qtpy.QtWidgets import QSlider
from qtpy.QtCore import Qt

from ryven.gui_env import *

from . import nodes


class RandSliderWidget(NodeInputWidget, QSlider):
"""a standard Qt slider widget, which updates the node
Expand Down Expand Up @@ -230,6 +239,7 @@ class RandSliderWidget(NodeInputWidget, QSlider):
self.setValue(state['value'])


@node_gui(nodes.RandNode)
class RandNodeGui(NodeGUI):
color = '#fcba03'

Expand All @@ -241,25 +251,11 @@ class RandNodeGui(NodeGUI):
init_input_widgets = {
0: {'name': 'slider', 'pos': 'below'}
}

export_guis([
RandNodeGui,
])
```

and you now just need to reference the `RandNodeGUI` in `nodes.py`:

```python
guis = import_guis(__file__)

class RandNode(Node):
...
GUI = guis.RandNodeGui
```

The value provided by an input widget (through `self.update_node_input(val)`) will be returned in `Node` by `self.input(0)` only when the corresponding input is _not_ connected. Otherwise the value of the connected output will be returned.
and this is it! Ryven will now register `RandNodeGui` as "GUI class" of the `RandNode` class, which serves as a container for all UI things. Your can add custom primary ("main") widgets to your nodes, input widgets, and further customize the look of the nodes.

So now we can reconstruct the previous example, but we don't need to connect the `val` node to the `Rand` node anymore. Change the slider and see how many different random values are printed.
The value provided by an input widget (e.g. `self.update_node_input(val)` above) will be returned in the node, when calling `input()` (e.g. `self.input(0)` in the `RandNode`), but only when the corresponding input is _not connected_. Otherwise, the value of the connected output will be returned.

</details>

Expand Down
30 changes: 30 additions & 0 deletions debug.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#!/usr/bin/env python3

# manually debug ryven; ensure that the following packages are
# not installed in the current environment:
# * ryven
# * ryvencore-qt'
# * ryvencore

RYVEN_PATH = './ryven-editor'
RYVEN_QT_PATH = './ryvencore-qt'
RYVENCORE_PATH = '../ryvencore'

import sys

sys.path.insert(0, RYVEN_PATH)
sys.path.insert(0, RYVEN_QT_PATH)
sys.path.insert(0, RYVENCORE_PATH)

from ryven import run_ryven

if __name__ == '__main__':
run_ryven(
f"{RYVEN_PATH}/ryven/example_projects/matrices.json",
nodes=[
f"{RYVEN_PATH}/ryven/example_nodes/examples",
f"{RYVEN_PATH}/ryven/example_nodes/linalg",
],
qt_api='pyside6',
show_dialog=False,
)
21 changes: 21 additions & 0 deletions mypy.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# mypy configuration, type-checking both the Ryven editor, and the
# ryvencore-qt library. ryvencore must be installed for this to work.
# Simply run `mypy` in the Ryven root directory to check the code.

[mypy]
warn_return_any = True
warn_unused_configs = True
warn_unused_ignores = True
files = ryven-editor/ryven, ryvencore-qt/ryvencore_qt

[mypy-ryven.*]
check_untyped_defs = False

[mypy-ryven.example_nodes.*]
ignore_errors = True

[mypy-ryven.main.packages.built_in.*]
ignore_errors = True

[mypy-ryven.gui.uic.*]
ignore_errors = True
6 changes: 0 additions & 6 deletions ryven-editor/pyproject.toml

This file was deleted.

2 changes: 2 additions & 0 deletions ryven-editor/ryven/example_nodes/examples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
This package contains a bunch of nodes for different purposes, to showcase some of Ryven's features.
Some simple nodes are found in `basic_operators.py`, while more advanced ideas are implemented in `special_nodes.py`.
Original file line number Diff line number Diff line change
@@ -1,22 +1,19 @@
from ryven.node_env import *

guis = import_guis(__file__)


class OperatorNodeBase(Node):
"""
Base class for nodes implementing a binary operation.
"""

version = 'v0.2'
version = 'v0.3'
init_inputs = [
NodeInputType(),
NodeInputType(),
]
init_outputs = [
NodeOutputType(),
]
GUI = guis.OperatorNodeBaseGui

def __init__(self, params):
super().__init__(params)
Expand Down Expand Up @@ -50,7 +47,7 @@ def apply_op(self, elements: list):


class LogicNodeBase(OperatorNodeBase):
GUI = guis.LogicNodeBaseGui
pass


class NOT_Node(LogicNodeBase):
Expand Down Expand Up @@ -123,7 +120,7 @@ def apply_op(self, elements: list):


class ArithmeticNodeBase(OperatorNodeBase):
GUI = guis.ArithNodeBaseGui
pass


class Plus_Node(ArithmeticNodeBase):
Expand Down Expand Up @@ -194,7 +191,6 @@ def apply_op(self, elements: list):


class ComparatorNodeBase(OperatorNodeBase):
GUI = guis.CompNodeBaseGui

def apply_op(self, elements: list):
# if len(elements) > 0:
Expand Down Expand Up @@ -264,9 +260,20 @@ def comp(self, a, b) -> bool:
export
"""


nodes = [
node_types = [
*logic_nodes,
*arithmetic_nodes,
*comparator_nodes,
]

# account for old package name
for n in node_types:
n.legacy_identifiers = [
*getattr(n, 'legacy_identifiers', []),
f'std.{n.__class__.__name__}',
]

export_nodes(
node_types=node_types,
sub_pkg_name='basic_operators'
)
Loading
Loading