enyo
enyo

Reputation: 41

Interactive bar chart

I'm trying to create some kind of gantt chart using pyqt5. My goal is to have a chart, which provides these features:

So I could do the first and second point, but I don't know how to create the interactivity. Is this possible with a BarGraphItem?

My data is given from a .csv which looks like this:

Task    Start   End Duration
0       0       4   4
2       4       7   3
6       7       8   1
2       8       9   1
4       9       13  4
2       13      15  2
4       15      21  6
5       21      23  2
...

Pandas dataframe will be used to read the file. Here's the code so far:

import sys
import pandas as pd
from PyQt5.QtWidgets import QApplication, QMainWindow, QFrame, QGridLayout, QComboBox, QLabel, QCheckBox
from PyQt5.QtCore import QSize, Qt
import pyqtgraph as pg

class View(QMainWindow):
    def __init__(self):
        QMainWindow.__init__(self)
        self.setupUI()

    def setupUI(self):
        self.setWindowTitle("Monitor")
        self.resize(800, 600)
        self.setMinimumSize(QSize(800, 600))
        
        graph_frame = QFrame()
        graph_g_layout = QGridLayout(graph_frame)
        graph_g_layout.setContentsMargins(15, 15, 15, 15)
        graph_g_layout.setSpacing(20)

        self.label = QLabel("Task-Monitor", graph_frame)
        self.checkbox = QCheckBox("Selector", graph_frame)
        self.checkbox.stateChanged.connect(self.clickBox)
        self.graph_cbox = QComboBox(graph_frame)
        self.graph_cbox.setMinimumSize(QSize(150, 0))        
        self.graph_widget = pg.PlotWidget(graph_frame)
        self.graph_widget.showGrid(x=True, y=True)
        self.graph_widget.setBackground(background=None)

        graph_g_layout.addWidget(self.label, 0, 0, 1, 1)
        graph_g_layout.addWidget(self.checkbox, 0, 1, 1, 1)
        graph_g_layout.addWidget(self.graph_cbox, 0, 2, 1, 1)
        graph_g_layout.addWidget(self.graph_widget, 1, 0, 1, 3)
        graph_g_layout.setColumnStretch(0, 0 + 1)
        self.setCentralWidget(graph_frame)
        self.updatePlot()

    def clickBox(self, state):
        if state == Qt.Checked:
            self.lr = pg.LinearRegionItem(bounds=[0, 100], movable=True)       
            self.graph_widget.addItem(self.lr)        
        else:
            self.graph_widget.removeItem(self.lr)

    def updatePlot(self):
        dataFrame = pd.read_csv("GUI/data.csv", delimiter=';')
        x0 = []
        y0 = []
        width = []
        brush = pg.mkBrush(color=(90, 90, 90))

        for index, row in dataFrame.iterrows():
            x0.append(row['start'])
            y = row['task']
            y0.append(y)
            width.append(row['duration'])

        self.barItem = pg.BarGraphItem(x0=x0, y0=y0, width=width, height=0.8, brush=brush)
        self.graph_widget.addItem(self.barItem)


if __name__ == "__main__":
    app = QApplication(sys.argv)
    win = View()
    win.show()
    sys.exit( app.exec_() )

Is it possible to make this BarGraphItem interactive with a mouseclick or hover? I'm pretty sure it has to be harder to do something like this...

Upvotes: 0

Views: 880

Answers (1)

musicamante
musicamante

Reputation: 48335

As already reported in the linked answer, if you want to be able to individually control each bar, you have to create individual items.

Then, since BarGraphItem inherits from QGraphicsObject (and QGraphicsItem) you can just subclass it and set its tooltip or override its methods.

class InteractiveBarItem(pg.BarGraphItem):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.setToolTip('hello! y={}'.format(self.boundingRect().y()))
        # required in order to receive hoverEnter/Move/Leave events
        self.setAcceptHoverEvents(True)

    def hoverEnterEvent(self, event):
        print('hover!')

    def mousePressEvent(self, event):
        print('click!')


class View(QMainWindow):
    # ...
    def updatePlot(self):
        dataFrame = pd.read_csv("data.csv", delimiter=';')
        brush = pg.mkBrush(color=(90, 90, 90))

        for index, row in dataFrame.iterrows():
            item = InteractiveBarItem(
                x0=[row['start']], 
                y0=row['task'], 
                width=row['duration'], 
                height=0.8, 
                brush=brush)
            self.graph_widget.addItem(item)

Upvotes: 1

Related Questions