domarm-comat / pglive

Live pyqtgraph plot
MIT License
77 stars 14 forks source link

Horizontal Bar chart with categories #7

Closed optio50 closed 2 years ago

optio50 commented 2 years ago

Hello,

I have successfully been using PGlive for awhile now and love it.

Can PGlive do Horizontal Bar chart with categories like below?

Barchart

If so can you add some details to accomplish this? Categories on the Y-axis and keep the time on the X-axis.

Thanks for a great charting app!

domarm-comat commented 2 years ago

Hey!

I'm glad to hear you're happy with pglive!

What you want to plot is not feasible in current implementation. You can do categories easily (it's just change of tick LiveAxis -> tickStrings), but not with continuous X axis updates (it works with base 0 on axis X). To plot something like that, you also need to extend Y data format.

I can imagine that to have multiple bars on X you need to specify width of each category for every X. Or having list of categories, that are "1 (included)" in specific tick to be able to plot width of that specific bar in time (I would prefer this solution).

If you can provide me with how you would imagine your Y values or suggest some easier solution, I will be happy to implement it in pglive.

domarm-comat commented 2 years ago

Short update on your plot request. I was working on it a bit and got plot like this: categorized-bar-plot.gif

The idea is, that you generate List of Categories for every tick. Width of the bar is then calculated as a number of Ticks that holds certain category.

You can define categories beforehand in the list which makes them appear like they appear in the list. For my example: categories = ["On", "Off", "Idle", "Warning", "Failure"] On is y=0, Off is y=1 and so on. But it's not mandatory, plot can handle also unknown categories and add it to the top as they appear.

You can also define specific color for each category and default color as well for undefined.

I need to do some testing and write documentation. I'm expecting to release it soon. If you have any thoughts or comments on it, please let me know.

optio50 commented 2 years ago

Ha......I think thats it. I will try it out. Thanks :-)

optio50 commented 2 years ago

Well I am having difficulty figuring it out. I normally build a graph in PyQT5 like this

    `volts_plot = LiveLinePlot(pen="red", fillLevel=0, brush=(102,0,0,100))

    # Data connectors for each plot with dequeue of max_points points
    self.volts_connector = DataConnector(volts_plot, max_points=86400) # 24 hours in seconds

    # Setup bottom axis with TIME tick format
    # use Axis.DATETIME to show date
    volts_bottom_axis = LiveAxis("bottom", **{Axis.TICK_FORMAT: Axis.TIME})
    #volts_left_axis = LiveAxis('left', showValues=True)

    # Create plot itself
    self.Battery_Volts_graph_Widget = LivePlotWidget(title="Battery Volts 24 Hrs", axisItems={'bottom': volts_bottom_axis}, **kwargs)

    # Show grid
    self.Battery_Volts_graph_Widget.showGrid(x=True, y=True, alpha=0.3)

    # Set labels
    self.Battery_Volts_graph_Widget.setLabel('bottom')
    self.Battery_Volts_graph_Widget.setLabel('left', 'Volts')

    # Add Line
    self.Battery_Volts_graph_Widget.addItem(volts_plot)

    # Add chart to Layout in Qt Designer
    self.Chart_Volts_Layout.addWidget(self.Battery_Volts_graph_Widget) 

   self.volts_connector.cb_append_data_point(float(BatteryVolts), timestamp)

`

I tried using the LiveHBarPlot & categories I tried to specify the y category in the append_data_point as in state=0

Sorry, maybe you can provide some insight?

from pglive.sources.live_categorized_bar_plot import LiveCategrorizedBarPlot ModuleNotFoundError: No module named 'pglive.sources.live_categorized_bar_plot'

In addition, after upgrading to pglive .5. When not using the LiveCategrorizedBarPlot my other programs get the error.

Traceback (most recent call last): File "./PyQT5-Dual-Charger.py", line 1308, in <module> UIWindow = UI() File "./PyQT5-Dual-Charger.py", line 189, in __init__ self.watts_connector = DataConnector(watts_plot, max_points=86400) # 24 hours in seconds File "/home/chromebox/.local/lib/python3.8/site-packages/pglive/sources/data_connector.py", line 60, in __init__ self.sig_data_reset.connect(self.plot.plot_widget.slot_connector_reset) AttributeError: 'NoneType' object has no attribute 'slot_connector_reset

Is the spelling error intentional? LiveCategrorizedBarPlot gror?

Edit: I tried reinstalling different versions and 0.3.3 is the last one that works with my existing programs.

optio50 commented 2 years ago

