Adding GraphicsObject to two ViewBoxes or QGraphicsViews - python-3.x

Ultimately I want pyqtgraph to display a single GraphicsObject simultaneously in several ViewBoxes, sharing a single scene.
At the same time I want to have some other GraphicsObjects in a single ViewBox only.
Something like this:
vb0 = ViewBox()
vb1 = ViewBox()
# shown only in first ViewBox vb0
local_item = GraphicsObject()
# shown in all ViewBoxes
global_item = GraphicsObject()
vb0.addItem(local_item)
assert vb0.scene() is vb1.scene()
# the magic function i am looking for
vb0.scene().addItemGlobally(global_item)
So very naively I looked into the ViewBox sourcecode and reproduced the steps for addItem() like here:
import pyqtgraph as pg
from pyqtgraph.Qt import QtCore as qc, QtGui as qg, QtWidgets as qw
class Polygon(pg.GraphicsObject):
"""Just a Triangle..."""
app = qw.QApplication([])
viewer = pg.GraphicsWindow()
vb0 = viewer.addViewBox(0, 0)
vb1 = viewer.addViewBox(0, 1)
viewer.show()
poly_yellow = Polygon((125, 125, 0, 255))
scene = vb0.scene()
added_items = vb1.addedItems = vb0.addedItems
child_group = vb1.childGroup = vb0.childGroup
child_group.setParent(scene)
child_group.itemsChangedListeners.append(vb1)
# here reproducing steps found in addItem()
if scene is not poly_yellow.scene():
scene.addItem(poly_yellow)
poly_yellow.setParentItem(child_group)
added_items.append(poly_yellow)
vb0.updateAutoRange()
# vb1.updateAutoRange()
# checking if ViewBoxes share relevant attributes
assert vb0.scene() is vb1.scene()
assert vb0.scene() is poly_yellow.scene()
assert vb0.addedItems is vb1.addedItems
assert vb0.childGroup is vb1.childGroup
app.exec_()
Running this gives me two ViewBoxes, but only vb0 showing the triangle. Also this approach would give me global only Items. Is there any way to get something like local/global items without re-implementing ViewBoxes completely?
EDIT: I think it is impossible to achieve what I want with pyqtgraph ViewBoxes. A transform of the global items must happen just before the painting.

I found it is not easily doable using ViewBox. However, it is possible to us pyqtgraphs GraphicsView, implementing a lot of functionality found in the ViewBox class already.
My approach now is to generate as many GraphicsView as I need, and set them all to one scene via GraphicsView.setScene()
The scene contains the 'global' items, displayed in every View. The Viewspecific, local Items are drawn using the GraphicsView.drawBackground() function.
I haven't tested it to much, but it seems quite good working, for the case, were several thousend items are added to the scene, but only few items are drawn to the background.

Related

class inherit from networkx.Graph fails when using networkx.union()

All
I try to inherit networkx.Graph with my own, adding two node and an edge when the graph is created. But it fail with
networkx.exception.NetworkXError: ('The node sets of G and H are not disjoint.', 'Use appropriate rename=(Gprefix,Hprefix)or use disjoint_union(G,H).')
when I am trying to union my graphs, here is my code. Anything do I miss?
#!/usr/bin/python3
import networkx as nx
class die(nx.Graph):
nLatency = 2
def __init__(self):
super().__init__()
self.addNet()
def addNet(self):
self.add_node('N0')
self.add_node('N1')
self.add_edge('N0', 'N1', name='nLink', latency=self.nLatency)
S0D0 = die()
S1D0 = die()
Top = nx.union(S0D0, S1D0, rename=('S0D0', 'S1D0'))
So what is happening here is that networkx tries to create two temporary graphs whose nodes are 'S0D0-N0', 'S0D0-N1' for one and 'S1D0-N1', 'S1D0-N2' for the other. Then it tries to join them.
However as you dig through the code when it does that, the two new graphs created have the same class as the originals. So, let's call the new graphs created H1 and H2. Because H1 and H2 also both have class die, they are initialized with the nodes 'N0' and 'N1' and then 'S0D0-N0', 'S0D0-N1' or 'S1D0-N1', 'S1D0-N2' are added. So both are initialized with 'N0' and 'N1'.
So then at the next stage in the union process it tests whether or not H1 and H2 have any common nodes, and they do. So you get the error.
So that's the cause of the error. How to fix it probably depends on why you are initializing the graphs with these nodes, and what class you want Top to have.
If Top has class die, it's going to have to have 'N0' and 'N1' (because of the initialization), which I suspect you don't actually want. If you just want Top to be a Graph, you can first turn S0D0 and S1D0 into Graphs:
Top = nx.union(nx.Graph(S0D0), nx.Graph(S1D0), rename=('S0D0', 'S1D0'))

