Skip to content

Conversation

@uutzinger
Copy link

Updated legend.py:

  • added update_using_camera function which scales and places origin in dock
  • dock can be left,right, top, bottom
  • updated color handling (not fully tested)
  • updated legend padding

Example qt/lineplot.py

  • creates 3 traces in a lineplot and animates it
  • uses white background to plot black, red and blue demo traces
  • uses PyQt
  • utilizes legend
  • adjusts tick, grids, docks
  • measures displayed segments per second

gitignore

  • added _gallery

testing

  • Used black to update formatting
  • ran tests, some failed but not due to legend changes
  • python docs/source/generate_api.py because legend has now background color option

uutzinger and others added 10 commits May 20, 2025 19:03
Updated legend color handling and scaling
Provided PyQt lineplot example using black on white background
# delete docs/api dir
rm -rf docs/source/api

# delete _gallery dir
rm -rf docs/source/_gallery

# regenerate the API docs
python docs/source/generate_api.py
Updated legend color handling and scaling
Provided PyQt lineplot example using black on white background
# delete docs/api dir
rm -rf docs/source/api

# delete _gallery dir
rm -rf docs/source/_gallery

# regenerate the API docs
python docs/source/generate_api.py
@kushalkolar
Copy link
Member

This looks like a great start! Do you want to implement more of what's in #403 or we'll just use this for now and implement the rest when we can.

I have a bunch of changes to make but I think it's easier if I edit your branch directly if you don't mind.

Comment on lines +307 to +311
def update_using_camera(self):
"""
Update the legend position and scale using the camera.
This only works if legend is in a Dock, not a Plot or Subplot.
"""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will be much easier after #830 is merged (by tomorrow) since it adds an overlay render pass.

)

# Subplot
self.ax = self.fig[0, 0]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's just call these subplot instead of ax to conform with the rest of the examples, I can make these changes if you want

Comment on lines +24 to +31
def rotate(angle, axis_x, axis_y, axis_z):
"""
Quaternion representing rotation around the given axis by the given angle.
"""
a2 = angle/2.0
c = cos(a2)
s = sin(a2)
return (axis_x * s, axis_y * s, axis_z * s, c)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice, would be useful to have this in the addition to the existing Graphic.rotate

Comment on lines +86 to +107
# X label
self.ax.docks["bottom"].size = 30
self.ax.docks["bottom"].add_text(
"X",
font_size=16,
face_color=(0, 0, 0, 1),
anchor="middle-center",
offset=(0, 0, 0),
)
self.ax.docks["bottom"].background_color = self.WHITE