When I do eventually get the module imported Here is what I will try, adapting from your example

    `categories = ["On", "Off", "Idle", "Warning", "Failure"]
    state_plot = LiveCategrorizedBarPlot(categories,
                           category_color={"Failure": "r", "Warning": "orange", "Off": "silver", "Idle": "blue"})

    # Data connectors for each plot with dequeue of max_points points
    self.state_connector = DataConnector(state_plot, max_points=86400) # 24 hours in seconds

    # Setup bottom axis with TIME tick format
    # use Axis.DATETIME to show date
    state_bottom_axis = LiveAxis("bottom", **{Axis.TICK_FORMAT: Axis.DURATION})
    state_left_axis = LiveAxis("left", **{Axis.TICK_FORMAT: Axis.CATEGORY, Axis.CATEGORIES: plot.categories})

    # Create plot itself
    self.Charger_State_graph_Widget = LivePlotWidget(title="Charger State 24 Hrs", axisItems={'bottom': state_bottom_axis, 'left': left_axis})

    # Show grid
    self.Charger_State_graph_Widget.showGrid(x=True, y=True, alpha=0.3)

    # Set labels
    self.Charger_State_graph_Widget.setLabel('bottom')
    #self.Charger_State_graph_Widget.setLabel('left', 'Amps')

    # Add Line
    self.Charger_State_graph_Widget.addItem(state_plot)

    # Add chart to Layout in Qt Designer
    self.Chart_Charger_State_Layout.addWidget(self.Charger_State_graph_Widget)`
domarm-comat commented 2 years ago

Except that, do you have any other errors with LiveCategorizedBarPlot, is example working for you? I've just briefly test your code and just had to change Axis.CATEGORIES: plot.categories to Axis.CATEGORIES: state_plot.categories.

optio50 commented 2 years ago

Before I try getting the categorized Bar chart working, I am trying to find the solution to the second error which is eluding me. ver .0.3.3 everything is ok but from then on it error's out. I understand when you say you introduced a new feature but I cant seem to grasp it.

ver 0.5.1 Traceback (most recent call last): File "./PyQT5-No-Multiplus-Single-Charger-JBD-BMS.py", line 821, in <module> UIWindow = UI() File "./PyQT5-No-Multiplus-Single-Charger-JBD-BMS.py", line 140, in __init__ self.watts_connector = DataConnector(watts_plot, max_points=86400) # 24 hours in seconds File "/home/chromebox/.local/lib/python3.8/site-packages/pglive/sources/data_connector.py", line 60, in __init__ self.sig_data_reset.connect(self.plot.plot_widget.slot_connector_reset) AttributeError: 'NoneType' object has no attribute 'slot_connector_reset' I am still building the chart in the same way and dont understand what you mean by overriding LivePlotWidget.init and I am not sure what to modify from the links you posted.

I apologize for my lack of understanding, I have only been using python a short time. Thank you for taking the time I know you are busy.

Here is the code that errors.

   `# Define crosshair parameters
    kwargs = {Crosshair.ENABLED: True,
    Crosshair.LINE_PEN: pg.mkPen(color="purple", width=1),
    Crosshair.TEXT_KWARGS: {"color": "white"}, }
    pg.setConfigOption('leftButtonPan', False) # For drawing a zooming box.
    watts_plot = LiveLinePlot(pen="orange", fillLevel=0, brush=(213,129,44,100))

   # Data connectors for each plot with dequeue of max_points points
    self.watts_connector = DataConnector(watts_plot, max_points=86400) # 24 hours in seconds

    # Setup bottom axis with TIME tick format
    # use Axis.DATETIME to show date
    watts_bottom_axis = LiveAxis("bottom", **{Axis.TICK_FORMAT: Axis.TIME})

    # Create plot itself
    self.Solar_graph_Widget = LivePlotWidget(title="Solar Watts 24 Hrs", axisItems={'bottom': watts_bottom_axis}, **kwargs)

    # Show grid
    self.Solar_graph_Widget.showGrid(x=True, y=True, alpha=0.3)

    # Set labels
    self.Solar_graph_Widget.setLabel('bottom')
    self.Solar_graph_Widget.setLabel('left', 'Watts')

    # Add Line
    self.Solar_graph_Widget.addItem(watts_plot)

    # Add chart to Layout in Qt Designer
    self.Chart_Watts_Layout.addWidget(self.Solar_graph_Widget)

    self.watts_connector.cb_append_data_point(int(SolarWatts), timestamp)`
domarm-comat commented 2 years ago

Thanks to your example, I found out the issue. The problem was, that DataConnector was created before LiveLinePlot was added into Solar_graph_Widget. Therefor it was accessing None value. I released v0.5.2 where you can use the same code without triggering the error. I hope that solves your issue.

optio50 commented 2 years ago

@domarm-comat

Wow! super cool, I have the Categorized Bar Chart working and the Dataconnector is now working for my older programs.

Thanks :-)