GithubHelp home page GithubHelp logo

klauer / qtpynodeeditor Goto Github PK

View Code? Open in Web Editor NEW
171.0 12.0 51.0 8.1 MB

Python Qt NodeEditor (qtpy, PyQt5, PySide)

Home Page: https://klauer.github.io/qtpynodeeditor/

License: Other

Python 100.00%

qtpynodeeditor's Introduction

image

image

qtpynodeeditor

Python Qt node editor

Pure Python port of NodeEditor, supporting PyQt5 and PySide through qtpy.

Requirements

  • Python 3.6+
  • qtpy
  • PyQt5 / PySide

Documentation

Sphinx-generated documentation

Screenshots

Style example

image

Calculator example

image

Installation

We recommend using conda to install qtpynodeeditor.

$ conda create -n my_new_environment -c conda-forge python=3.7 qtpynodeeditor
$ conda activate my_new_environment

qtpynodeeditor may also be installed using pip from PyPI.

$ python -m pip install qtpynodeeditor pyqt5

Running the Tests

Tests must be run with pytest and pytest-qt.

$ pip install -r dev-requirements.txt
$ pytest -v qtpynodeeditor/tests

qtpynodeeditor's People

Contributors

klauer avatar robinechuca avatar tfarago avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

qtpynodeeditor's Issues

Port examples

  • Calculator - could use further cleaning
  • Styles
  • Connection colors
  • Images
  • Example2

error

i got this error while running the calc example

PS E:\me\Better-Dorky> & C:/Users/ahmed/AppData/Local/Programs/Python/Python310-32/python.exe e:/me/Better-Dorky/testingAPI.py      
Traceback (most recent call last):
  File "C:\Users\ahmed\AppData\Local\Programs\Python\Python310-32\lib\site-packages\qtpynodeeditor\connection_graphics_object.py", line 136, in paint
    ConnectionPainter.paint(painter, self._connection, self._style)
  File "C:\Users\ahmed\AppData\Local\Programs\Python\Python310-32\lib\site-packages\qtpynodeeditor\connection_painter.py", line 180, in paint
    draw_normal_line(painter, connection, style)
  File "C:\Users\ahmed\AppData\Local\Programs\Python\Python310-32\lib\site-packages\qtpynodeeditor\connection_painter.py", line 117, in draw_normal_line
    p.setWidth(line_width)
TypeError: setWidth(self, width: int): argument 1 has unexpected type 'float'

Cyclic Connection

The open issue paceholder/nodeeditor#198 actually has a working solution in the comments. I integrated it long time ago and had a problem with that, I don't know why he isn't merging it though.

This porting has the same issue or do implement that fix?

'connection_created' signal called too early when created by mouse action

Expected Behavior

I have connected the FlowScene connection_created signal to a function responsible of complete clear and redrawing the graph sometimes. The problem is that this function is called before the connection is drawn! It is a problem because when I delete all the items from this function, it is impossible for qtpynodeeditor to finish the task.

Current Behavior

I obtain the following traceback

Traceback (most recent call last):
  File "/home/robin/.pyenv/versions/3.11.4/lib/python3.11/site-packages/qtpynodeeditor/connection_graphics_object.py", line 201, in mouseReleaseEvent
    if node and interaction.try_connect():
                ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/robin/.pyenv/versions/3.11.4/lib/python3.11/site-packages/qtpynodeeditor/node_connection_interaction.py", line 175, in try_connect
    self._node.graphics_object.move_connections()
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'move_connections'

Possible Solution

I've had a look at the source code and I think I've pinpointed the problem.

  1. In mouseReleaseEvent method of connection_graphic_object.py, maybe call connection_created at the end of this function ?
  2. In try_connect method of node_connection_interaction.py, at the line self._connection.connect_to(port) the signal is triggered. As explicited in the traceback, the graphics_object attribute of self._node can be indirectly reset because of the signal trigger.
  3. In connect_to method of connection.py, the line self.connection_completed.emit(self) trigger the connection_completed signal. It is not directly the connection_created signal.
  4. In create_connection method of flow_scene.py, the line connection.connection_completed.connect(self.connection_created.emit) has previously associated the 2 signals. Why this two signals are connected?

Maybe the solution could be to remove this signal alias in create_connection method and trigger directly connection_created at the end of try_connect or mouseReleaseEvent ?
I'm not involved in the project, so maybe the solution I'm proposing will break something else ?

Thank you for this beautiful project, and if you guide me, I'm ready to try a pull request on this subject.

NodeDataModel __init__ called twice when adding node in flow_scene

It seems that in flow_view.py (click_handler), the init is called once in self._scene.registry.create(model_name) and another time in self.scene.create_node(type)

Expected Behavior

NodeDataModel init method should be called only once per node when using the GUI for adding a node.
Note: this issue does not occur when the node is added directly in the Python script as it is done in calculator.py.

self._scene.registry.create(model_name) (line 168 of flow_view.py) should check whether a model exists already without having to create an instance.
If the model does exist, icreate an instance of that model thru the call to self.scene.create_node(type) (line 170 from flow_view.py)

Current Behavior

self._scene.registry.create(model_name) creates an instance of the class "model_name" (hence calling init) and call init again in self.scene.create_node(type)
This occurs only when click_handler (line 163 of flow_view.py) is called

Calling init only once ensures that what is done inside the init will be done once.

Possible Solution

If a model has been created already, do not create it again, what could be done by having a way to check whether a model has been created already

Steps to Reproduce (for bugs)

I have added a print('NumberSourceDataModel') in the init of NumberSourceDataModel in calculator.py (provided as an example by qtpynodeeditor).
When I run calculator.py, this message is printed 10 times as expected (5 * 2)
But when I add a NumberSourceDataModel from the screen, I have 2 print for a single instance, what I find not normal.

Context

I have a NodeDataModel for Webcam acquisition using opencv.VideoCapture. This call is very long already but when called twice it is even worst.

Your Environment

python 3, pyqt5, last qtpynodeeditor version installed in July

edges/pins/slots move without staying attached to their connected nodes

when I move a bunch of nodes, the edges sometimes move without staying attached at the other end to a static node.
bad node move

Expected Behavior

the edge/connection would stay attached to the static node that's not moving

Current Behavior

sometimes the edge moves without updating correctly
this results in a bad UX

Possible Solution

move the static node a little, to refresh the edge

Steps to Reproduce (for bugs)

not always reproducable, think it's triggered by some kind of middle mouse click / shift combo

  1. shift + RMB to select a bunch of nodes
  2. middle mouse click + move mouse. (try without releasing previous shift key too)
  3. move a selected node with RMB
  4. notice the edges moving

Context

this is using the calculator example python -m qtpynodeeditor.examples.calculator

Your Environment

windows 11
pycharm
python 3.10.5 [MSC v.1929 64 bit (AMD64)] on win32
Qt 5.15.2
installed qtpynodeeditor from PyPi

`QFontMetrics.horizontalAdvance()` is introduced in Qt-5.11

QFontMetrics.horizontalAdvance() is introduced in Qt-5.11. It's a rather new version, not always available in stable repos. For example in latest Anaconda, just PyQt-5.9 LTS conda package is provided, which is required by spyder4 etc, so pip-installing newer PyQt is not an option. So to use qtpynodeeditor in stable Anaconda this commit would be better reverted.

Originally posted by @NovA80 in #36 (comment)

Fix PySide2 compatibility

  • Travis configuration isn't working for PySide2 just yet
  • QVariant doesn't exist in PySide2 (only used for typing; to be removed)
  • pytest is crashing for me locally when trying to use QT_API=PySide2 PYTEST_QT_API=PySide2, lldb points at QMessageLogger:
qtpynodeeditor/tests/test_basic.py::test_instantiation Process 96282 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGABRT
    frame #0: 0x00007fff741812c6 libsystem_kernel.dylib`__pthread_kill + 10
libsystem_kernel.dylib`__pthread_kill:
->  0x7fff741812c6 <+10>: jae    0x7fff741812d0            ; <+20>
    0x7fff741812c8 <+12>: movq   %rax, %rdi
    0x7fff741812cb <+15>: jmp    0x7fff7417b457            ; cerror_nocancel
    0x7fff741812d0 <+20>: retq
Target 0: (python) stopped.
(lldb) up
frame #1: 0x00007fff7423cbf1 libsystem_pthread.dylib`pthread_kill + 284
libsystem_pthread.dylib`pthread_kill:
->  0x7fff7423cbf1 <+284>: movl   %eax, %r15d
    0x7fff7423cbf4 <+287>: cmpl   $-0x1, %eax
    0x7fff7423cbf7 <+290>: jne    0x7fff7423cc23            ; <+334>
    0x7fff7423cbf9 <+292>: callq  0x7fff7423fafe            ; symbol stub for: __error
(lldb)
frame #2: 0x00007fff740eb6a6 libsystem_c.dylib`abort + 127
libsystem_c.dylib`abort:
->  0x7fff740eb6a6 <+127>: movl   $0x2710, %edi             ; imm = 0x2710
    0x7fff740eb6ab <+132>: callq  0x7fff740be568            ; usleep$NOCANCEL
    0x7fff740eb6b0 <+137>: callq  0x7fff740eb6b5            ; __abort

libsystem_c.dylib`__abort:
    0x7fff740eb6b5 <+0>:   cmpq   $0x0, 0x367ebacb(%rip)    ; gCRAnnotations + 7
(lldb)
frame #3: 0x00000001104325e9 QtCore`___lldb_unnamed_symbol171$$QtCore + 9
QtCore`___lldb_unnamed_symbol171$$QtCore:
->  0x1104325e9 <+9>: nopl   (%rax)

QtCore`___lldb_unnamed_symbol172$$QtCore:
    0x1104325f0 <+0>: pushq  %rbp
    0x1104325f1 <+1>: movq   %rsp, %rbp
    0x1104325f4 <+4>: movq   %rdi, %rax