# Y label
q = rotate(pi/2.0, 0., 0., 1.) # rotate 90 deg around z-axis
self.ax.docks["left"].size = 30
self.ax.docks["left"].add_text(
"Y",
font_size=16,
face_color=(0, 0, 0, 1),
anchor="middle-center",
offset=(0, 0, 0),
rotation=q,
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pygfx will have seen soon, let's wait until we have the proper implementation pygfx/pygfx#739

Comment on lines +76 to +84
self.ax.docks["top"].size = 30
self.ax.docks["top"].add_text(
"Line Plots",
font_size=16,
face_color=(0, 0, 0, 1),
anchor="middle-center",
offset=(0, 0, 0),
)
self.ax.docks["top"].background_color = self.WHITE
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not subplot.title = "title"?

@kushalkolar
Copy link
Member

Nice work! I was taking a look at your repos, are you trying to create something like this https://github.com/uutzinger/SerialUI ? Curious to hear how fastplotlib has been working for your use case.

@uutzinger
Copy link
Author

Nice work! I was taking a look at your repos, are you trying to create something like this https://github.com/uutzinger/SerialUI ? Curious to hear how fastplotlib has been working for your use case.

Yes that is why I am looking into fastplotlib. I hope to have some working prototype with fastplotlib shortly.

I have been trying to figure out what the advantage of fastplotlib might be compared to pyqtgraph which is what I am currently using.

  • It took a while to figure out how to make a "traditional" chart plot with fastplotlib, especially because there are limited examples for Qt and I had to first figure out which window manager configuration on ubuntu works for fastplotlib.
  • I made a simple test program for each graphics package that plots more or less the same line plots.
  • So far my conclusion is that fastplotlib does on my old mobile RTX3060 not provide more throughput but it has lower main thread cpu usage hopefully providing more capacity for my other qt threads.
  • Specifically flastplotlib has about 50% main thread cpu usage and pyqtgraph has about 80-90%. The number of line segments per second is slightly lower with fastplotlib compared to pyqtgraph.
  • It is not trivial to measure throughput with fastplotlib. The time it takes to generate new data, update the line plots with the new data and scheduling a refresh of the canvas is in my case less then10% of the update/loop time but GPU goes up to 75% on NVIDIA usage meter and plotting maxes out.
  • Also when I resize the display window, the update frame rate is becoming slower with fastplotlib, more so than with pqqtgraph.
    Urs

@kushalkolar
Copy link
Member

Nice work! I was taking a look at your repos, are you trying to create something like this https://github.com/uutzinger/SerialUI ? Curious to hear how fastplotlib has been working for your use case.

Yes that is why I am looking into fastplotlib. I hope to have some working prototype with fastplotlib shortly.

I have been trying to figure out what the advantage of fastplotlib might be compared to pyqtgraph which is what I am currently using.

  • It took a while to figure out how to make a "traditional" chart plot with fastplotlib, especially because there are limited examples for Qt and I had to first figure out which window manager configuration on ubuntu works for fastplotlib.

We rely on the community for more detailed Qt examples, thanks for your contribution! We're not a Qt lib like pyqtgraph, wgpu can render to a anything that rendercanvas supports.

  • I made a simple test program for each graphics package that plots more or less the same line plots.
  • So far my conclusion is that fastplotlib does on my old mobile RTX3060 not provide more throughput but it has lower main thread cpu usage hopefully providing more capacity for my other qt threads.
  • Specifically flastplotlib has about 50% main thread cpu usage and pyqtgraph has about 80-90%. The number of line segments per second is slightly lower with fastplotlib compared to pyqtgraph.

How many lines are your plotting and how many points per line? Is your 3060 selected or integrated graphics?

  • It is not trivial to measure throughput with fastplotlib. The time it takes to generate new data, update the line plots with the new data and scheduling a refresh of the canvas is in my case less then10% of the update/loop time but GPU goes up to 75% on NVIDIA usage meter and plotting maxes out.

I'm hoping to make a benchmark for fastplotlib soon, you can take a look at the pygfx benchmarks for now: https://github.com/pygfx/pygfx-benchmarks

  • Also when I resize the display window, the update frame rate is becoming slower with fastplotlib, more so than with pqqtgraph.

I think the rendering is intentionally paused when the canvas is being resized.

@uutzinger
Copy link
Author

Nice work! I was taking a look at your repos, are you trying to create something like this https://github.com/uutzinger/SerialUI ? Curious to hear how fastplotlib has been working for your use case.

Yes that is why I am looking into fastplotlib. I hope to have some working prototype with fastplotlib shortly.
I have been trying to figure out what the advantage of fastplotlib might be compared to pyqtgraph which is what I am currently using.

  • It took a while to figure out how to make a "traditional" chart plot with fastplotlib, especially because there are limited examples for Qt and I had to first figure out which window manager configuration on ubuntu works for fastplotlib.

We rely on the community for more detailed Qt examples, thanks for your contribution! We're not a Qt lib like pyqtgraph, wgpu can render to a anything that rendercanvas supports.

  • I made a simple test program for each graphics package that plots more or less the same line plots.
  • So far my conclusion is that fastplotlib does on my old mobile RTX3060 not provide more throughput but it has lower main thread cpu usage hopefully providing more capacity for my other qt threads.
  • Specifically flastplotlib has about 50% main thread cpu usage and pyqtgraph has about 80-90%. The number of line segments per second is slightly lower with fastplotlib compared to pyqtgraph.

How many lines are your plotting and how many points per line? Is your 3060 selected or integrated graphics?

  • It is not trivial to measure throughput with fastplotlib. The time it takes to generate new data, update the line plots with the new data and scheduling a refresh of the canvas is in my case less then10% of the update/loop time but GPU goes up to 75% on NVIDIA usage meter and plotting maxes out.

I'm hoping to make a benchmark for fastplotlib soon, you can take a look at the pygfx benchmarks for now: https://github.com/pygfx/pygfx-benchmarks

  • Also when I resize the display window, the update frame rate is becoming slower with fastplotlib, more so than with pqqtgraph.

I think the rendering is intentionally paused when the canvas is being resized.

Test Programs

3 x 50_000 line segments per frame
normal window size 800x600
full screen 2500x1400
PyQt6 Window
Generating = per cent of loop spent generating data

test_lineplot.zip

Hardware Options

  • NVIDIA 3060 notebook
  • Radeon, AMD Renoir (part of AMD Ryzen)
  • AMD Ryzen 7 4800H (used for CPU rendering)
  • OpenGL as provided by OS

Running on Ubuntu

FASTPLOTLIB Performance

Nividia 3060 Vulkan

Lines/s: 10,215,192, Interval: 1.011 Generating: 4.1% FPS: 63.40 Python Main Thread usage: 60.3%
Lines/s: 11,411,716, Interval: 1.022 Generating: 4.9% FPS: 71.35 Python Main Thread usage: 65.3%
Lines/s: 10,975,969, Interval: 1.007 Generating: 5.0% FPS: 66.59 Python Main Thread usage: 72.5%

fullscreen

Lines/s: 5,058,138, Interval: 1.048 Generating: 3.8% FPS: 31.27 Python Main Thread usage: 48.7%
Lines/s: 4,843,211, Interval: 1.032 Generating: 4.0% FPS: 29.50 Python Main Thread usage: 47.8%
Lines/s: 5,014,156, Interval: 1.057 Generating: 3.8% FPS: 30.68 Python Main Thread usage: 47.6%

Radeon Vulkan

Lines/s: 11,278,138, Interval: 1.007 Generating: 4.9% FPS: 75.57 Python Main Thread usage: 75.6%
Lines/s: 11,457,797, Interval: 1.022 Generating: 5.2% FPS: 67.99 Python Main Thread usage: 77.8%
Lines/s: 13,151,688, Interval: 1.010 Generating: 5.1% FPS: 81.19 Python Main Thread usage: 79.8%

fullscreen

Lines/s: 5,571,927, Interval: 1.055 Generating: 3.0% FPS: 34.26 Python Main Thread usage: 49.8%
Lines/s: 5,609,378, Interval: 1.051 Generating: 3.4% FPS: 34.76 Python Main Thread usage: 48.9%
Lines/s: 5,604,300, Interval: 1.001 Generating: 3.8% FPS: 37.45 Python Main Thread usage: 50.6%

CPU/LLVM Vulkan

Lines/s: 350,917, Interval: 1.715 Generating: 0.3% FPS: 2.33 Python Main Thread usage: 4.1%
Lines/s: 359,257, Interval: 1.673 Generating: 0.2% FPS: 2.32 Python Main Thread usage: 3.7%
Lines/s: 370,106, Interval: 1.624 Generating: 0.2% FPS: 2.32 Python Main Thread usage: 3.3%

fullscreen

Lines/s: 540,940, Interval: 1.112 Generating: 0.3% FPS: 3.39 Python Main Thread usage: 6.4%
Lines/s: 532,875, Interval: 1.129 Generating: 0.3% FPS: 3.38 Python Main Thread usage: 6.3%
Lines/s: 533,941, Interval: 1.126 Generating: 0.2% FPS: 3.38 Python Main Thread usage: 6.8%

Radeon OpenGL

Lines/s: 14,919,069, Interval: 1.010 Generating: 6.4% FPS: 88.10 Python Main Thread usage: 86.6%
Lines/s: 14,899,254, Interval: 1.014 Generating: 6.7% FPS: 90.26 Python Main Thread usage: 86.3%
Lines/s: 15,034,213, Interval: 1.004 Generating: 6.6% FPS: 88.11 Python Main Thread usage: 86.9%

fullscreen

Lines/s: 5,379,418, Interval: 1.041 Generating: 3.5% FPS: 32.88 Python Main Thread usage: 49.2%
Lines/s: 5,545,056, Interval: 1.003 Generating: 2.9% FPS: 33.93 Python Main Thread usage: 45.6%
Lines/s: 5,500,725, Interval: 1.009 Generating: 2.7% FPS: 34.04 Python Main Thread usage: 47.8%

PYQTGRAPH Performance

regular window

Lines/s: 6,333,290, Interval: 1.018 Generating: 2.3% FPS: 41.75 Python Main Thread usage: 82.8%
Lines/s: 6,436,724, Interval: 1.002 Generating: 2.3% FPS: 41.08 Python Main Thread usage: 82.9%
Lines/s: 6,183,019, Interval: 1.017 Generating: 2.2% FPS: 39.36 Python Main Thread usage: 81.7%

full screen

Lines/s: 2,195,240, Interval: 1.033 Generating: 0.8% FPS: 14.54 Python Main Thread usage: 72.9%
Lines/s: 2,227,183, Interval: 1.018 Generating: 0.8% FPS: 14.76 Python Main Thread usage: 73.1%
Lines/s: 2,243,544, Interval: 1.011 Generating: 0.8% FPS: 14.83 Python Main Thread usage: 73.1%

@kushalkolar
Copy link
Member

Thanks for the example, we're a bit busy with other projects at the moment so I'll incorporate your changes next month.

@uutzinger
Copy link
Author

legend.py
I proposed previously some changes the legend.py so that I can specify background color and I added a function so that the legend adjusts with custom update_using_camera call. In attached file I also added option to change text color for the legend. There were some recent changes made to enable pygfx 0.13.0 and I included these changes in the attached legend,py also but since I run release v0.5.1 version, I used try/except blocks so that it works on latest updates on github as well as 0.5.1. I believe you wanted to wait for some other changes to occur first before considering my suggestions. It would help me to be able to specify to background color of the legend and the color of the legend text. Also a function that works like my proposed update_using_camera would be helpful. If you like me to try a different approach to contribute to legend code let me know please.

@kushalkolar
Copy link
Member

Hi sorry I haven't had time to follow up on this. We're pretty busy with other low level priorities at the moment.

If you'd like to contribute to a fully-fledged legend we outlined our ideas here, some of the ideas do need to be updated w.r.t. recent improvement in pygfx & fpl: #403

Legends should go in the overlay render pass that was introduced in #830, so updating with camera changes won't be necessary.

I'm not sure if we still want to do a separate colorbar when colormaps are used for lines, it may make sense to derive something from the existing HistogramLUTTool.

@uutzinger
Copy link
Author

This is as far as I got with fastplotlib in my charting app: Recording made for this response (on youtube) The legend placing could be improved but runs with latest fpl from github.

The way it looks like with pyqtgraph can be seen in the app repo https://github.com/uutzinger/SerialUI.

The updated legends.py I use is in https://github.com/uutzinger/SerialUI/tree/main/Python_libraries/fastplotlib/legends
The description in basics section items in #403 work.
I did not work on colorbar for lines.

The charting in my application works similar to Electromagnetic Wave Animation in your Examples.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants