Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

I tried to implement clickable multiple plot with logarithmic scale. Because I want to the plots clickable, I used PlotCurveItem() and addItem rather than just use plot(). But unfortunatolly, if I used SetLogMode with PlotCurveItem(), the x-axis and y-axis will have almost infinite values (And in reality, it is not displayed in logarithmic ). When I use plot() such problem doesn't happen.

Any solution? or Do you know other good way to implement clickable multiple plot with logarithmic scale? Thanks.

app = QtGui.QApplication([])
w = pg.GraphicsLayoutWidget(show=True)
w.resize(800,800)
p1 = w.addPlot(0, 0, title="p1")
p1.setLogMode(True,True)
temp_curve = pg.PlotCurveItem(y=2*x,
                              pen=pg.mkPen(pg.mkColor(str(color[0]))),
                              width=4, clickable=True)
temp_curve2 = pg.PlotCurveItem(y=3*x,
                              pen=pg.mkPen(pg.mkColor(str(color[1]))),
                              width=4, clickable=True)
p1.addItem(temp_curve)
p1.addItem(temp_curve2)

Problem

  • When you change the scale of the Plot widget, it doesn´t update the data on the PlotCurveItem. Then you have the same curve but with a wrong axis scale. It works when you use plot() because it creates a PlotDataItem instead of a PlotCurveItem, below I will explain a little more about that.
  • Solution 1 (not convenient)

  • Create a function like the getData() inside the PlotDataItem's doc. to get the data, transform it to log/linear mode, and then set the data again into the PlotCurveItem every time you change the scale. This can be messy.
  • Solution 2 (More convenient)

  • You can use the PlotDataItem instead because it generates a PlotCurveItem as part of it (read this: PlotCurveItem's doc. and PlotDataItem's doc.). This PlotDataItem updates the data of the plot when you change the scale. Because that is how it works. If you want to know how the changes of scale work I suggest to read the PlotDataItem's source code. Implementing this you can create your plots using:
  • curve_1 = pg.PlotDataItem(
        x, y1, pen=pg.mkPen(pg.mkColor((70,70,30))),
        width=4 # , clickable = True   <-- This don't work
    curve_2 = pg.PlotDataItem(
        x, y2, pen=pg.mkPen(pg.mkColor((70,70,70))),
        width=4 # , clickable = True   <-- This don't work
    

    Problem of Solution 2 (and its solution)

  • Now a new problem arises, this PlotDataItem has a little error. It has the signals when it's clicked, but never activates the "clickable" property of the PlotCurveItem created inside it. This will cause you to never get a response when you click on the plot despite you pass the clickable = True argument.
  • We can do a lot of things to solve this problem, the best and the general solution will implicate a big rework in the __init__() builder of PlotDataItem(). But considering that you want to implement clickable multiple plots with a logarithmic scale, we can just force it to be clickable. To do this we have to set the clickable property of the curve item inside the PlotDataItem with this:
  • curve_1.curve.setClickable(True)
    curve_2.curve.setClickable(True)
    
  • Now you can connect the signal emited (sigClicked) by the PlotCurveItem when it is clicked like this:
  • curve_1.sigClicked.connect(curve_1_clicked)
    curve_2.sigClicked.connect(curve_2_clicked)
    def curve_1_clicked(ev):
        print('Curve 1 clicked')
        ## Do more things...
    def curve_1_clicked(ev):
        print('Curve 2 clicked')
        ## Do more things...
    

    I tested it with a code different from yours, using the PlotWidget and using objects to build the GUI, but it must work the same. Here is my code:

    import sys
    import pyqtgraph as pg
    import numpy as np
    from PyQt5 import QtWidgets
    class MyApp(QtWidgets.QWidget):
        def __init__(self):
            QtWidgets.QWidget.__init__(self)
            lay = QtWidgets.QVBoxLayout()
            self.setLayout(lay)
            self.glw = pg.PlotWidget(show=True)
            lay.addWidget(self.glw)
            x = np.arange(100)
            y1 = 2*x
            y2 = 3*x**3
            self.curve_1 = pg.PlotDataItem(
                x, y1, pen=pg.mkPen(pg.mkColor((70,70,30))),
                width=4 # , clickable = True   <-- This don't work
            self.curve_2 = pg.PlotDataItem(
                x, y2, pen=pg.mkPen(pg.mkColor((70,70,70))),
                width=4 # , clickable = True   <-- This don't work
            self.curve_1.curve.setClickable(True)
            self.curve_2.curve.setClickable(True)
            self.glw.addItem(self.curve_1)
            self.glw.addItem(self.curve_2)
            # self.glw.setLogMode(False,False)
            self.glw.setLogMode(True,True)
            self.curve_1.sigClicked.connect(self.c1click)
            self.curve_2.sigClicked.connect(self.c2click)
        def c1click(self, ev):
            print('Curve 1 clicked')
        def c2click(self, ev):
            print('Curve 2 clicked')
    if __name__ == "__main__":
        app = QtWidgets.QApplication(sys.argv)
        window = MyApp()
        window.show()
        sys.exit(app.exec_()) 
            

    Thanks for contributing an answer to Stack Overflow!

    • Please be sure to answer the question. Provide details and share your research!

    But avoid

    • Asking for help, clarification, or responding to other answers.
    • Making statements based on opinion; back them up with references or personal experience.

    To learn more, see our tips on writing great answers.