(lldb)
frame #4: 0x0000000110433d34 QtCore`QMessageLogger::fatal(char const*, ...) const + 202
QtCore`QMessageLogger::fatal:
->  0x110433d34 <+202>: ud2
    0x110433d36 <+204>: jmp    0x110433d38               ; <+206>
    0x110433d38 <+206>: movq   %rax, %rbx
    0x110433d3b <+209>: leaq   -0x18(%rbp), %rdi

Review styles

Styles have been reworked to be slightly less cumbersome than the original C++ version, but it's not perfect:

  • StyleCollection is a container for node, connection, and view styles
    • Can be loaded from JSON files, Python dictionaries, or just user-tweaked Style classes
  • Styles are passed in as follows:
    • Scene takes in a collection; uses the connection and view styles
    • NodeDataModel takes in a collection; uses the node style

Customize node background color

I'd like to customize the node background color, but there is no style setting to do so (or I missed it). Anyone knows how to do it?

NodeDataModel class input_connection_created called twice

Originally posted by @thorphil in #12 (comment):

I came across an issue where my NodeDataModel class input_connection_created was being called twice after a successful connection. Tracing this back, it seems to be caused by a the _FlowSceneModel constructor being called twice when the FlowScene is instantiated, thus registering two signal-slot connections. To fix, I changed the order of parents in FlowScene from (QGraphicsScene,_FlowSceneModel) to (_FlowSceneModel,QGraphicsScene). I am not entirely sure why this fixed the issue.

Alternatives to qtpynodeeditor

First option which made me search for something better:

Alternatives I've found (after spending the time porting nodeeditor, of course!):

Feel free to comment with other libraries here.

Plan

  1. Complete basic port
  2. Write some unit tests (pytest, pyqtest-qt)
  3. Port some additional examples - style, calculator, ...
  4. Make things more Pythonic
    a. Styles are a mess slightly better now
    b. Data model architecture isn't great
    c. Many getters/setters could be easily replaced with properties (Python or qt?) mostly done
  5. Make into a package, add CI, ...

Nodes not drawn with newest pyqt5

After updating pyqt5 from 5.14.2 to 5.15.5 5.15.0, nodes are no longer drawn correctly:

calculator-no-nodes

Console output:

qt.qpa.xcb: QXcbConnection: XCB error: 5 (BadAtom), sequence: 1083, resource id: 0, major code: 20 (GetProperty), minor code: 0
QPainter::begin: Paint device returned engine == 0, type: 2
QPainter::setRenderHint: Painter must be active to set rendering hints
QPainter::setRenderHint: Painter must be active to set rendering hints
QPainter::setWorldTransform: Painter not active
QPainter::setClipRect: Painter not active
QPainter::font: Painter not active
QPainter::setPen: Painter not active
QPainter::setBrush: Painter not active
QPainter::setBrush: Painter not active
QPainter::setBrush: Painter not active
QPainter::setBrush: Painter not active
... (and many more lines like that)

Is something wrong with my installation, or can you reproduce this issue?

Environment

Arch Linux, Python 3.8.4, pyqt5 5.15.5 5.15.0, qtpy 1.9.0, qtpynodeeditor freshly cloned

Refactor how type conversion works

TypeConverter class, TypeConverterId can be awkward and unnecessary boilerplate.

Some preliminary discussion in #43 (comment):

I was thinking that class inheritance information could be used to indicate conversion compatibility, perhaps. At least in relatively simple graphs, it may even be possible to omit data_type = NodeDataType(...) then. That's aiming toward my hopeful overall goal of less boilerplate.

But data types may still be a necessity:

Perhaps, but there might be some very exotic conversions (e.g. an image into a text) and then the inheritance wouldn't help that much.

Using a widget value inside the nodes compute method

I can see from the calculator example how a nodes widget (e.g. the QLineEdit widget in NumberSourceDataModel) can output data 'downstream' in the nodegraph via the data_updated signal and subsequent call to the out_data() method. However, this process does not involve rerunning the nodes compute() method when a widget is updated and forces all node input variables to be additional nodes in the tree.

If I want to have a nodes widget play a part in the computation of the result (e.g. a 'math' node with 2 inputs connections and a QCombo box for selecting the math operation) what is a suitable approach? Move the computation of the result to the out_data() method and have an empty compute() method?

Support of QUndoCommand

Add the support of QundoCommand and QUndoStack to add ctrl+z/ctrl+y easily

Expected Behavior

I think we have to create:
Create_node_command
Delete_nodes_command
Move_nodes_command
Unlink_nodes_command
Link_nodes_command
Resize_nodes_command
and add the undo()/redo() action to scene

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.