PyQt QScrollArea doesn't display widgets

I am somewhat new to GUI programming and very new to PyQt, and I'm trying to build a GUI that displays a list of questions. I have created a QuestionBank class that subclasses QWidget and overrides the .show() method to display the list properly. I have tested this alone and it works correctly. However, the list of questions can be quite long, so I've been trying to make it scrollable. Rather than add a QScrollBar to the widget and then set up the event triggers by hand, I've been trying to my QuestionBank widget in a QScrollArea based on the syntax I've seen in examples online. While the scroll area shows up fine, it does not at all display the question bank but rather just shows a blank outline.
The QuestionBank class looks like this:
class QuestionBank(QWidget):
BUFFER = 10 # space between questions (can be modified)
def __init__(self, parent, questions):
# `parent` should be the QWidget that contains the QuestionBank, or None if
# QuestionBank is top level
# `questions` should be a list of MasterQuestion objects (not widgets)
QWidget.__init__(self, parent)
self.questions = [MasterQuestionWidget(self, q) for q in questions]
self.bottomEdge = 0
def show(self, y=BUFFER):
QWidget.show(self)
for q in self.questions:
# coordinates for each each question
q.move(QuestionBank.BUFFER, y)
q.show()
# update y-coordinate so that questions don't overlap
y += q.frameGeometry().height() + QuestionBank.BUFFER
self.bottomEdge = y + 3 * QuestionBank.BUFFER
# ... other methods down here
My code for showing the scroll bar looks like this:
app = QApplication(sys.argv)
frame = QScrollArea()
qs = QuestionBank(None, QFileManager.importQuestions())
qs.resize(350, 700)
frame.setGeometry(0, 0, 350, 300)
frame.setWidget(qs)
frame.show()
sys.exit(app.exec_())
I have tried many variants of this, including calling resize on frame instead of qs, getting rid of setGeometry, and setting the parent of qs to frame instead of None and I have no idea where I'm going wrong.
If it helps, I'm using PyQt5
Here is the question bank without the scroll area, to see what it is supposed to look like:
Here is the output of the code above with the scroll area:
This variation on the code is the only one that produces any output whatsoever, the rest just have blank windows. I'm convinced its something simple I'm missing, as the frame is obviously resizing correctly and it obviously knows what widget to display but its not showing the whole thing.
Any help is much appreciated, thank you in advance.

How to feed an existing visualizer with values from a sync_block in GNU Radio?

This is a continuation of this question. I understand that calling a plotting function from a block thread is problematic because it might conflict with the WX Gui thread.
Can anyone show me a very simple code snippet which plots the value of input_items using an existing visualizer instead of plotting it inside the block thread?
Here is an outline of the GNU Radio sync_block which receives the values to be plotted in input_items :
import numpy
from gnuradio import gr
class xyz(gr.sync_block):
def __init__(self, multiple):
gr.sync_block.__init__(self,
name="xyz",
in_sig=[<+numpy.float+>],
out_sig=[<+numpy.float+>])
def work(self, input_items, output_items):
in0 = input_items[0]
out = output_items[0]
.....
# How to pass the value of input_items to an existing visualizer which plots a graph based on these values on the GUI
.....
out[:] = in0
return len(output_items[0])
Also which existing plot block(in gnuradio/gr-wxgui) is suggested to be used as a reference for this?

PyQt/PySide How to access/move QGraphicsItem after having added it to QGraphicsScene

