Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I draw nodes and edges in PyQT?

Tags:

python

graph

pyqt

In PyQT, how can I plot small "Nodes" at given points and connect them with edges? All of the PyQT tutorials I find are "plot a button! plot a checkbox!"

like image 670
Mark Avatar asked Jan 31 '10 20:01

Mark


2 Answers

It has been a pain to find a good explanation for this (as of by the end of 2014 already), and since this question asks exactely what I was looking for, I'll post a transcription (from C++ to Python) of what I found in this post.

The code is below, and here is the rationale:

  1. QGrahpicsItem, QPainterPath and QPainterPath.Element are the classes you are looking for. Specifically, QPainterPath implements the kind of vector functionality you expect in applications such as CorelDraw, Adobe Illustrator, or Inkscape.
  2. The example below benefits from the pre-existing QGraphicsEllipseItem (for rendering nodes) and QGraphicsPathItem (for rendering the path itself), which inherit from QGraphicsItem.
  3. The Path constructor iterates over the QPainterPath elements, creating Node items for each one; Each of them, in turn, send updates to the parent Path object, which updates its path property accordingly.
  4. I found much, much easier to study the C++ Qt4 Docs than the rather less structured PyQt docs found elsewhere. Once you get used to mentally translate between C++ and Python, the docs themselves are a powerful way to learn how to use each class.

#!/usr/bin/env python
# coding: utf-8

from PyQt4.QtGui import *
from PyQt4.QtCore import *

rad = 5

class Node(QGraphicsEllipseItem):
    def __init__(self, path, index):
        super(Node, self).__init__(-rad, -rad, 2*rad, 2*rad)

        self.rad = rad
        self.path = path
        self.index = index

        self.setZValue(1)
        self.setFlag(QGraphicsItem.ItemIsMovable)
        self.setFlag(QGraphicsItem.ItemSendsGeometryChanges)
        self.setBrush(Qt.green)

    def itemChange(self, change, value):
        if change == QGraphicsItem.ItemPositionChange:
            self.path.updateElement(self.index, value.toPointF())
        return QGraphicsEllipseItem.itemChange(self, change, value)


class Path(QGraphicsPathItem):
    def __init__(self, path, scene):
        super(Path, self).__init__(path)
        for i in xrange(path.elementCount()):
            node = Node(self, i)
            node.setPos(QPointF(path.elementAt(i)))
            scene.addItem(node)
        self.setPen(QPen(Qt.red, 1.75))        

    def updateElement(self, index, pos):
        path.setElementPositionAt(index, pos.x(), pos.y())
        self.setPath(path)


if __name__ == "__main__":

    app = QApplication([])

    path = QPainterPath()
    path.moveTo(0,0)
    path.cubicTo(-30, 70, 35, 115, 100, 100);
    path.lineTo(200, 100);
    path.cubicTo(200, 30, 150, -35, 60, -30);

    scene = QGraphicsScene()
    scene.addItem(Path(path, scene))

    view = QGraphicsView(scene)
    view.setRenderHint(QPainter.Antialiasing)
    view.resize(600, 400)
    view.show()
    app.exec_()
like image 87
heltonbiker Avatar answered Sep 28 '22 01:09

heltonbiker


If you want to be able to interact with the objects displayed in the plot, you will be better off using a QGraphicsScene. It handles zooming and panning and can contain other QGraphicsItem objects that can handle their own interactions.

It's very easy to use, but there is a bit of overhead involved, especially if you plan to make thousands of objects.

You can find a PyQt tutorial here. This and the API docs should get you started.

like image 28
drxzcl Avatar answered Sep 28 '22 01:09

drxzcl