arlen
arlen

Reputation: 29

How to modify viewbox of existing PlotWidget, which is generated by QT designer?

I need to select and get data of two graphics from plots, I found a example, where it is created a custom viewbox and then create a plot like that pw = pg.PlotWidget(viewBox=vb), however I don't know how to do it when we have the plot as a Qt Creator promoted pyqtgraph widget. Below is the example that I found in a forum (link: https://groups.google.com/forum/#!topic/pyqtgraph/pTrem1RCKSw) Here is the code (works perfectly)

import numpy as np
import pyqtgraph as pg
from pyqtgraph.Point import Point
from pyqtgraph.graphicsItems.ItemGroup import ItemGroup
from pyqtgraph.Qt import QtGui, QtCore
from matplotlib.mlab import inside_poly
from pyqtgraph.graphicsItems import ScatterPlotItem


class ChildGroup(ItemGroup):
    sigItemsChanged = QtCore.Signal()
    def __init__(self, parent):
        ItemGroup.__init__(self, parent)
        # excempt from telling view when transform changes
        self._GraphicsObject__inform_view_on_change = False
    def itemChange(self, change, value):
        ret = ItemGroup.itemChange(self, change, value)
        if change == self.ItemChildAddedChange or change == self.ItemChildRemovedChange:
            self.sigItemsChanged.emit()   
        return ret

class MyViewBox(pg.ViewBox):
      
    def mouseDragEvent(self, ev):
        
        if ev.button() == QtCore.Qt.RightButton:
            ev.ignore()    
        else:
            pg.ViewBox.mouseDragEvent(self, ev)
         
        ev.accept() 
        pos = ev.pos()
        if ev.button() == QtCore.Qt.RightButton:
            
            if ev.isFinish():  
                self.rbScaleBox.hide()
                self.ax = QtCore.QRectF(Point(ev.buttonDownPos(ev.button())), Point(pos))
                self.ax = self.childGroup.mapRectFromParent(self.ax) 
                self.Coords =  self.ax.getCoords()  
                self.getdataInRect()
                self.changePointsColors()
            else:
                self.updateScaleBox(ev.buttonDownPos(), ev.pos())
           
    def getdataInRect(self):
        # Get the data from the Graphicsitem
        self.getDataItem()
        x = self.dataxy[0]
        y = self.dataxy[1]
        # Rect Edges
        Xbl = (self.Coords[0],self.Coords[1]) # bottom left
        Xbr = (self.Coords[2],self.Coords[1]) # bottom right
        Xtr = (self.Coords[2],self.Coords[3]) # top right
        Xtl = (self.Coords[0],self.Coords[3]) # top left
        #Make a list of [(x0,y0),(x1,y1) ...]
        self.xy = list()
        for i in x:
                tmp = (x[i],y[i])
                self.xy.append(tmp)            
        self.insideIndex = inside_poly(self.xy,[Xbl, Xbr, Xtr, Xtl])    
                 
    def getDataItem(self):
        
        self.ObjItemList = pg.GraphicsScene.items(self.scene(),self.ax)
        self.dataxy = self.ObjItemList[0].listDataItems()[0].getData()
       
    def changePointsColors(self):
        
        print(self.xy)
        print(self.insideIndex)
                  
app = QtGui.QApplication([])
mw = QtGui.QMainWindow()
mw.resize(800,500)
mw.show()

vb = MyViewBox()
pw = pg.PlotWidget(viewBox=vb) 

a  = np.array([0,1,2,3,4,5,6,7,8,9,10])
b  = np.array([0,1,2,3,4,5,6,7,8,9,10])

curve0 = pw.plot(a,b, clickable=True, symbol = '+')

mw.setCentralWidget(pw)

# Start Qt event loop unless running in interactive mode or using pyside.
if __name__ == '__main__':
    import sys
    if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
        QtGui.QApplication.instance().exec_()

How could I adapt the previous code to the PlotWidget "Plot1"?

class MyApp(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self):
    pg.setConfigOption('background', 'w')
    QtWidgets.QMainWindow.__init__(self)
    Ui_MainWindow.__init__(self)
    self.setupUi(self)

    self.Plot1.plotItem.showGrid(True, True, 0.7)

    self.Plot1.setLabel('left', 'q1')
    self.Plot1.setLabel('bottom', 'Time', units='s')
    self.Plot1.setTitle(title='q1')

    pen = pg.mkPen({'color': "FF0000", 'width': 2})
    pen1 = pg.mkPen({'color': "00FF00", 'width': 2})
    self.Plot1.addLegend()
    self.q1z = self.Plot1.plot(pen = pen, name = 'Trace 1')
    self.q1k = self.Plot1.plot(pen = pen1, name = 'Trace 2')

Here the file Sample.ui

could someone help me?

Upvotes: 1

Views: 2989

Answers (1)

eyllanesc
eyllanesc

Reputation: 243955

The solution is to create a class that inherits from PlotWidget and has a viewbox to MyViewBox and then promote that widget:

plotwidget.py

import numpy as np

import pyqtgraph as pg
from pyqtgraph.Qt import QtGui, QtCore


try:
    from matplotlib.mlab import inside_poly
except ImportError:
    # https://matplotlib.org/3.1.0/api/api_changes.html
    # mlab.inside_poly has been removed in matplotlib 3.1.0
    from matplotlib.path import Path

    def inside_poly(points, verts):
        poly = Path(verts)
        return [idx for idx, p in enumerate(points) if poly.contains_point(p)]


class ChildGroup(pg.ItemGroup):
    sigItemsChanged = QtCore.Signal()

    def __init__(self, parent):
        super(ChildGroup, self).__init__(parent)
        # excempt from telling view when transform changes
        self._GraphicsObject__inform_view_on_change = False

    def itemChange(self, change, value):
        ret = ItemGroup.itemChange(self, change, value)
        if change == self.ItemChildAddedChange or change == self.ItemChildRemovedChange:
            self.sigItemsChanged.emit()
        return ret


class MyViewBox(pg.ViewBox):
    def mouseDragEvent(self, ev):

        if ev.button() == QtCore.Qt.RightButton:
            ev.ignore()
        else:
            pg.ViewBox.mouseDragEvent(self, ev)

        ev.accept()
        pos = ev.pos()
        if ev.button() == QtCore.Qt.RightButton:
            if ev.isFinish():
                self.rbScaleBox.hide()
                self.ax = QtCore.QRectF(
                    pg.Point(ev.buttonDownPos(ev.button())), pg.Point(pos)
                )
                self.ax = self.childGroup.mapRectFromParent(self.ax)
                self.Coords = self.ax.getCoords()
                self.getdataInRect()
                self.changePointsColors()
            else:
                self.updateScaleBox(ev.buttonDownPos(), ev.pos())

    def getdataInRect(self):
        # Get the data from the Graphicsitem
        self.getDataItem()
        x = self.dataxy[0]
        y = self.dataxy[1]
        # Rect Edges
        Xbl = (self.Coords[0], self.Coords[1])  # bottom left
        Xbr = (self.Coords[2], self.Coords[1])  # bottom right
        Xtr = (self.Coords[2], self.Coords[3])  # top right
        Xtl = (self.Coords[0], self.Coords[3])  # top left
        # Make a list of [(x0,y0),(x1,y1) ...]
        self.xy = list()
        for i in x:
            tmp = (x[i], y[i])
            self.xy.append(tmp)
        self.insideIndex = inside_poly(self.xy, [Xbl, Xbr, Xtr, Xtl])

    def getDataItem(self):

        self.ObjItemList = pg.GraphicsScene.items(self.scene(), self.ax)
        self.dataxy = self.ObjItemList[0].listDataItems()[0].getData()

    def changePointsColors(self):
        print(self.xy)
        print(self.insideIndex)


class MyPlotWidget(pg.PlotWidget):
    def __init__(self, parent=None):
        super(MyPlotWidget, self).__init__(parent, viewBox=MyViewBox())

Sample.ui

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>Dialog</class>
 <widget class="QDialog" name="Dialog">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>487</width>
    <height>368</height>
   </rect>
  </property>
  <property name="palette">
   <palette>
    <active>
     <colorrole role="Base">
      <brush brushstyle="SolidPattern">
       <color alpha="255">
        <red>255</red>
        <green>255</green>
        <blue>255</blue>
       </color>
      </brush>
     </colorrole>
     <colorrole role="Window">
      <brush brushstyle="SolidPattern">
       <color alpha="255">
        <red>86</red>
        <green>86</green>
        <blue>86</blue>
       </color>
      </brush>
     </colorrole>
    </active>
    <inactive>
     <colorrole role="Base">
      <brush brushstyle="SolidPattern">
       <color alpha="255">
        <red>255</red>
        <green>255</green>
        <blue>255</blue>
       </color>
      </brush>
     </colorrole>
     <colorrole role="Window">
      <brush brushstyle="SolidPattern">
       <color alpha="255">
        <red>86</red>
        <green>86</green>
        <blue>86</blue>
       </color>
      </brush>
     </colorrole>
    </inactive>
    <disabled>
     <colorrole role="Base">
      <brush brushstyle="SolidPattern">
       <color alpha="255">
        <red>86</red>
        <green>86</green>
        <blue>86</blue>
       </color>
      </brush>
     </colorrole>
     <colorrole role="Window">
      <brush brushstyle="SolidPattern">
       <color alpha="255">
        <red>86</red>
        <green>86</green>
        <blue>86</blue>
       </color>
      </brush>
     </colorrole>
    </disabled>
   </palette>
  </property>
  <property name="windowTitle">
   <string>Dialog</string>
  </property>
  <layout class="QVBoxLayout" name="verticalLayout">
   <property name="leftMargin">
    <number>30</number>
   </property>
   <property name="topMargin">
    <number>40</number>
   </property>
   <property name="rightMargin">
    <number>40</number>
   </property>
   <property name="bottomMargin">
    <number>30</number>
   </property>
   <item>
    <widget class="MyPlotWidget" name="Plot1">
     <property name="autoFillBackground">
      <bool>false</bool>
     </property>
     <property name="backgroundBrush">
      <brush brushstyle="SolidPattern">
       <color alpha="255">
        <red>0</red>
        <green>0</green>
        <blue>0</blue>
       </color>
      </brush>
     </property>
    </widget>
   </item>
  </layout>
 </widget>
 <customwidgets>
  <customwidget>
   <class>MyPlotWidget</class>
   <extends>QGraphicsView</extends>
   <header>plotwidget</header>
  </customwidget>
 </customwidgets>
 <resources/>
 <connections/>
</ui>
pyuic5 Sample.ui -o sample.py -x

main.py

from PyQt5 import QtWidgets

import pyqtgraph as pg

from sample import Ui_Dialog


class MyApp(QtWidgets.QDialog, Ui_Dialog):
    def __init__(self, parent=None):
        super(MyApp, self).__init__(parent)
        pg.setConfigOption("background", "w")
        self.setupUi(self)
        self.Plot1.plotItem.showGrid(True, True, 0.7)
        self.Plot1.setLabel("left", "q1")
        self.Plot1.setLabel("bottom", "Time", units="s")
        self.Plot1.setTitle(title="q1")

        pen = pg.mkPen({"color": "FF0000", "width": 2})
        pen1 = pg.mkPen({"color": "00FF00", "width": 2})
        self.Plot1.addLegend()
        self.q1z = self.Plot1.plot(pen=pen, name="Trace 1")
        self.q1k = self.Plot1.plot(pen=pen1, name="Trace 2")


if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)
    w = MyApp()
    w.show()
    sys.exit(app.exec_())
├── main.py
├── plotwidget.py
├── sample.py
└── Sample.ui

Upvotes: 2

Related Questions