This might be a very uninformed question.
I've been trying to figure out QGraphics*, and have run into a problem when trying to move an item (a pixmap) relative to or inside of the QGraphicsView.
class MainWindow(QMainWindow,myProgram.Ui_MainWindow):
def __init__(self):
super().__init__()
self.setupUi(self)
self.scene = QGraphicsScene()
self.graphicsView.setScene(self.scene)
pic = QPixmap('myPic.png')
self.scene.addPixmap(pic)
print(self.scene.items())
This is the relevant part of the program with an arbitrary PNG
My goal, for example, would be to move the trash-can to the left-most part of the QGraphicsView.
I tried appending this to the code above:
pics = self.scene.items()
for i in pics:
i.setPos(100,100)
However, it doesn't make a difference, and even if it did, it would be awkward to have to search for it with a "for in".
So my questions are:
How do I access a QPixmap item once I have added it to a QGraphicsView via a QGraphicsScene. (I believe The QPixmap is at this point a QGraphicsItem, or more precisely a QGraphicsPixmapItem.)
Once accessed, how do I move it, or change any of its other attributes.
Would be very grateful for help, or if anyone has any good links to tutorials relevant to the question. :)
The problem is that you need to set the size of your scene and then set positions of the items, check this example:
from PyQt4 import QtGui as gui
app = gui.QApplication([])
pm = gui.QPixmap("icon.png")
scene = gui.QGraphicsScene()
scene.setSceneRect(0, 0, 200, 100) #set the size of the scene
view = gui.QGraphicsView()
view.setScene(scene)
item1 = scene.addPixmap(pm) #you get a reference of the item just added
item1.setPos(0,100-item1.boundingRect().height()) #now sets the position
view.show()
app.exec_()

VTK: how can I add a scrollbar to my project?

What's the easiest way to add a scrollbar to my VTK project ?
thanks
Update
def vtkSliderCallback2(obj, event):
sliderRepres = obj.GetRepresentation()
pos = sliderRepres.GetValue()
contourFilter.SetValue(0, pos)
SliderRepres = vtk.vtkSliderRepresentation2D()
min = 0 #ImageViewer.GetSliceMin()
max = 256 #ImageViewer.GetSliceMax()
SliderRepres.SetMinimumValue(min)
SliderRepres.SetMaximumValue(max)
SliderRepres.SetValue((min + max) / 2)
SliderRepres.SetTitleText("Slice")
SliderRepres.GetPoint1Coordinate().SetCoordinateSystemToNormalizedDisplay()
SliderRepres.GetPoint1Coordinate().SetValue(0.2, 0.6)
SliderRepres.GetPoint2Coordinate().SetCoordinateSystemToNormalizedDisplay()
SliderRepres.GetPoint2Coordinate().SetValue(0.4, 0.6)
SliderRepres.SetSliderLength(0.02)
SliderRepres.SetSliderWidth(0.03)
SliderRepres.SetEndCapLength(0.01)
SliderRepres.SetEndCapWidth(0.03)
SliderRepres.SetTubeWidth(0.005)
SliderRepres.SetLabelFormat("%3.0lf")
SliderRepres.SetTitleHeight(0.02)
SliderRepres.SetLabelHeight(0.02)
SliderWidget = vtk.vtkSliderWidget()
SliderWidget.SetInteractor(iren)
SliderWidget.SetRepresentation(SliderRepres)
SliderWidget.KeyPressActivationOff()
SliderWidget.SetAnimationModeToAnimate()
SliderWidget.SetEnabled(True)
SliderWidget.AddObserver("InteractionEvent", vtkSliderCallback2)
Just to be complete and for other users googling thing.
vtkSliderWidget will do what you want if you need it to set a value.
//edit based on your edit
If you want to get the value, you have to connect an event to the slider which is fired when the value is changed. Than retrieve this value and update accordingly. An example in C++ is found here
// I actually think my issue is that the callback function is invoked for each thumb position when I slide it. How can avoid that ? In other words I only want the last position to trigger the callback function...
Try coupling it to the EndInteractionEvent instead of to the InteractionEvent.
SliderWidget.AddObserver("EndInteractionEvent", vtkSliderCallback2)
// stuff
By the way, if you use python and VTK and need GUI stuff, I advice you to use the python QT and python qt widgets which ease up alot of this stuff. Some code of one of my old projects using QT+Python+VTK for GUI + python stuff:
self.verticalSlider = QtGui.QSlider(self.centralwidget)
self.verticalSlider.setOrientation(QtCore.Qt.Vertical)
self.verticalSlider.setObjectName("verticalSlider")
self.horizontalLayout.addWidget(self.verticalSlider)
// connect slider to a method onValueChange
QObject.connect(self.verticalSlider, SIGNAL("valueChanged(int)"),
self.setFibreVolumeOpacity)
def setFibreVolumeOpacity(self, value):
// do stuff here with slider value.

Resources