#pyqtgraph

1 messages · Page 2 of 1

fervent vale
#

for uint64, you are generating numbers that would lose precision when converted to float64

#

so the numpy warnings are valid in warning that we are doing something dubious

#

btw, the ascii coord graphic for NonUniformImage isn't right. The code takes midpts of the prev and curr index

#

border cells are handled specially

rough furnace
#

🤦

#

thanks for checking

fervent vale
#

it's a pretty specific piece of code

#

that may not fit a general use-case...

rough furnace
#

(I couldn't sleep last night, woke up super early and decided to iron out some minor stuff, there's a reason I left that PR in draft mode 😛 )

#

I'm considering changing it.... it's been in the library for a bit, but not documented; so I'm feeling ok with the idea of changing the API for it

fervent vale
#

in fact, it could even be implemented in terms of the new BarGraphItem...

rough furnace
#

(which would have the added bonus of the bounding rect calculations sorted out)

fervent vale
#

the tricky issue is that NonUniformImageItem permits NaNs and Infs in the image data

#

and it even has test images to compare against...

pseudo yarrow
#

Hi again guys. Does anyone know what happened to pyqtgraph/pyqtgraph#2323 ? It seems kind of abandoned (and unfortunately a bit outdated already), but the concept that is being implemented would be incredibly valuable for a visualization-library like pyqtgraph. Is there anything I can do to help revive that PR?

supple leafBOT
rough furnace
#

Tagging @torn coyote for #2323

rough furnace
pseudo yarrow
rough furnace
#

I don't think it's a problem w/ the timeout explicitely, ... for some reason in CI, the screenshot generation is really really slow if I remember right

#

migrating away from RTD CI for documentation building is not in the cards; RTD works really well for us with very minimal configuration... worst case scenario we just store images in the docs and try to remember to update them periodically (Which is what I believe VisPy docs do, and their docs/examples look amazing lemon_hearteyes )

pseudo yarrow
#

Yeah, that sounds like a possible alternative. Automatically compiled examples (and tutorials) would really push pyqtgraph to the next level in terms of user friendliness though.

rough furnace
#

i think we can get "live" tutorials in the web with jupyter-lite

#

https://jupyterlite.readthedocs.io/en/latest/

this is the holy grail, we have jupyter support working on binder; there are some exceptions (context menu dialogs and such)... I think there is some issue with capturing the entire window (like I think some of the right side are trimmed out) ... also not sure how responsive it would be

#

but a lot of the examples would need to be converted to their jupyter compatible equivalents, which I'm not crazy on the idea.

I'm hoping that once webassmebly/pyodine support rolls out for the Qt bindings, we can have better web integration

pseudo yarrow
#

That does indeed look cool, but static example-pages (with images) would be an incredible leap in its own right.

#

Do we have any idea which part of the CI build is so slow? 7% in 1800 seconds seems horrible 😅

rough furnace
#

@torn coyote would likely know better than I would...

fossil owl
#

Hwat?

pseudo yarrow
#

Their scraper does not look particularly fast though. For QtWidgets they are forcing 2*1.5 second timeouts for each screengrab. But maybe only a small part of their codebase uses QtWidgets?

#

Oh, their execution-time pages are online, so we can check how fast it runs.

rough furnace
#

would certainly love help on that PR ... if you can identify a change to make things better, I can cherry-pick a commit from your fork onto that branch

forest cliff
#

Is NonUniform image really slow or am I doing something wrong?

#

Even at 100 x 100 it slows down to a crawl

rough furnace
#

100x100 would likely be pretty brutal, that's drawing 10k rectangles, and we don't have drawRects implemented, so it's drawing one rectangle at a time. That GraphicsItem hasn't gotten much attention (primarily due to how undocumented it is 😆 ) so I wouldn't be surprised if it's not performing sufficiently well.

If we can group rectangles by color, and you have a lot of rectangles that use the same color, there would likely be substantial performance improvements available (but we would need to change the paint method of NonUniformImage to account for that)

fervent vale
#

You could try using PcolorMeshItem instead

#

Or even the BarGraphItem from #2596

rough furnace
#

pijyoi lobbying to kill NonUniformImage 😆 (note, I'm open to the idea of killing it)

@forest cliff I would give PColorMeshItem a shot and see how that works.

forest cliff
#

Fully fine with that one too

#

Will test it out later. Worst case I will fall back to matplotlib for that

#

btw thanks for all the good maintance work your guys did to pyqtgraph

#

Btw if a class has the name "image" in it and is only useful up to 50x50 pixel killing it may be the correct thing to do.👍

fervent vale
mortal grotto
rough furnace
#

second question concern, I'm down to submit, but due to timing, and a potential major geographic move for me, I can't lead an effort on this...

mortal grotto
#

second question concern I m down to

fervent vale
#

I am using the hoverEvent to print out the cursor values of an ImageItem, following the example of imageAnalysis.py. The pos returned from the event seems to be off by half a pixel

fervent vale
#

To answer my own question, the pos is untransformed. To convert to integer indices, one has to do integer truncation of the pos (as was done in the example)

rough furnace
#

@obsidian sapphire has done a bunch of work/examination on pixel/mouse position offsets.

copper lily
#

good job guys

hearty glade
#

hello,

has anyone managed to get a pyqtgraph 3D scatter plot to update based on the output of a for loop. I have made my own structure from motion system and I would like to plot the 3D points on a 3D pyqtgraph plot after each iteration of the for loop. Any help would be greatly appreciated

fervent vale
#

It might be more useful if you could tell us what issue you are encountering in your implementation.

quartz shale
#

all i wanted to let u guys know is u guys missed a beautiful opportunity to name this project qtpygraph which is like a shorthand for cutiepie, lol

mortal grotto
quartz shale
twin spire
rough furnace
#

A pyside maintainer has entered chat!

#

The plug-in was created by our very own @torn coyote

Do you happen to have the script handy/viewable?

pseudo yarrow
rough furnace
twin spire
#

I'm certain it could be better 😛 but at least does the job.
The script generate the .rst files that we later include in the docs.

rough furnace
pseudo yarrow
half spoke
#

000000000000000000000000000000000000000030

rough furnace
pseudo yarrow
# rough furnace We install the necessary drivers on GitHub actions to make it work, I think we i...

Yeah, that sounds reasonable. I'll share some more details on the OpenGL issues regardless, as I'm by the computer I did the testing on. In my headless ubuntu setup (WSL) I've managed to replicate what I assume is the behavior described by @torn coyote in the PR. Most of the OpenGL examples actually work, the exceptions are (as far as I can tell, due to very obvious misrenderings):

  • GLBarGraphItem
  • GLGraphItem
  • GLIsosurface
  • GLScatterPlotItem
  • GLSurfacePlot

In my setup It's a bit hard to tell which exact programs throw the GL errors because the sphinx-build output overwrites some of the lines as they are written, but at least some of these seem to correlate with the errors

hasty fern
#

how would I plot the time on the x axis ?

fervent vale
hasty fern
#

will it not show the time on the x axis tho?

fervent vale
#

At least for me, the plot shows years on the x-axis. And if I zoom in on the right hand side, it starts showing time

static drum
#

Hey everyone! I'm probably approaching the limits of Pyqtgraph in terms of speed in creating new plot items but I would like to hear from more expert people on the matter.
I have got a 2D Numpy array with shape 20x9000, my intention is to be able to allow the user to plot both 20 lines of 9000 points each or 9000 lines of 20 points each.
I am working with PySide 6.4 and I have promoted a QGraphicsView object to PlotWidget as suggested in the official documentation. I add lines to the plot in a for loop as such:

d = np.array(data) # Array that contains the data to be plotted
x = np.array(range(data.shape[1]) # Index for each line
for i in range(data.shape[0]):
    plot_widget.plot(x, d[i,:])

The following code works but as I said the 9000x20 case takes a lot to be generated and even removing all the lines with plot_widget.clean() takes almost as much as creating the plot in the first place.
Is there any way workaround that does not require subsampling?

static drum
fervent vale
#

For 9000x20, you would use the connect="finite" parameter of PlotCurveItem

#

The example there uses connect=ndarray instead of connect="finite"

rough furnace
#

connect=‘finite’ is most certainly the way to do it.

#

@fervent vale thanks for the PR on the segfault. I saw CI was failing but didn’t get to look more closely.

fervent vale
#

There's also something fishy about test_imageItem.py::test_dividebyzero()

#

"-5+25" was probably meant to be "-5e+25"

rough furnace
#

Ugh, yes that would make more sense!

#

I come home from vacation later today; but I will have driven forever so ill likely be useless. If I’m not too beat, ill look at merging PRs in hopefully 16 hours or so.

static drum
# fervent vale https://github.com/pyqtgraph/pyqtgraph/blob/master/pyqtgraph/examples/multiplePl...

Thanks for the pointer! connect=finite returns the correct plot using the following code but end-points "loop around".

    def updatePlot(self) -> None:
        plot = self.graphicsView.plotItem
        plot.enableAutoRange(False)
        plot.clear()
        d = self.data.to_numpy()
        x = np.arange(d.shape[1])
        shape = (d.shape[0], d.shape[1])
        xdata = np.tile(x, shape[0])
        ydata = d
        item = pg.PlotCurveItem()
        item.setData(xdata.reshape(-1), ydata.reshape(-1), connect="finite")
        plot.addItem(item)
#

Using a connection matrix like the one in the example returns the correct result:

    def updatePlot(self) -> None:
        plot = self.graphicsView.plotItem
        plot.enableAutoRange(False)
        plot.clear()
        d = self.data.to_numpy()
        x = np.arange(d.shape[1])
        shape = (d.shape[0], d.shape[1])
        xdata = np.tile(x, shape[0])
        ydata = d
        conn = np.ones(shape, dtype=bool)
        conn[...,-1] = False # make sure plots are disconnected
        item = pg.PlotCurveItem()
        item.setData(xdata.reshape(-1), ydata.reshape(-1), connect=conn.reshape(-1))
        plot.addItem(item)
#

I guess that working with a single PlotCurveItem() it is not possible to have lines having different colors, am I correct?

#

I must admit I didn't expect to hit the limitations of the library this soon 🥲
I am in the midst of porting a Matlab toolbox to Python and while still slow the same plot is obtained with varying colours in a matter of seconds. What is the bottleneck here?

mortal grotto
#

Does your data have nan entries to indicate where the lines split? connect="finite" creates a line break when encountering nan

static drum
rough furnace
#

Phil just emailed me that the sip.array fixes will be in the next snapshot

mortal grotto
#

connect="finite" relies on nan being present (finite corresponds to np.isfinite checks), but passing a connection matrix explicitly indicates the breaks. That's why it works in the second case

#
import pyqtgraph as pg
import numpy as np


def generate_data(shape: tuple[int, int]) -> np.ndarray:
    """monotonically increasing data similar to your screenshot"""
    data = np.linspace(0, 1, shape[1])[None, :] + np.abs(np.random.normal(size=shape))
    return np.cumsum(data, axis=1)

def pad_with_nan(data: np.ndarray) -> np.ndarray:
    """add nan values to the end of each row of data to indicate split locations"""
    return np.column_stack([data, np.full((data.shape[0], 1), np.nan)])


def update(curve):
    data = generate_data((9000, 20))
    data = pad_with_nan(data)
    x = np.tile(np.arange(data.shape[1]), (data.shape[0], 1))
    curve.setData(x.flatten(), data.flatten())

def main():
    pg.mkQApp()
    plot = pg.PlotWidget()
    curve = pg.PlotCurveItem()
    plot.addItem(curve)
    plot.show()
    timer = pg.QtCore.QTimer()
    timer.timeout.connect(lambda: update(curve))
    timer.start(1000)
    pg.exec()

if __name__ == "__main__":
    main()

Seems to update very quickly for me

static drum
mortal grotto
#

You are correct, you'll need several lines. However, it is also possible to abuse the ScatterPlotItem for this purpose 🙂 Note that you will have to adjust some of the logic for bounding rect calculation if you use this

import pyqtgraph as pg
from pyqtgraph.Qt import QtGui, QtCore
from pyqtgraph import functions as fn
import numpy as np


def generate_data(shape: tuple[int, int]) -> np.ndarray:
    """monotonically increasing data similar to your screenshot"""
    data = np.linspace(0, 1, shape[1])[None, :] + np.abs(np.random.normal(size=shape))
    return np.cumsum(data, axis=1)


def pad_with_nan(data: np.ndarray) -> np.ndarray:
    """add nan values to the end of each row of data to indicate split locations"""
    return np.column_stack([data, np.full((data.shape[0], 1), np.nan)])


def line_to_symbol(
    x: np.ndarray, y: np.ndarray
) -> tuple[QtGui.QPainterPath, tuple[float, float]]:
    position = x.min() + x.ptp() / 2, y.min() + y.ptp() / 2
    x = x - position[0]
    y = y - position[1]
    return fn.arrayToQPath(x, y), position


def update(scatter: pg.ScatterPlotItem):
    data = generate_data((9000, 20))
    # data = pad_with_nan(data)
    x = np.arange(data.shape[1])
    cmap = pg.colormap.get("viridis")
    symbols, positions = zip(*[line_to_symbol(x, y) for y in data])
    positions = np.array(positions).T
    colors = cmap.map(np.linspace(0, 1, data.shape[0]))
    scatter.setData(
        *positions,
        symbol=symbols,
        brush=None,
        pen=colors,
        size=1,
        pxMode=False,
    )


def main():
    pg.mkQApp()
    plot = pg.PlotWidget()
    scatter = pg.ScatterPlotItem()
    # Draw each curve as a separate color using a stepwise gradient

    plot.addItem(scatter)
    plot.show()
    timer = QtCore.QTimer()
    timer.timeout.connect(lambda: update(scatter))
    timer.start(3000)
    # timer.setSingleShot(True)
    pg.exec()


if __name__ == "__main__":
    main()
static drum
hasty fern
#

when I move the graph from one screen to another with different scaling the graph doesnt seem to correct itself. any way to fix that?

fervent vale
#

I have tried moving plots between screens of dpi 1 and 2 before, and it does work. Could you share more details of your setup? OS, binding version, pyqtgraph version, code snippet. Does the issue occur with one of the bundled pyqtgraph examples?

hasty fern
#

this is on my laptop screen running at 1080 p scale at 100 %

#

when I move it to a 1440p screen with 200 % scaling this is what it does.

#

when I use the mouse to change the graphs scale it's still not starting at 0 which it should have.

fervent vale
#

You seem to be using BarGraphItem. Could you provide the full runnable source code?

static drum
#

Regarding adjusting the logic of bounding rect calculation, to avoid having the lines disappears when moving the middle of the graph out of the viewport, I resorted to manually settings self.bounds after setting the data and avoiding to reset them when viewTransformChanged() is called for the ScatterPlotItem object.
First part was quite easy, for the second I had to resort to subclass ScatterPlotItem altogether, I don't know if there's a better way for it.
Here is your example adapted to what I just said:

#
import pyqtgraph as pg
from pyqtgraph.Qt import QtGui
from pyqtgraph import functions as fn
import numpy as np


def generate_data(shape: tuple[int, int]) -> np.ndarray:
    """monotonically increasing data similar to your screenshot"""
    data = np.linspace(0, 1, shape[1])[None, :] + np.abs(np.random.normal(size=shape))
    return np.cumsum(data, axis=1)


def line_to_symbol(
    x: np.ndarray, y: np.ndarray
) -> tuple[QtGui.QPainterPath, tuple[float, float]]:
    position = x.min() + x.ptp() / 2, y.min() + y.ptp() / 2
    x = x - position[0]
    y = y - position[1]
    return fn.arrayToQPath(x, y), position


class FastPlot(pg.ScatterPlotItem):
    def viewTransformChanged(self):
        self.prepareGeometryChange()
        pg.GraphicsObject.viewTransformChanged(self)


def update(scatter: FastPlot):
    data = generate_data((9000, 20))
    # data = pad_with_nan(data)
    x = np.arange(data.shape[1])
    cmap = pg.colormap.get("viridis")
    symbols, positions = zip(*[line_to_symbol(x, y) for y in data])
    positions = np.array(positions).T
    colors = cmap.map(np.linspace(0, 1, data.shape[0]))
    scatter.setData(
        *positions,
        symbol=symbols,
        brush=None,
        pen=colors,
        size=1,
        pxMode=False,
    )
    scatter.bounds = [(0, len(x)-1), (data.min(), data.max())]


def main():
    pg.mkQApp()
    plot = pg.PlotWidget()
    scatter = FastPlot()
    # Draw each curve as a separate color using a stepwise gradient
    plot.addItem(scatter)
    plot.show()
    update(scatter)
    bound_rect = scatter.boundingRect()
    plot.setLimits(xMin=bound_rect.left(), xMax=bound_rect.right(), yMin=bound_rect.top(), yMax=bound_rect.bottom())
    pg.exec()


if __name__ == "__main__":
    main()
#

(I had to split in 2 messages because of message size restriction)

fervent vale
#
class FastPlot(pg.ScatterPlotItem):
    def dataBounds(self, ax, frac=1.0, orthoRange=None):
        if hasattr(self, '_realbounds'):
            return self._realbounds[ax]
        else:
            return super().dataBounds(ax, frac, orthoRange)

def update(scatter: FastPlot):
    # ... snip ...
    scatter._realbounds = [(x.min(), x.max()), (data.min(), data.max())]
#

And remove the "plot.setLimits" in your main function

tribal moth
#

wow

fervent vale
#

There's this code fragment in ScatterPlotItem:

w, pw = max(itertools.chain([(self._maxSpotWidth, self._maxSpotPxWidth)], self._measureSpotSizes(**kwargs)))

It doesn't seem to be calculating what I think it wants to do because:

In [7]: max(itertools.chain([(1,5)], [(5,3), (6,2)]))
Out[7]: (6, 2)

i.e. the comparison is at a whole tuple level

#

I think the code fragment would have intended the result to be (6,5) ?

fervent vale
#

A more "elegant" solution to the ScatterPlotItem bounding box would have been to add two invisible points:

    scatter.addPoints(
        [x.min(), x.max()],
        [data.min(), data.max()],
        symbol=['s', 's'],
        pen=pg.mkPen(None)
    )
rough furnace
#

I'm not that versed with the scatter plot stuff, I'll have to take a look, but yeah if the intended result is (6, 5) then using max(itertools.chain()) is definitely not going to give the "correct" result.

fervent vale
#

Unless the logic was to find the largest spot and its associated pix width

fervent vale
#

The comment for _maxSpotPxWidth says "maximum size of the scale invariant portion of all spots"; so it's independent of _maxSpotWidth

rough furnace
#

I'm in DLL hell, and I hate everything about computers right now

#

(completely off topic for pyqtgraph, just venting after spending the better part of a day on this)

rough furnace
#

With depends.exe I managed to trace down the cause, libxml2

cobalt seal
#

do i perceive correctly that that took 2 hours to track down?

rough furnace
# cobalt seal do i perceive correctly that that took 2 hours to track down?

Oh this issue has taken me a lot longer than 2 hours to track down. This was a day long project haha

JUST KIDDING issue was only fixed when I started python from a particular directory, which happened to be where I was testing from, starting python elsewhere didn't work still; and yes, that directory is in my %PATH% 🙃

rough furnace
#

ok, I don't want to hold up the new release until I can sort out #2418 any more... (same w/ Non-Uniform Image docs, I can sort that out later)

Any other PRs/issues that should be addressed before the next release?

rough furnace
#

Assuming I am awake after I put the kids to bed, I’ll start on the changelog for the next release

rough furnace
#

ugh, when the changelog is so long it's annoying to arrange, clearly too long went by before a release was done 😆

rough furnace
#

14 first time contributors between 0.13.1 and 0.13.2

fervent vale
#

The immediate next commit would be to bump the version to 0.13.3.dev0?

rough furnace
#

Yeah forgot to do that; will do that shortly (now done)

rough furnace
#

thanks for keeping me honest 😄

fervent vale
#

It should be 0.13.3.dev0 not 0.13.2.dev0

#

According to the convention set by #2526

rough furnace
#

Lmao, you’re right

#

Why am I so bad at this

half jewel
#

I haven't even contributed a PR to black in like two months, cut yourself some slack!

rough furnace
#

haha, there is clearly a historical pattern here occurring, I've noticed my OSS contribution rate slows down from mid November - February going to have to keep this in mind as we approach the end of the year and recognize that this is just something that happens.

#

@fervent vale good looking out, fixed now

#

seriously tho, my daughter napping at daycare is absolutely killing my evening time... she's old enough that she can leave her room... and since she naps she's wide awake until later in the evening than I am ... I know it's not going to be like this forever

copper lily
#

I have a question. I want to monitor several sensors in a PyQt GUI, but I don't know what the best way to do this is.

Initially, I used QRunnable and read some articles that suggested it may not be the best approach for 24/24 7/7 365/365 monitoring.

Next, I created a QThread class to monitor each device. Do you have any suggestions for the best approach? Thanks

fervent vale
#

The general way would be for each QThread to emit a signal containing the acquired sensor data and the GUI thread would connect to it and then display it

#

There's a long thread in github pyqtgraph discussions #1745 that may give you some ideas

keen crater
#

Hello, guys!
I'm working on a project to show data in 3d using pyqtgraph and opengl. And now I have a problem that I need to realise a mouse click on the object and get information about this object. I understood that I need to use a rayCasting method, and I got a QVector3D from the screen. But I don't understand what should I do next, how can I find an intersection with other objects? Do your library have a solution to find an object under click in 3d? I tried to use itemsAt, but it doesn't work for me, this method just returned to me all objects.

keen crater
#

I also find this code:

        # Example item selection code:
        #region = (ev.pos().x()-5, ev.pos().y()-5, 10, 10)
        #print(self.itemsAt(region))
        
        ## debugging code: draw the picking region
        #glViewport(*self.getViewport())
        #glClear( GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT )
        #region = (region[0], self.height()-(region[1]+region[3]), region[2], region[3])
        #self.paintGL(region=region)
        #self.swapBuffers()

It is a code in GLViewWidget.py. But we don't have a swapBuffers function in GLViewWidget. How can I change it to draw the picking region?

rough furnace
rough furnace
fervent vale
#

From a read of the docs for QOpenGLWidget, to do painting (or any OpenGL calls) outside of a automatic framework call to paintGL(), first you need to call makeCurrent().

#

swapBuffers() is a method of QOpenGLContext, an instance of which can be obtained with self.context()

#

So it may be possible to update the commented out code from QGLWidget code to QOpenGLWidget code.

fervent vale
#

I found an old testing script for itemAt()

import pyqtgraph as pg
import pyqtgraph.opengl as gl
import numpy as np

class MyGLV(gl.GLViewWidget):
    def mouseReleaseEvent(self, ev):
        lpos = ev.position() if hasattr(ev, 'position') else ev.localPos()
        region = [lpos.x()-5, lpos.y()-5, 10, 10]
        # itemsAt seems to take in device pixels
        dpr = self.devicePixelRatio()
        region = tuple([x * dpr for x in region])
        for item in self.itemsAt(region):
            print(item.objectName())

pg.mkQApp()

glv = MyGLV()
glv.setCameraParams(elevation=90, azimuth=-90, distance=50)
# X points right, Y points up
glv.show()

side = 8
names = ['red', 'green', 'blue']
for idx, name in enumerate(names):
    box = np.zeros((side, side, 4), dtype=np.uint8)
    box[..., [idx, 3]] = 255
    img = gl.GLImageItem(box)
    img.setObjectName(name)
    img.translate(-side/2, -side/2, 0)  # center the box
    img.translate((idx-1)*side*2, (idx-1)*side*1, 0)
    glv.addItem(img)

pg.exec()
#

It no longer works correctly since Qt 6.4 though

#

So it seems like Qt 6.4 has changed something about OpenGL. We even had a PR #2546 to fix OpenGL for Qt 6.4

rough furnace
#

Well, that’s annoying!

keen crater
fervent vale
#

I have some working swapBuffers code in #2650

#

But it will require you to patch your pyqtgraph to add a GLViewWindow

willow cypress
#

!voice

runic umbraBOT
#
Voice verification

Can’t talk in voice chat? Check out #voice-verification to get access. The criteria for verifying are specified there.

fervent vale
#

One key difference between QOpenGLWidget and QOpenGLWindow is that the former undergoes an extra composition step.

#

The latter has behaviour closer to QGLWidget which GLViewWidget was originally based off

#

So some of the "less-used" functionality doesn't work with QOpenGLWidget

#

I would hazard a guess that most users are fine with QOpenGLWidget

obsidian obsidian
#

!

fervent vale
#

In #2650, there's a patch that refactors GLViewWidget into GLViewMixin, with the aim of allowing users to swap QOpenGLWindow for QOpenGLWidget. Would that be useful to merge into pyqtgraph (without merging the GLViewWindow bit). Those power users who really need GLViewWindow can write it directly in their own code.

rough furnace
#

I have a hard time assessing utility in that part of the library, but sounds like this has come up periodically so probably isn't a bad idea

copper lily
fervent vale
copper lily
#

Yes

rough furnace
copper lily
fervent vale
#

#2164 has a more instructive example where setting the QObject parent correctly becomes important. I always use moveToThread rather than inherit from QThread. The latter is a Java pattern, I think.

rough furnace
#

the qthread functionality on pyside2 was quite broken until 5.14.x if memory serves... maybe my reluctance to using .moveToThread() was due to pyside bindings behaving unexpectedly ...in my (simple) use-cases I've done just fine via subclassing... next time I give it another go I'll try and do without subclassing again

rough furnace
#

wondering if there are good places in the library to introduce the use of QThreads; ...maybe the IsoCurveItem, I think that guy takes some extra processing steps

fervent vale
#

The default implementation of QThread::run() executes an event loop. I want to make use of that event loop, so I don't subclass and override run().

#

If you want to send messages to the thread, then you need the event loop running

rough furnace
runic umbraBOT
#

src/barney/controllers/AudiotagInterface.py line 26

class AudiotagCommunicationThread(QThread):```
rough furnace
#

(been a while since I worked w/ this code-base, and this is a slimmed down variant of what I worked on internally at a previous company)

#

another way of me saying please don't judge me too harshly 😄

fervent vale
#

In order to receive queued connection signals, you need the event loop

#

If not, your slots are actually being executed by the caller thread

#

Because you override run() with an empty function, there's no event loop

#

Or put another way, if you connect queued connection to your slots, they won't get fired

rough furnace
#

ahh, I rarely use Queued connections, that's probably why I never noticed

fervent vale
#

But if your slots are being executed by the caller thread, then there was no need to use a QThread...

rough furnace
#

hmm... pretty sure they were not being executed by the caller thread in this case, as the sql write command could take a while to write in this case. Before I migrated to a QThread, that would result in the GUI hanging... but after migrating to a subclassed QThread the GUI was not impacted further

fervent vale
#

"It is important to remember that a QThread instance lives in the old thread that instantiated it, not in the new thread that calls run(). This means that all of QThread's queued slots and invoked methods will execute in the old thread. Thus, a developer who wishes to invoke slots in the new thread must use the worker-object approach; new slots should not be implemented directly into a subclassed QThread."

#

"...the thread will exit after the run function has returned. There will not be any event loop running in the thread unless you call exec()."

fervent vale
#
from PySide6 import QtCore

class MyThread(QtCore.QThread):
    def __init__(self):
        super().__init__()
        tid = QtCore.QThread.currentThread()
        print('init called by', tid)

    def direct_slot(self, msg):
        tid = QtCore.QThread.currentThread()
        print('direct slot called by', tid, msg)

    def queued_slot(self, msg):
        tid = QtCore.QThread.currentThread()
        print('queued slot called by', tid, msg)

    def run(self):
        pass

class Sender(QtCore.QObject):
    signal = QtCore.Signal(object)

app = QtCore.QCoreApplication([])
tid = QtCore.QThread.currentThread()
print('main thread is', tid)

thr = MyThread()
print('child thread is', thr)

sig = Sender()
sig.signal.connect(thr.direct_slot, QtCore.Qt.ConnectionType.DirectConnection)
sig.signal.connect(thr.queued_slot, QtCore.Qt.ConnectionType.QueuedConnection)
thr.start()

sig.signal.emit('MSG1')

QtCore.QTimer.singleShot(100, lambda : sig.signal.emit("MSG2"))
QtCore.QTimer.singleShot(500, app.exit)

print('before exec')
app.exec()
print('after exec')
thr.wait()
#

both direct_slot and queued_slot are executed by the main thread's event loop

rough furnace
#

Hmm…. That app Barney definitely does the spectrogram calculation on the non-gui thread; curious what specifically makes it work.

fervent vale
#

In the example above, the slot is called by the caller thread, which may not necessarily be the gui thread

#

The callee thread above was useless

fervent vale
#

Anything making use of GraphicsWidgetAnchor will fail

fervent vale
#

Hmm, it seems that it is an implementation bug of pyqtgraph that is being exposed by this PySide6 6.5 change

copper lily
rough furnace
#

@fervent vale nice catch on the mixin issue coming up in pyside 6.5

fervent vale
#

Doing a regex search, RHS inheritance still exists in the pyoptic and the DateAxisItem_QtDesigner examples. For the former, it's clear there would be double init. For the latter, it's hard to say what would be the right way, especially if it is supposed to be following C++ Qt syntax.

fervent vale
#

It also seems like LabelItem is needlessly inheriting from GraphicsWidgetAnchor

rough furnace
#

It’s amazing the number of issues you can find by just taking a closer look at just about any class in the library haha

fervent vale
#

interestingly, it was a commit exactly 10 years ago to the day (2013/03/26) that added GraphicsWidgetAnchor to LabelItem (w/o making use of any of the former's functionality)

#

The merge comments say "Made LabelItem a subclass of GraphicsWidgetAnchor", so it was definitely intentional

rough furnace
#

Wonder if it was needed in Qt4 or something

runic umbraBOT
#

Hey @copper lily!

You either uploaded a .txt file or entered a message that was too long. Please use our paste bin instead.

copper lily
#

https://paste.pythondiscord.com/abemefufam

I tried to code with your help and i have several problems. When i put three QDockWidget in my mainWindow it was impossible to start all the graphs. Do you have an idea why this behaviours occurs ? thanks @fervent vale

fervent vale
#

Is the use of QDockWidget important to the concept of using QThread?

#

Okay, so I played around with your script a bit

copper lily
#

I don’t know, it is more convinient for the gui.

fervent vale
#

and it hangs when trying to start all 3 plots

pearl prairie
#

def handleGraphPlot(self):
print (self.strain)
self.strain = [self.strain]
print (self.stress)
self.stress = [self.stress]
self._svs.plot(self.strain, self.stress, pen=pg.mkPen('r', width=2), clear=True)
'''self._lvd.plot(self.ch2, self.ch1, pen=pg.mkPen('r', width=2), clear=True)
self._lvt.plot(self.timeWindow, self.ch1, pen=pg.mkPen('r', width=2), clear=True)
self._dvt.plot(self.timeWindow, self.ch2, pen=pg.mkPen('r', width=2), clear=True)

self._svs.plot([self.ch4], [self.ch3], pen=pg.mkPen('r', width=2), clear=True)'''

    self._svs.show()
    self.log_data()  self._svs.plot(self.strain, self.stress, pen=pg.mkPen('r', width=2), clear=True) this graph is visible autoscalling but not ploting pls someone help
rough furnace
#

Why clear=True?

rough furnace
#

pijyoi, what a weird crash on the widgetgroup PR you created... can't help but wonder if the cause of the segfault is something unrelated and the change you made is exacerbating the underlying issue; .... not sure how to determine what the underlying issue would be tho

twin spire
#

@rough furnace we just released PySide 6.5.0 let me know if you found some issues once you have some time to play with it and pyqtgraph 🙂

rough furnace
#

@fervent vale was kind enough to do testing pre-release (congratulations on the release by the way)

#

there is no non-pyqtgraph specific example yet, but the pyqtgraph code here is fairly minimal

twin spire
#

Cool, then we can take a look 😄
(and thanks for that @fervent vale 🎉 )

rough furnace
#

Thanks, we would have opened an issue in the bug tracker, but as we don't have a pyside only script going, I didn't want to post there ...

Congratulations on the release and I really appreciate you reaching out directly @twin spire

twin spire
#

hehe I really didn't like gitter, so I was really happy when I found you here 🙂

rough furnace
#

yeah, I low key hate it

#

I'm surprised you're not on the (admittedly low-traffic) Qt discord server.

#

oh wait:

rough furnace
#

@twin spire I just triggered a CI run with pyside6 6.5, we're getting a segfault on this line here:

https://github.com/pyqtgraph/pyqtgraph/blob/master/.github/workflows/main.yml#L123

[14](https://github.com/pyqtgraph/pyqtgraph/actions/runs/4610114611/jobs/8148181442?pr=2596#step:10:15)
qt.qpa.plugin: Could not load the Qt platform plugin "xcb" in "" even though it was found.
[15](https://github.com/pyqtgraph/pyqtgraph/actions/runs/4610114611/jobs/8148181442?pr=2596#step:10:16)
This application failed to start because no Qt platform plugin could be initialized. Reinstalling the application may fix this problem.
[16](https://github.com/pyqtgraph/pyqtgraph/actions/runs/4610114611/jobs/8148181442?pr=2596#step:10:17)

[17](https://github.com/pyqtgraph/pyqtgraph/actions/runs/4610114611/jobs/8148181442?pr=2596#step:10:18)
Available platform plugins are: xcb, eglfs, offscreen, vnc, wayland, minimalegl, linuxfb, vkkhrdisplay, minimal, wayland-egl.
[18](https://github.com/pyqtgraph/pyqtgraph/actions/runs/4610114611/jobs/8148181442?pr=2596#step:10:19)

[19](https://github.com/pyqtgraph/pyqtgraph/actions/runs/4610114611/jobs/8148181442?pr=2596#step:10:20)```

going to do a little more homework here first...
runic umbraBOT
#

.github/workflows/main.yml line 123

xvfb-run --server-args="-screen 0, 1920x1200x24 -ac +extension GLX +render -noreset" python -m pyqtgraph.util.glinfo```
twin spire
rough furnace
#

common fixes historically there have been to install a package via apt. We also had someone open an issue with a PySide 6.5 failure they encountered, but I haven't investigated.

Over the next day or so I'll try and get a concise list ...goes without saying, we should do a little more homework first.

Also I should probably configure our CI to run against pre-release wheels to make testing upcoming releases easier

plush fulcrum
#

matplotlib is also caught by xcb/pyside6.5 interactions

plush fulcrum
rough furnace
#

Not only us!

rough furnace
plush fulcrum
# rough furnace Is there an issue in the mpl repo? Or a PR with a workaround/fix?

Nothing yet, I first noticed it when we had a PR intended to "fix" (by disallowing the current specific version) a different dependency that released yesterday and was causing test failures... when that still caused test failures (which I had been, I saw Qt problems)

I have no fix, can't even run simple pyside-only examples locally

#

The only "workaround" I would have right now is not allowing pyside 6.5...

rough furnace
#

Last time I ran into this. Needed an extra apt package installed; @the-compiler historically has been amazing at identifying fixes for these issues

plush fulcrum
#
import sys
from PySide6.QtWidgets import QApplication, QLabel

app = QApplication(sys.argv)
label = QLabel("Hello World!")
label.show()
app.exec()

Even this fails with:

qt.qpa.plugin: Could not load the Qt platform plugin "xcb" in "" even though it was found.
This application failed to start because no Qt platform plugin could be initialized. Reinstalling the application may fix this problem.

Available platform plugins are: vkkhrdisplay, minimalegl, linuxfb, wayland, offscreen, xcb, vnc, eglfs, minimal, wayland-egl.

Aborted (core dumped)
#

Okay, found it:

Running with QT_DEBUG_PLUGINS=1 python pyside_ex.py

Revealed that it was trying to link to libxcb-cursor.so.0, which was not installed

This was fixed by sudo apt install libxcb-cursor0

#

Specifically this line from that output (Well, and the very similar log right before it):

qt.core.plugin.loader: QLibraryPrivate::loadPlugin failed on "/home/kyle/.pyenv/versions/3.11.0/envs/edge/lib/python3.11/site-packages/PySide6/Qt/plugins/platforms/libqxcb.so" : "Cannot load library /home/kyle/.pyenv/versions/3.11.0/envs/edge/lib/python3.11/site-packages/PySide6/Qt/plugins/platforms/libqxcb.so: (libxcb-cursor.so.0: cannot open shared object file: No such file or directory)"
rough furnace
#

Nice debugging I think I set that variable in our CI

plush fulcrum
fervent vale
#

For the memory leak, there's a lambda function being used as a slot in WidgetGroup and it holds references to self and a widget. I was able to significantly reduce the amount of memory leaked by avoiding the lambda capture. However there is still some memory growth and it doesn't explain why all other bindings including PySide6 6.4.3 have no such issues

rough furnace
#

Nice find … this lambda reference situation sounds familiar, I think @obsidian sapphire ran into this when setting up some signals/slot mechanism with parameter trees but I could be remembering wrong

#

I believe the breakage was lambdas that referenced self (and by breakage I mean reference hold)

obsidian sapphire
#

Yeah, qt does magic to clean up references, but only for fully declared methods.

fervent vale
#

In this case, it's a double reference. lambda *args: self.widgetChanged(w, *args)

fervent vale
#

PySide6 6.5.0 does run out-of-the-box on WSL2 since it uses wayland

fervent vale
#

I have added a pyqtgraph-free example to #2672 that shows a leak only on PySide6 6.5.0.

#

I wonder if it was just a happy accident that it didn't leak for PySide < 6.5.0

rough furnace
#

I'm going to speculate happy accident

#

looks like CI is green, thanks Kyle (already tagged you on github, don't need to tag you here too 😆 )

rough furnace
#

Probably should do another release after we verify PyQt6 6.5 works.

rough furnace
#

looking at those pypy test failures 😬 I'm amazed you're willing to take this on pijyoi, admittedly I'm not sure of the utility here is (but I've never done anything with pypy before so 🤷 )

fervent vale
#

just to see how far it goes... the major hurdle is the lhs mixin inheritance not working

rough furnace
#

PyQt6 6.5 snapshots are available for testing

mortal grotto
fervent vale
#

I would guess this is what is happening in Barney

from PySide6 import QtCore

class SenderThread(QtCore.QThread):
    signal = QtCore.Signal(object)

    def __init__(self):
        super().__init__()

    def send(self, msg):
        self.signal.emit(msg)

class ReceiverThread(QtCore.QThread):
    def __init__(self):
        super().__init__()

    def recv(self, msg):
        print(QtCore.QThread.currentThread(), msg)

    def run(self):
        # thread finishes upon return from run() 
        pass

app = QtCore.QCoreApplication([])
sender = SenderThread()
receiver = ReceiverThread()

print('main    ', QtCore.QThread.currentThread())
print('sender  ', sender)
print('receiver', receiver)

# thread affinity of "receiver" QObject is main thread
assert receiver.thread() == QtCore.QThread.currentThread()

sender.signal.connect(receiver.recv)
receiver.finished.connect(lambda: print('BYE'))
receiver.start()
sender.start()

timer = QtCore.QTimer()
QtCore.QTimer.singleShot(500, lambda : sender.send("HELLO"))
QtCore.QTimer.singleShot(1000, app.exit)
app.exec()

for thr in [sender, receiver]:
    thr.quit()
    thr.wait()
#

In the above script, "HELLO" is actually printed by the main thread. receiver thread is already finished by the time the signal is emitted

rough furnace
#

@twin spire based on the activity on our issue tracker, people seem to be quick to be adopting pyside 6.5 😆

mortal grotto
runic umbraBOT
#

pyqtgraph/widgets/GraphicsView.py line 107

self.setCentralItem(QtWidgets.QGraphicsWidget())```
rough furnace
runic umbraBOT
#

pyqtgraph/widgets/GraphicsView.py line 180

self.sceneObj.addItem(item)```
mortal grotto
#

I need to become more robust on the widget/item relationship here, but is there a good spot to look for where the additional top window is being created?

from pyqtgraph import mkQApp, GraphicsView

app = mkQApp()
app.processEvents()
print(f"{app.topLevelWidgets()=}")
# app.topLevelWidgets()=[]
view = GraphicsView()
app.processEvents()
print(f"{app.topLevelWidgets()=}")
# app.topLevelWidgets()=[<PySide2.QtWidgets.QWidget(0x1cbd24bb420) at 0x000001CBD45928C0>, <pyqtgraph.widgets.GraphicsView.GraphicsView(0x1cbd3587150) at 0x000001CBD4592780>]
#

I thought it was at line 107 during debugging, but the place it appears changes over multiple runs so it's not reported deterministically

rough furnace
#

I don't think there is anything wrong here, I suspect the QWidget here is the "viewport" (see GraphicsView.useOpenGL)?

#

also if you think it's an ownership thing, try calling view.close() and then see what the topLevelWidgets are?

pale musk
#

i just fell through the rabbit hole --- i ended up here , fast data display you say...

mortal grotto
#
view.viewport() in app.topLevelWidgets()
# False

I thought so too, but I can also set viewport to None and there is still an additional top widget

#

Also doesn't help to use view.viewport().setParent(parent) or view.viewport().close()

rough furnace
#

does calling view.close() remove both of the top level widgets?

mortal grotto
#

After view.close() just the GraphicsView is a top level widget

rough furnace
#

huh, the GraphicsView doesn't get removed from the list of topLevelWidgets ? that's weird...

mortal grotto
#

Probably because the cli still has a reference to it? Or because it was never shown in the first place? same thing happens with win = QMainWindow();win.close();app.topLevelWidgets()

rough furnace
#

wait are you doing things from interactive mode?

mortal grotto
#

Yeah, but the outputs are the same as running a python script so far

fervent vale
#

I found that the act of using both setLayout and setGeometry creates the extra top-level QWidget. (pg.GraphicsView uses both together)

from PySide6 import QtCore, QtWidgets
app = QtWidgets.QApplication([])
scene = QtWidgets.QGraphicsScene()
gv = QtWidgets.QGraphicsView(scene)
gw = QtWidgets.QGraphicsWidget()
gw.setLayout(QtWidgets.QGraphicsGridLayout())
gw.setGeometry(QtCore.QRectF(0, 0, 100, 100))
scene.addItem(gw)
gv.show()
print(app.topLevelWidgets())
pale musk
#

im very new here

pale musk
#

any hoomans here today

rough furnace
pale musk
#

im just doing the basic installs fresh , python3.11 - 64bit , pyserial , openCV ....

#

QT is payware ?

#

i have , pyqtgraph installed -- i ask because , sometimes there is a better way

rough furnace
#

Python Qt bindings are dual licensed LPGL for pyside and GPLv3 for pyqt; and a commercial license is available for both. Commercial licenses may require a commercial Qt license but I’m not a lawyer and I don’t know.

pale musk
#

i dont know what to use -

rough furnace
#

Best way to install is to pip install the Qt bindings of your choice. We make no attempt to show preference towards any particular binding

pale musk
#

freeware and workable with minimal headache is my goal

rough furnace
#

I’ll say that the newly released pyside6 6.5 bindings have an issue with PyQtGraph, issue will be fixed in the next release

pale musk
#

can Tkinter be used to send message to the speedy , pyqtgraph stuff for drawing

rough furnace
#

Python Qt bindings are available for free, but there are license restrictions on what you can do with them. This is not a good place to discuss those restrictions

#

PyQtGraph needs qt to work. Can’t use PyQtGraph without a Python wt binding.

Unless you’re doing something really complicated you will likely not notice a difference between the bindings other than their respective imports

pale musk
#

im just using a breadboard MCU project

#

pygame will be tooooo slow for what i want to do

#

nothing commercial just messing around with MCU

rough furnace
#

Qt would likely work well for you GUI wise. I’ve never used pygame so I can’t compare

rough furnace
#

pyqt6 6.5 was released, but getting a failure on test_PolyLineROI (at least on macOS)

rough furnace
#

seems to pass on CI tho 😖

rough furnace
#

I’d like to do a release on the next few days. I’m going on a holiday where I’ll be inaccessible for over a week starting Saturday.

Anyone have issues in mind that should be addressed before the release? I want to look at the test_PolyLineROI failure more closely.

mortal grotto
#

TIL about Nuitka for compiling python applications (https://github.com/Nuitka/Nuitka)

Specifically configured to work with PyQt/PySide applications and compiles to C++ (python calls, but still in C)! Wild. Author is super responsive and working on getting it compiling with pyqtgraph

GitHub

Nuitka is a Python compiler written in Python. It's fully compatible with Python 2.6, 2.7, 3.4, 3.5, 3.6, 3.7, 3.8, 3.9, 3.10, and 3.11. You feed it your Python app, it does a lot of cleve...

rough furnace
#

I knew it was a thing but didn’t know it worked with Qt stuff; curious how well it works with PyQtGraph, we could consider adding a CI pipeline for it too

mortal grotto
#
GitHub

Output of pip freeze certifi @ file:///C:/b/abs_85o_6fm0se/croot/certifi_1671487778835/work/certifi click==8.1.3 colorama==0.4.6 darkdetect==0.7.1 Nuitka==1.5.5 numpy==1.24.2 ordered-set==4.1.0 pac...

GitHub

I am currently experiencing issues with nuitka and pyqtgraph that I am having a difficult time diagnosing. I am able to get pyqtgraph to work, but as soon as I right click on the graph and select t...

#

I’d like to do a release on the next few

rough furnace
#

love the "excellent_report" label 😆

mortal grotto
#

Haha right?

fervent vale
#

#2658 should get addressed.

merry lintel
#

Quick question, before I create an issue:

Is this problem already known? EDIT: IT IS ALREADY KNOWN!

Traceback (most recent call last):
  File "*********\GidAppTools\.venv\Lib\site-packages\pyqtgraph\examples\ColorBarItem.py", line 85, in <module>
    main_window = MainWindow()
                  ^^^^^^^^^^^^
  File "*********\GidAppTools\.venv\Lib\site-packages\pyqtgraph\examples\ColorBarItem.py", line 27, in __init__
    p1 = gr_wid.addPlot(title="non-interactive")
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "*********\GidAppTools\.venv\Lib\site-packages\pyqtgraph\graphicsItems\GraphicsLayout.py", line 72, in addPlot
    plot = PlotItem(**kargs)
           ^^^^^^^^^^^^^^^^^
  File "*********\GidAppTools\.venv\Lib\site-packages\pyqtgraph\graphicsItems\PlotItem\PlotItem.py", line 154, in __init__
    self.titleLabel = LabelItem('', size='11pt', parent=self)
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "*********\GidAppTools\.venv\Lib\site-packages\pyqtgraph\graphicsItems\LabelItem.py", line 19, in __init__
    GraphicsWidget.__init__(self, parent)
  File "*********\GidAppTools\.venv\Lib\site-packages\pyqtgraph\graphicsItems\GraphicsWidget.py", line 18, in __init__
    QtWidgets.QGraphicsWidget.__init__(self, *args, **kwargs)
TypeError: GraphicsWidgetAnchor.__init__() takes 1 positional argument but 2 were given

reproduceable with the examples:
.) ColorBarItem.py
.) BarGraphItem.py
.) customPlot.py
.) probably many other, but I checked with those

vers: pyqtgraph-0.13.2 (also did a fresh reinstall with --force-reinstall --no-cache-dir)
os: Windows 10
python: Python 3.11.1

other deps: QtPy-2.3.1, PySide6-6.5.0

mortal grotto
merry lintel
#

oh sorry, somehow overlooked that,

you want me to delete that block of text to keep the channel clean?

mortal grotto
#

No worries, I think it's fine to leave the history

rough furnace
#

looks like the test_polyROI failure I'm seeing is OS version dependent, not Qt version dependent

rough furnace
#

going to start the process for a new release, have a ton of stuff going on today so hopefully I don't mess it up 😄

#

actually, going to wait for #2689 to be updated, then I'll start the release process 😆

rough furnace
#

0.13.3 released with pyside6 6.5 compatibility, thank you to all contributors for making this release happen!

rough furnace
#

Not going to lie. These smaller releases where I can do the change log in 1 minute are nice

merry lintel
#

Want to thank, for all the effort and the awesome package!

rough furnace
#

I’m going on a holiday until next Sunday, @mortal grotto now is your opportunity to merge everything while I’m gone 😂

fervent vale
#

Finally got the conda CI to pass by dropping to Python 3.10. PySide2 is not officially supported on Python 3.11.

#

If someone happens to be running Fedora 37 (which uses Python 3.11), they could try installing the distro provided PySide2 and see if the tests crash at the same spot

rough furnace
#

If I was home i would fire up a docker container … but pyside2 is pretty old now, wouldn’t be surprised if there are issues with Python 3.11 (or 3.10). That said the conda forge folks try to maintain comparability

#

Just saw your comment. Yeah feedstock maintainers will definitely apply patches to try and maintain comparability. They may want to know of this issue. For our purposes I’m good with skipping Python 3.10 on the conda CI pipelines

fervent vale
#

I tried running Python 3.11/PySide2 on Fedora 37/WSL2 and also miniconda/Windows. No crashes there.

keen crater
#

Hello!
I found that shaders will break itemsAt function in pyqtgraph.openGL
If we will set shader='shaded' in GLMeshItem, itemsAt will return all objects with near param = ~0.0:
[(0.3899694386380653, 0), (0.0, 1), (0.4387341480326685, 2), (0.0, 3), (0.0, 4), (0.3882200134425005, 5)]
If we delete shader='shaded', all works good:
[(0.9989592458584716, 1)]
How shaders change the logic of itemsAt? How can I fix it?
Next message I will send a testing code

#
import QGIS3dViewer.tools.pyqtgraph as pg
import QGIS3dViewer.tools.pyqtgraph.opengl as gl
import numpy as np


class Point:

    def __init__(self, x, y, z):
        self._x = x
        self._y = y
        self._z = z

    def x(self):
        return self._x

    def y(self):
        return self._y

    def z(self):
        return self._z


class MyGLV(gl.GLViewWidget):
    def mouseReleaseEvent(self, ev):
        lpos = ev.position() if hasattr(ev, 'position') else ev.localPos()
        region = [lpos.x()-5, lpos.y()-5, 10, 10]
        # itemsAt seems to take in device pixels
        dpr = self.devicePixelRatio()
        region = tuple([x * dpr for x in region])
        for item in self.itemsAt(region):
            print(item.objectName())


pg.mkQApp()

glv = MyGLV()
glv.setCameraParams(elevation=90, azimuth=-90, distance=50)
# X points right, Y points up
glv.show()


info = {'red': [Point(0, 0, 0), Point(10, 10, 10), Point(5, 10, 0)], 'blue': [Point(10, 20, 15), Point(20, 20, 20), Point(0, 1, 20), Point(-10, -20, -15), Point(0, 0, 0)]}
#
for name, points in info.items():
    for i in range(0, len(points) - 1):
        x = points[i].x()
        y = points[i].y()
        z = points[i].z()
        x1 = points[i + 1].x()
        y1 = points[i + 1].y()
        z1 = points[i + 1].z()
        point1 = np.array([x, y, z])
        point2 = np.array([x1, y1, z1])
        v = point2 - point1
        theta = np.arctan2(v[1], v[0])
        phi = np.arctan2(np.linalg.norm(v[:2]), v[2])
        tr = pg.Transform3D()
        tr.translate(*point1)
        tr.rotate(theta * 180 / np.pi, 0, 0, 1)
        tr.rotate(phi * 180 / np.pi, 0, 1, 0)

        md = gl.MeshData.cylinder(rows=1, cols=6, radius=[1, 1], length=np.linalg.norm(v))
        m1 = gl.GLMeshItem(meshdata=md,
                           smooth=False,
                           shader='shaded')
        m1.setTransform(tr)
        m1.setObjectName(f'{name}')

        glv.addItem(m1)

pg.exec()
fervent vale
keen crater
fervent vale
#

I tried out your script, even with shaders enabled, only one color gets printed

keen crater
fervent vale
#

I tried master with PyQt 5.15. Then I rebased #2659 to master and tried it with PySide 6.5.0

white bane
#

a

torn minnow
white bane
#

c

worn pulsar
#

d

half jewel
#

I appreciate the humour, but this is bordering on the line of spam. This is a partner channel for the pyqtgraph open source project.

pearl prairie
#

Pls some help I want to generate sinewave through dac0 of waveshare high precision adda board with pi4 python pyside2 but increasing in sample rate decreasing the frequency

rough furnace
pearl prairie
# rough furnace What do you need help with here? PyQtGraph should handle that no problem, especi...

def handleSinewave(self):
a = 0.2
f = 0.1
period = 1.0 / f
print("Period", period)
samples_per_period = 1000
wt = period / samples_per_period
num_periods = 10 # number of full periods to generate
num_samples = samples_per_period * num_periods
while True :
for i in range(num_samples):
voltage = 2.446 + a * np.sin(2 * np.pi * f * i / samples_per_period)
voltage = np.clip(voltage, 0, 5)
self.DAC.DAC8532_Out_Voltage(0x30, voltage)
print("voltage", voltage)
time.sleep(wt)

When I increase the sampling rate frequency decreases and as I decreasing the frequency the sinewave dissorted using pi4 8 gb as microcontroller
Waveshare high precision adda board with dac8532 as wave generator
Matplotlib is plotting the same graph but in practical above scenario is happening

rough furnace
#

I'm still not understanding exactly what the issue is in context to pyqtgraph, maybe some screenshots of the plots in question might help? I don't know much about micro-controllers so if there is an issue there, I won't be of any use, sorry 🙂

rough furnace
fervent vale
#

Oh nice. I see that it's a separate issue from referencing self.

rough furnace
#

looks like now marked to be fixed in 6.5.1

#

Regarding pyqtgraph/pyqtgraph#2707 I'm not sure if that axes attribute qualifies as public API or not, I'm not seeing a reference to it in the documentation so I'm inclined to say we should just close out this PR. Thanks for posting the alternative method of getting it.

supple leafBOT
rough furnace
#

looking at pyqtgraph/pyqtgraph#2694 (using self.sender() ) I have some ancient memory of that only working if you had a @Slot() decorator... but that could have been a Qt4 thing.

supple leafBOT
rough furnace
#

@fervent vale is there anything specific I can help with regarding your GLViewWidget PR?

fervent vale
#

How do the docs look like with that PR?

rough furnace
#

I imagine you want the docstrings to get inherited ?

#

I don't mind adding the commit there, but I don't go pushing to other people's branches unless they ask me to 😛

fervent vale
#

OK I will give it a stab later

rough furnace
#

Since v5.0, it can take a comma separated list of ancestor classes. It allows to suppress inherited members of several classes on the module at once by specifying the option to automodule directive. (this is for the :inherited-members; bit, not sure how to add a coma separated list here (I'm a n00b when it comes to sphinx)

#

so probably :inherited-members: GLViewMixin anyway I'll leave it at that

anything else you think I should give attention to sooner than later? you've been more active on the repo than I have the last few .... last while

fervent vale
#

#2664 has loads of whitespace modifications.

#

I know it would be difficult for the author to undo them

#

But it's really hard to see the diffs

rough furnace
#

even w/ the "hide whitespace" option on the diff viewer?

#

this doesn't look too bad (diff view format that is)

fervent vale
#

Oh that's a feature I didn't know about

rough furnace
#

it's bitten me before with python not catching changes in indentation

#

so definitely use it with caution but for browsing a PR with changes this like that it's very useful

fervent vale
#

I can't seem to get :inherited-members: working correctly. I want it to list the GLViewMixin members but not the QOpenGLWidget members

#

it's supposed to work by specifying the base class that you do not want, but various combinations of specifying QOpenGLWidget doesn't seem to work

#

fully qualified would be PyQt6.QtOpenGLWidgets.QOpenGLWidget

rough furnace
#

Not sure, I’ll mess with it locally on my system today and see if I can get the intended behavior

polar thistle
#

Hi guys

#

I want to draw a graph , but have no clue how to do so , can someone help me

rough furnace
#

Is that like an iso plot? Is there an analogous plot in matplotlib you can point to as a reference?

polar thistle
#

Sorry didn't get ur question

#

Like I want an idea to code it with matplotlib

rough furnace
polar thistle
#

Nope

#

This is like the output code for one of the algorithm we run

#

And btw is there any way I can label regions like the top right triangle as t1 , the trapezium as some t2 and so on

rough furnace
#

Labeling might take extra work but if you can represent the regions as polygons, you can use PColorMeshItem

polar thistle
#

Like this is the final graph

rough furnace
#

Ahh I see, I think if you look at the examples and look at the isometric plot that can draw what you want

rough furnace
#

that's probably not what we want....

rough furnace
#

ok, got it

GLViewWidget
============

.. autoclass:: pyqtgraph.opengl.GLViewWidget
    :members:
    :inherited-members: QOpenGLWidget

    .. automethod:: pyqtgraph.opengl.GLViewWidget.__init__
#

ok, i'm off to bed 💤

hearty hazel
#

Hello friends i am new to this community .

#

I was creating a Amazon price tracker .

#

For that i want to make a bot which take the url of the current page after searching for the product . And i want to host it on python anywhere but , for that can't use gui i want to use headless browser . Which i have never worked with , the normal code is running good but headless i am having error .

#

This is the code.

#

'''import time
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.options import Options

def get_the_price_data_from_selenium(url):
# Path to the ChromeDriver executable
chrome_driver_path = "D:/Codebase2.0/100Days_of_code/Day47/Amazon_price_tracker/Web_Amz_prz_tkr/chromedriver.exe"

# chrome options
# chrome_options = Options()
# chrome_options.add_argument("--headless")

# Initialize the ChromeDriver
service = Service(chrome_driver_path)
# driver = webdriver.Chrome(service=service, options=chrome_options)
driver = webdriver.Chrome(service=service)


# Implicit wait
driver.implicitly_wait(time_to_wait=20)

# Navigate to the website
driver.get('https://keepa.com/#!product/10-B0BYZ26QGB')
print("1. Navigated to the website.")

# Find the search icon element and click it
# search_icon = driver.find_element(By.ID, "showSearchBar")
# search_icon.click()

search_icon = driver.find_element(By.XPATH, '//a[@id="showSearchBar"]')
search_icon.click() 

print("2. Clicked on the search icon.")

# Add a delay to allow the search bar to load
time.sleep(2)

# Now search bar has changed to search input
search_bar = driver.find_element(By.ID, "searchInput")
search_bar.send_keys(url)
search_bar.send_keys(Keys.ENTER)
print("3. Entered search query and pressed ENTER.")

# Wait for the search results to load
wait = WebDriverWait(driver, 5)
product_url = driver.current_url
print("4. Obtained the current URL.")
print(product_url)

# Close the browser
driver.quit()
print("5. Browser closed.")

test_url = "https://amzn.eu/d/0MsiF3d"
get_the_price_data_from_selenium(test_url)
'''

fervent vale
#

Another thing is that he parent argument actually still works by cooperative multi-inheritance, but it doesn't belong to the doc-string of GLViewMixin either.

fervent vale
#

Ok, I took the easy way out and just put the docstring into GLViewWidget

#

I found a possibly breaking change. The devicePixelRatio and rotationMethod arguments are keyword only in this PR due to the cooperative multi-inheritance

#

Previously, they could have been positional, although that usage would have been awkward

rough furnace
#

Yeah I agree, that would be awkward. I’m fine with the change to keyword only

rough furnace
#

You know, maybe we’re overthinking this documentation wise.

We can have GlViewMixin documented, have a warning saying this class is not intended to be inherited by end users, and not part of the official API .., that way we can highlight how we are inheriting the base QOpenGL widget as well.

fervent vale
#

not sure what I am doing wrong, but GLViewMixin is getting documented wrongly as GLViewWidget

rough furnace
#

I’ll take a look after I get the kids to bed 👍

rough furnace
#

no clue how/why it's got them confused (maybe a sphinx bug?) but I think the following generates results that are sufficient:

GLViewWidget
============

.. autoclass:: pyqtgraph.opengl.GLViewWidget
    :members:
    :show-inheritance:
    :inherited-members: QOpenGLWidget

    .. automethod:: pyqtgraph.opengl.GLViewWidget.__init__


.. autoclass:: pyqtgraph.opengl.GLViewWidget::GLViewMixin
    
    .. warning:: This class is not intended for users to inherit or use 
        directly, and is not considered part of the public API.
        
    :members:

    .. automethod:: pyqtgraph.opengl.GLViewWidget::GLViewMixin.__init__
rough furnace
#

on re-reading your pijyoi, realized that GLViewMixin was intended for inheriting for use of QOpenGLWindow, so that warning is perhaps not great completely off base, sorry about that.

#

let me re-read and I'll try and suggest something that might be better

fervent vale
#

I think the intention was that the advanced user could do that. But there was no promise that the interface would be stable.

rough furnace
#

something along those lines should be put there...

The intention of this class is to provide users who want to use ``QOpenGLWindow`` instead of ``QOpenGLWidget`` but retain the benefits of ``GLViewWidget``. Usage of this class should be considered experimental for the time being as it may change without warning in future releases.

updated the comment section.

rough furnace
#

so, ugh... what should get my attention next in the library? probably that styling PR?

fervent vale
#

The problem with unnecessary whitespace modifications is that you are prone to merge conflicts

rough furnace
#

Yeah; that will certainly cause issues, at just we no longer have 150+ open PRs.

#

I’m down to look at other PRs first

rough furnace
runic umbraBOT
#

pyqtgraph/exporters/SVGExporter.py line 108

items = [item]```
plush fulcrum
#

I think specifically because it is only ever appended to, it may actually just work and be much easier to manage than doing multiple containers. It’s basically just a breadth first node list

rough furnace
#

was thinking before I went to bed that issue 2661 would be straight forward before I went to bed... ugh, was way off base...

#

if I figure out what's wrong w/ this issue and make a PR for it, I may redo that bit of code...

plush fulcrum
#

The real problems with modifying containers you are looping over is if you modify contents at or before the index you are looking at… changing (and expecting it to be visited), inserting, or removing will cause weird problems and “don’t do that” is an easier rule of thumb, but doesn’t actually capture the nuance of what will just work

rough furnace
#

and with this attempt, I think I'm going to call it a night 😆

rough furnace
#

ok, ended up being not too difficult to implement what I wanted to ... still need to handle the scaling case and do a bunch more code cleanup

rough furnace
#

I don't like having this logic in the exporter where it must know about a variety of the different graphics items...

if isinstance(item, PlotCurveItem):
    # manipulate positioning as such
elif isinstance(item, ScatterPlotItem):
    # manipulate positioning of data different way...
elif isinstance(item, TextItem):
    # manipulate positioning of text in a different way...

we're having to manipulate the positioning here due to precision issues involving SVGs, but the if/elif/elif/elif... structure doesn't seem great here.

fervent vale
#

Regarding #2728, what is the meaning of dx = max(rect.right(), 0.) - min(rect.left(), 0.)? E.g., if right==2000 and left==1000, then dx==2000?

rough furnace
#

i copied that logic over from the method that was commented out; but in you're right, that doesn't look correct

#

I still hate that I'm having to call setData, I should try applying a QTransform to the curve and see if that works (and apply the inverse transform after the SVG is written)

rough furnace
runic umbraBOT
#

src/gui/painting/qpaintengineex.cpp lines 375 to 380

if (pen.isCosmetic()) {
    clipRect = d->exDeviceRect;
    cpRect.translate(xf.dx(), xf.dy());
} else {
    clipRect = xf.inverted().mapRect(QRectF(d->exDeviceRect));
}```
rough furnace
#

i keep thinking the SVG PR I'm working on (for shifting plot items back forwards the origin and scaling them between -0.5, 0.5) that I shouldn't need to call PlotCurveItem.setData but merely call PlotCurveItem.setTransform but I couldn't get that working to save my life...

if someone else wants to have another go at it, the closest I got was taking the current PR ...

and having the following differences...

#
if isinstance(item, PlotCurveItem):

    rect = item.viewRect()
    x_range = rect.right() - rect.left()
    dx =  rect.left() + (x_range / 2)

    y_range = rect.top() - rect.bottom()
    dy = rect.bottom() + y_range / 2 

    sx = 1 / abs(x_range)
    sy = 1 / abs(y_range)

    # to undo the change we roll out...
    preserve_original = item.transform()

    # move towards origin
    center = QtGui.QTransform(sx, 0, 0, sy, dx, dy)

    item.setTransform(center)

... 

# after the QPainter does it's paint operation...
doc = xml.parseString(arr.data())
if isinstance(item, PlotCurveItem):
    item.setTransform(preserve_original)
fervent vale
#

The following test fails on PySide6 6.5.1 : pytest -v .\tests\parametertree\test_parametertypes.py::test_pen_settings

#

Almost all the pen related settings in PlotSpeedTest.py no longer work. Only changing color works.

fervent vale
#

I rather suspect it's the bug fix for the lambda leak that is causing this

#

It seems like all the various pen properties are wrongly calling "setColor"

#

And "setColor" happens to be the first property of pen that is being created.

rough furnace
#

Nice catch, we can probably just remove the workaround at this point, or do you think we should keep it being an if statement of a pyside version check

fervent vale
#

No, I meant that it's PySide's bugfix for the leak that's causing this new issue

rough furnace
#

Oh, I misread (just woke up) I’ll try and take a look in a few hours

fervent vale
#

This is the pyqtgraph-free MWE that shows the issue present in PySide 6.5.1, and is the reason for pyqtgraph's pen parameter failure

from PySide6 import QtCore

class MySignals(QtCore.QObject):
    sigA = QtCore.Signal()
    sigB = QtCore.Signal()
    sigC = QtCore.Signal()

x = MySignals()
sigs = [x.sigA, x.sigB, x.sigC]
for idx, sig in enumerate(sigs):
    sig.connect(lambda x=idx: print(x))
for sig in sigs:
    sig.emit()
rough furnace
#

I won’t be on a computer for a few hours but I’m curious if there would be different behavior if the slot has the @Slot decorator.

rough furnace
#

figured that was a long shot :/

fervent vale
#

I have modified the example, and it seems like it should break a lot of code

#

or maybe not... it's code with multiple signals that connect to on-the-fly created slots

plush fulcrum
fervent vale
#

Another error occurring in PySide 6.5.1

import pyqtgraph as pg
from pyqtgraph.Qt import QtCore

app = pg.mkQApp()
pw = pg.PlotWidget()
pw.addItem(pg.GradientEditorItem())
pw.show()

# following timer is resulting in:
# TypeError: sigGradientChangeFinished(PyObject) needs 1 argument(s), 0 given!
QtCore.QTimer.singleShot(500, app.quit)

pg.exec()
#

This code exit pattern is used to test the pyqtgraph examples, with the result that any example that includes a GradientEditorItem will fail

#

ok, I have the pyqtgraph-free MWE

from PySide6 import QtCore

class Parent(QtCore.QObject):
    sigParent = QtCore.Signal(object)

class Child(Parent):
    sigChild = QtCore.Signal(object)

    def __init__(self):
        super().__init__()
        self.sigParent.connect(self.sigChild.emit)

app = QtCore.QCoreApplication()
c = Child()

# following timer is resulting in:
# TypeError: sigGradientChangeFinished(PyObject) needs 1 argument(s), 0 given!
QtCore.QTimer.singleShot(500, app.quit)

app.exec()
#

this signal-slot connection pattern is present in GradientEditorItem

fervent vale
#

This one seems to be a pyqtgraph anti-pattern. Shouldn't it just be self.sigParent.connect(self.sigChild)?

rough furnace
#

Yeah

#

Signals can be connected to other signals, not to their emit method

#

I’m actually surprised that ever worked

fervent vale
#

Maybe @mortal grotto could review #2734 regarding the changes to pen parameter

rough furnace
#

diff LGTM; I'll let @mortal grotto merge tho

mortal grotto
#

LGTM too 👍

rough furnace
#

Thank you @fervent vale !!

#

Should we do another release with this fix? Maybe we wait for pyqt6 6.5.1 to be released and make sure it works there?

fervent vale
#

Should raise a bug report against PySide for the inner function conflation

rough furnace
#

I’ll make a bug report using your MWE if you’re good with that

#

anything else we should consider merging before doing another release?

fervent vale
#

Looks like someone already made the bug report PYSIDE-2346

rough furnace
#

Oh nice, am putting the kids to bed in an hour or so so was going to submit then

plush fulcrum
#

pyqt6 6.5.1 is now out... I know because mpl tests started failing....

rough furnace
#

😆

plush fulcrum
#

gah and the pypi search page had the old version listed still so I fooled myself into thinking that wasn't it for a couple minutes and started scratching my head like "what changed???"

rough furnace
#

amateur move there, gotta go to that "release history" page 😉

#

no, I've never been duped like that before 😆

#

triggering a CI rrun .... we'll see what happens 😬

plush fulcrum
#

We had:

  • Deprecation warning regarding QtCore.Qt.AA_EnableHighDpiScaling
  • GLIBC version mismatch?
  • somehow loading both qt6.5.0 and qt6.5.1?
  • Subprocess calls which failed, but not sure why from just gh actions logs, could just be the above, could potentially be the same problem we had with pyside6.
rough furnace
#

our regular test suite passed, we got some warnings but they're numpy related

#

we're all green here regarding PyQt6 6.5.1

plush fulcrum
#

the pyqt6 6.5.1 wheels contain symbols from a glibc version greater than the wheel states it supports... so that's a thing...

rough furnace
#

I would post to the mail list, Phil is pretty responsive for things like that

fervent vale
#
for x in *abi3.so; do objdump -t $x | grep GLIBC_ ; done | sort | uniq | grep -v "2.2.5"
0000000000000000       F *UND*  0000000000000000              __stack_chk_fail@GLIBC_2.4
0000000000000000       F *UND*  0000000000000000              hypotf@GLIBC_2.35
0000000000000000       F *UND*  0000000000000000              memcpy@GLIBC_2.14
#

QtGui.abi3.so has a dependency on hypotf@GLIBC_2.35

#

So pypi provided wheel PyQt6 6.5.1 doesn't run on Ubuntu 20.04 which has glibc 2.31

#

I guess it wouldn't run on the RHEL 9.x family either, since that's on glibc 2.34

rough furnace
#

if this can't run on ubuntu 20.04 or RHEL 9, I imagine that there will be some complaints about this in short order.

rough furnace
#

leave it to me to completely blow up the scope of a PR to fix an issue pyqtgraph/pyqtgraph#2728

realized that the exporter checks for the existance of GraphicsItem.generateSvg() and if it exists, it runs that for SVG output.. .realize that's not documented anywhere, so I create an empty method in GraphicsItem, only to realize that none of the exporters are documented 🤦‍♂️

rough furnace
#

also not sure how I feel about these annotations 😬

#

I think I'm somewhat partial for that... although wish that the return type was a bit more expanded...

#

@plush fulcrum if you're looking at type-hints for mpl, I am sure you must be shuddering at how your huge function signatures are going to render...

plush fulcrum
#

type hints in stub files are not picked up by sphinx (at least by default), and we turned rendering in the docs off for now for e.g. pyplot which is is typed inline (as the docstring has the info already, and the rendering was not great...)

The mode to try to put them in the docstring did not play well with numpydoc, I believe (possible it could be as simple as reordering sphinx extensions, but at least in initial testing)

rough furnace
#

With numpy doc I’m having a hard time detailing the return type that is tuple[Element, list[Element]]

rough furnace
#

ugh, I'm going to have to create a named tuple, aren't I...

rough furnace
#

I suppose this looks ok, the "return time"

rough furnace
#

fixed the SVG background color issue, but the scale value on the other hand is going to be tricky 😬

fervent vale
#

"Overwrite" should be spelt "override"

rough furnace
#

yes, yes it should 😆

#

just sorted out the sizing issue on the SVG export, hoping that after I merge this PR it will be a long time before I have to look at it again

#

computer says no

rough furnace
rough furnace
#

managed to also fix pyqtgraph/pyqtgraph#1849 on this PR, in hindsight I think that would have been a good first issue, oh well.

supple leafBOT
rough furnace
#

@plush fulcrum saw a message on the pyqt mail list that the latest snapshot of pyqt6 had the mismatched libc issue sorted out

rough furnace
#

story of my life right now, finish chores/exercise realize that I can get around my issue in pyqtgraph/pyqtgraph#2418 by using a separate object to capture timing information, but that object should run on a separate thread, and periodically it can fire off a signal with what should be on the title.

I sit down, fire up my editor, hop on the branch, start looking at the code and realize "you know what, going to bed on time sounds great"

supple leafBOT
rough furnace
#

@fervent vale @mortal grotto if you two have some time, would appreciate if you could test #2418 on your machines again; instead of doing weird things w/ QEventLoop, I decide to handle timing in a separate QThread.

fervent vale
#

In this PR, sigPaintFinished was added to PlotCurveItem. Then another standalone 2 signals are created: paintStarted and paintFinished. PlotCurveItem::sigPaintFinished -> paintFinished. But paintStarted gets emitted by PlotSpeedTest. Why not have a PlotCurveItem::sigPaintStarted? Then the 2 standalone signals are not needed at all.

fervent vale
#

Or maybe a single signal within PlotCurveItem would suffice: sigPaintDuration

#

That would eliminate measurement error due to signal overhead

rough furnace
#

Ooo good idea

#

the PR certainly needs more cleanup; curious if the QThread implementation is causing issues, if not I'll also adopt it to the scatter plot speed test as well

fervent vale
#

But is there really any need for a QThread? The setTitle goes back to the main gui thread. So sigPaintDuration could just as well be connected to a main thread callback using a QueuedConnection

rough furnace
#

the need for qthread has to do with being able to accurately measure the time between when setData is called and when paint finishes... previously I attempted to do this with introducing some blocking code via QEventLoop.exec(), and used sigPaintFinished to "unblock" the code, and thus measure the elapsed time.

#

unfortunately as you and ntjess noticed, that didn't work as intended

#

I probably should update the original post on that PR and state more clearly what I'm trying to achieve.

fervent vale
#

instead of measuring time, just count the number of frames

#

instead of calling processEvents, just let the regular event loop run

#

there's no need to keep an averaging window

#

the large averaging window was there due to the jitter per frame

#

there is no need to measure "accurately" the time elapsed per frame

rough furnace
#

Thanks for posting, I’m traveling today but will try and take a closer look on Monday.

keen crater
#

Hello!
I have a lot of lines that I want to draw as a cyllinder.
I used gl.MeshData.cylinde for that:

md = gl.MeshData.cylinder(rows=1, cols=cols, radius=[radius, radius], length=np.linalg.norm(v))
m1 = gl.GLMeshItem(meshdata=md,
                   smooth=False,
                   shader='myShader',
                   color=color_rgb)
m1.setTransform(tr)

But I want to combine them to optimize drawing. How can I do that? How can I update meshData to one big mesh?

rough furnace
#

@keen crater I don't know the answer to your question, just wanted you to know I'm not ignoring you ...I just don't use the 3D aspects of the library much myself...

rough furnace
rough furnace
#

just realized that in Qt 6.5, using fusion application style, it automatically detects and applies the windows dark theme 🎊

fervent vale
#

The multiple inheritance issue is PySide's problem

rough furnace
#

So think the test suite went from routinely segfault and to getting runtime warnings for non-common platforms…

fervent vale
#

s390x may be uncommon, but aarch64 is getting mainstream

rough furnace
#

yeah, as much as I use macOS, I don't have access to one of the M-processors yet; still chugging away on my 2019 16" i9 macbook pro

undone flame
#

Just wanted to state my appreciation to any PyQtGraph developers / contributors here. I'm using PyQtGraph for the first time, and it rocks!

rough furnace
#

Thanks @undone flame I’m curious what project/problem you had that caused you to give PyQtGraph a look, and what parts of the library you’re using!

undone flame
# rough furnace Thanks <@641607416072503298> I’m curious what project/problem you had that cause...

Early stages of an audio app, using PyQtGraph to provide a visual representations of the waveform. Initially I tried VisPy, which has excellent performance, but I was finding it tricky to accomplish what I was trying to do. I then gave PyQtGraph a go as it seemed to be a bit more mature. I'm very impresses with it, and great to see that it has a good amount of documentation, and it looks like it will flexible and fast enough for my needs.

rough furnace
#

my involvement w/ pyqtgraph started out of audio work too funny enough

#

i haven't touched it in a while, but you may find this guy of interest: https://github.com/j9ac9k/barney

our documentation could be much better, I'm quite jealous of the vispy examples documentation

#

if you ever figure out how to use QAudioOutput with a numpy array, in a way that is consistently low latency, be sure to let me know!

rough furnace
#

@fervent vale I’m in a location with awful internet connectivity, will comment more tomorrow once I get home.

fervent vale
#

I need to remove the remaining calls to processEvents()

#

The 0 interval update timer only gets fired when all other events have completed

#

Hence, each call to update() will naturally result in one repaint, without having to force it

runic umbraBOT
#

:incoming_envelope: :ok_hand: applied timeout to @fickle robin until <t:1687574719:f> (10 minutes) (reason: duplicates spam - sent 4 duplicate messages).

The <@&831776746206265384> have been alerted for review.

half jewel
rough furnace
rough furnace
fervent vale
#

If it was a fix backported to 1.22.4, shouldn't it appear in newer versions?

rough furnace
#

🤷‍♀️ i get no error on 1.23.0

#

Going to run through the test suite one more time to make sure…

#

yea; test suite for 1.23.0 passes, ... I'll run a few more patch versions of 1.23.x ... also curious if this is an issue on python 3.10 or just 3.9

fervent vale
#

There's a sip 6.7.8 if else branch prior to the error

#

Need to check which branch is being taken

rough furnace
#
PyQt5          5.15.9
PyQt5-Qt5      5.15.2
PyQt5-sip      12.12.1
fervent vale
#

Okay, so it's taking the >= 6.7.8 branch

#

It should be sufficient to make the code go through the sip voidptr branch

rough furnace
runic umbraBOT
#

pyqtgraph/Qt/internals.py line 177

if sip.SIP_VERSION >= 0x60708:```
fervent vale
#

But if numpy >= 1.23 works fine, then there must be another patch that didn't get backported

#

Yes line 177

#

The else branch is actually the workaround path

#

Where an extra voidptr gets instantiated

#

But I would consider it a bug (numpy or sip) that a sip.array can't be used directly with frombuffer

rough furnace
#

i agree it's a bug, and not a bug in our code.... and if it wasn't the most recent patch version for numpy 1.22 i would ignore it...

#

unfortunately have to wait until jan 1st before we can go numpy 1.23+ 😦

#

i guess I'll add an and condition there that checks for the numpy version...

#

i feel like I'm dirtying your pretty code 😆

            if (sip.SIP_VERSION >= 0x60708 and 
                np.__version__ != "1.22.4"  # workaround for numpy/sip issue
            ):
#

but it works

#

since fixing tox; noticing another intermediate error (completely unrelated)

    def test_mouseDragEventSnap():
        plt = pg.GraphicsView()
        plt.show()
        resizeWindow(plt, 200, 200)
        vb = pg.ViewBox()
        plt.scene().addItem(vb)
        vb.resize(200, 200)
        QtTest.QTest.qWaitForWindowExposed(plt)
        QtTest.QTest.qWait(100)

        # A Rectangular roi with scaleSnap enabled
        initial_x = 20
        initial_y = 20
        roi = pg.RectROI((initial_x, initial_y), (20, 20), scaleSnap=True,
                         translateSnap=True, snapSize=1.0, movable=True)
        vb.addItem(roi)
        app.processEvents()

        # Snap size roundtrip
        assert roi.snapSize == 1.0
        roi.snapSize = 0.2
        assert roi.snapSize == 0.2
        roi.snapSize = 1.0
        assert roi.snapSize == 1.0

        # Snap position check
        snapped = roi.getSnapPosition(pg.Point(2.5, 3.5), snap=True)
        assert snapped == pg.Point(2.0, 4.0)

        # Only drag in y direction
        roi_position = roi.mapToView(pg.Point(initial_x, initial_y))
        mouseDrag(plt, roi_position, roi_position + pg.Point(0, 10),
                  QtCore.Qt.MouseButton.LeftButton)
>       assert roi.pos() == pg.Point(initial_x, 19)
E       assert Point(20.000000, 20.000000) == Point(20.000000, 19.000000)
E         Use -v to get more diff

tests/graphicsItems/test_ROI.py:228: AssertionError
#

I'm actually surprised this numpy error hasn't been reported yet, this isn't some old version of numpy here...

fervent vale
#

Looking at the backport PR, from the point of view of numpy, it's not their bug either...

#
In [15]: sa = sip.array(QtCore.QPointF, 2)

In [16]: memoryview(sa)
---------------------------------------------------------------------------
BufferError                               Traceback (most recent call last)
Cell In[16], line 1
----> 1 memoryview(sa)

BufferError: format has not been specified
#

the numpy backported PR converts the object into a Python memoryview if it was not a numpy array to begin with

#

So the raised error is from Python

#

in newer numpy, a different condition is used to decide whether to make the buffer into a memoryview

#

The "issue" as it were, is that we are using sip.array of non-primitive-scalar type, so the buffer doesn't have a format code set

#

whereas going through a sip.voidptr makes it into a "byte" type

#

which then passes through "memoryview" w/o error

rough furnace
#

I'll reword the comment as such that i'm not pointing the finger at numpy so much

#

is there any change that the sip module can (or should?) make to minimize likelihood of future issues?

fervent vale
#

I was mistaken, the exception "format has not been specified" is raised by sip, inside sip_array.c

#

As sip.array(s) of non-scalar types don't have a format, sip raises an exception if the buffer requestor asks for the "format" field to be populated

#

(There's a bug in sip here. A leak will occur if this exception is raised)

#

It could be argued that sip is being overly strict here, and that it could simply set the format as "B" (for bytes) in such a case

#

Python's PyBuffer_FillInfo will populate format as "B", so there's a precedent for that

#

The code in question is in PyQt6_sip-13.5.1.tar.gz::sip_array.c::sipArray_getbuffer

rough furnace
#

i assume the bug with sip is still there w/ the current version?

fervent vale
#

13.5.1 is the latest available from pypi

#

But I would imagine it would still be there in the snapshots

rough furnace
#

hmm...ok, probably should post something on the mail list about it...

fervent vale
#

To be clear, Python "memoryview(sip.array(QPointF, 2))" is sufficient to trigger the issue. No numpy needed

rough furnace
#

love you were able to scrap some of the calls to .processEvents()!

#

love this diff so much, moving the timing functionality to utils.py makes everything better, and the examples are less cluttered... there is uniformity across the benchmarks...

fervent vale
#

Strangely, infiniteline_performance.py isn't listed in utils.py

#

Not quite convinced of its usefulness though

rough furnace
#

Feel free to delete it; we clearly have gone this long without it

fervent vale
#

I see it's from PR286. Supposed to demonstrate the improvements due to caching bounding rect

rough furnace
#

… yeah I’m good with removing that

rough furnace
#

@fervent vale you considerirng any other changes on the fps update PR?

fervent vale
#

No more changes. I'm at work now anyway

rough furnace
#

I'm starting to come around to the idea of removing NonUniformImage

#

I'm not there yet 😆 (but getting there)

#

realizing the first/last elements are treated differently ...

                # left, right, bottom, top
                l = x[0] if i == 0 else (x[i - 1] + x[i]) / 2
                r = (x[i] + x[i + 1]) / 2 if i < x.size - 1 else x[-1]
                b = y[0] if j == 0 else (y[j - 1] + y[j]) / 2
                t = (y[j] + y[j + 1]) / 2 if j < y.size - 1 else y[-1]
#

hmm...I see matplotlib has also a NonUniformImage that seems to accept data in the same way...

fervent vale
rough furnace
#

oh right; you had mentioned that...

fervent vale
#

The border edges and the non-finite handling behavior are the missing parts

rough furnace
#

you think the thing to do would be to scrap the current implementation, and create a new NonUniformImage that subclasses from BarGraphItem and has the appropriate methods?

Feels weird, but if it works well, it works well

fervent vale
#

It would be busy-work for little gain

#

Speaking of weird, there's drawing functionality in ImageItem, which probably doesn't belong there

fervent vale
#

It looks like NonUniformImage::setLookupTable expects a HistogramLUTItem as argument. This differs from ImageItem and PColorMeshItem which take a numpy array for method of the same name

#

I think that is overly coupling NonUniformImage with HistogramLUTItem. Unfortunately, the NonUniformImage example makes use of it.

rough furnace
#

given that NonUniformImage hasn't been in the docs, I'm good with putting very aggressive deprecations to methods/arguments... one of the methods I don't like the name of, there is no setData method, ... tl;dr it needs work (which I'm happy to do).

fervent vale
#

The NonUniformImage::generatePicture is non-vectorized. It is prevented from doing so because the colormap / lut is not quantized to a set number of entries.

#

Something like what PColorMeshItem does would be good. Take either a colorMap instance or allow the user to set the numpy lut directly.

dapper yarrow
#

Was playing around with a ScaleBar today, and it works really well.
However, I would like the user to be able to toggle the scale bar on or off,
is it possible to delete a ScaleBar after it has been created?

fossil berry
#

i cant able to install python in my computer

rough furnace
rough furnace
rough furnace
runic umbraBOT
#

pyqtgraph/graphicsItems/PColorMeshItem.py line 201

ValueError('Data must been sent as (z) or (x, y, z)')```
dapper yarrow
# rough furnace oh you meant delete, not just hide? sure if you have a reference to the scale ba...

Thanks for the quick reply,
In this case the scale bar is in a ViewBox, and trying ViewBox.removeItem(img_scale) gives the following answer

Traceback (most recent call last):
  File "code\libs\create_gui.py", line 505, in show_scale_bar
    self.viewbox.removeItem(self.img_scale)
  File "Python310\site-packages\pyqtgraph\graphicsItems\ViewBox\ViewBox.py", line 429, in removeItem
    item.setParentItem(None)
  File "Python310\site-packages\pyqtgraph\graphicsItems\ScaleBar.py", line 67, in setParentItem
    self.anchor(itemPos=anchor, parentPos=anchor, offset=offset)
  File "Python310\site-packages\pyqtgraph\graphicsItems\GraphicsWidgetAnchor.py", line 37, in anchor
    raise Exception("Cannot anchor; parent is not set.")
Exception: Cannot anchor; parent is not set.
self.viewbox.removeItem(self.img_scale)**

I think I might just do show/hide for now, as this seem to work well

rough furnace
#

Huh, that error shouldn’t be happening. Can you create a minimal example with it occurring? I’d like to take a closer look

fervent vale
#

PColorMeshItem::setLookupTable was implemented to take a list of QColors; vs ImageItem::setLookupTable taking a numpy array

#

and this difference actually got coded into ColorBarItem

fervent vale
#

I was making a breaking change to NonUniformImage::setLookupTable so that it accepts a numpy array. Then NonUniformImage no longer needs to know anything about HistogramLUTItem. Instead, NonUniformImage can be duck-typed to HistogramLUTItem::setImageItem

#

Then I thought, why not make it compatible to ColorBarItem::setImageItem?

#

And that's when I saw the discrepancy with PColorMeshItem::setLookupTable, which I rather consider to be an error

#

A more general method (on top of have setLookupTable take numpy arrays) would have been PColorMeshItem::setColorMap, from where a list of QColor(s) could have been obtained, if that was the more-convenient internal format

rough furnace
#

I'm good w/ breaking changes, ... if this is planned work, we should probably commit a warning to the repo right now, saying what won't work in the future

#

not sure if you saw the PyQt mail list, but Phil replied saying the sip_array.c::sipArray_getbuffer issues will be addressed in the next snapshot

rough furnace
#

for my changes to NonUniformItem; I'm going to ignore the LUT stuff for the time being; I'm making enough changes as it is in this PR 😅

I would certainly love to normalize setLookupTable and setColorMap throughout the library to have the same function signatures; but the scope of the nonuniformitem PR I'm working on has already grown.

I'm largely emulating a lot of what you did w/ PColorMeshItem

dapper yarrow
# rough furnace Huh, that error shouldn’t be happening. Can you create a minimal example with it...
from PySide2 import QtCore, QtWidgets
import pyqtgraph
import sys

class MainWindow(QtWidgets.QMainWindow):
    def __init__(self, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)
        self.imv = pyqtgraph.ImageView()
        self.view_box = self.imv.getView()
        self.setCentralWidget(self.imv)
        self.scale_bar = pyqtgraph.ScaleBar(5, offset = (-30,-30))
        self.scale_bar.setParentItem(self.view_box)

    def remove_scale_bar(self):
        print('############ removing ###########')
        self.view_box.removeItem(self.scale_bar)

if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    main_window = MainWindow()
    main_window.show()
    QtCore.QTimer.singleShot(1000, main_window.remove_scale_bar)
    sys.exit(app.exec_())

Here you go 🙂

fervent vale
#

There is indeed a bug in ScaleBar (and also LegendItem), It assumes setParentItem is not being used to remove the parent.

rough furnace
#

@fervent vale brilliant use of np.pad

rough furnace
#

loving all the comments in the NonUniformItem.generatePicutre() ...thanks so much for adding those!

rough furnace
#

https://bugreports.qt.io/browse/QTBUG-74407

looks like there is planned work to move the QRhi abstraction layer to QPainter; ... unfortunately from the looks of things there hasn't been a lot of work here recently 😦

glacial crown
rough furnace
#

@fervent vale I'm curious how much (if any?) performance speed up there would be with moving these nested for loops into a list comprehension

        polys = []
        for r in range(nrows):
            for c in range(ncols):
                bl = points[(r+0)*(ncols+1)+(c+0)]
                tl = points[(r+0)*(ncols+1)+(c+1)]
                br = points[(r+1)*(ncols+1)+(c+0)]
                tr = points[(r+1)*(ncols+1)+(c+1)]
                poly = (bl, br, tr, tl)
                polys.append(poly)

to

polys = [
   (
        points[(r+0)*(ncols+1)+(c+0)],
        points[(r+1)*(ncols+1)+(c+0)],
        points[(r+1)*(ncols+1)+(c+1)],
        points[(r+0)*(ncols+1)+(c+1)]
   ) for r in range(nrows) for c in range(ncols)
]

I'll be the first to admit it's not as readable, but list-comprehensions are usually a bit faster than the generic for loops

#

actually, i can benchmark that myself after I get the kids to bed...

fervent vale
#

at least for the use-case demonstrated in the example, that gets invoked once only

rough furnace
#

oh, that's not worth it then

fervent vale
#

actually it's already quite confusing to me... there's the X,Y axis transposition. I am not even sure if bl,br are bottom and tl,tr is top

#

in the scipy ascent image display example, the image has to be transposed to be displayed correctly

#

at least in the double for loop, it's explicit what the programmer's intent (rightly or wrongly) of br, tl, br, tr was

rough furnace
#

actually, in the case of polygons, it doesn't really matter, provided the points have a line connecting them?

fervent vale
#

there's clockwise and anti-clockwise

#

for OpenGL it matters

#

it determines the direction of the normal

rough furnace
#

ahh, makes sense

fervent vale
#

for NUImg, it's clear that setLookupTable(HistogramLUTItem) is the wrong api to expose

#

for PCMI, it's not so clear that setLookupTable() shouldn't be able to take either an ndarray or a list of QColor

rough furnace
#

Is it that difficult to support both? We eventually need to convert the colors to QColor instances anyway

fervent vale
#

it's simpler to support both

#

it's more a question of whether there should be consistency across the various image-like items

#

using ImageItem as the "standard" may be the wrong thing to do too, beause ImageItem predated ColorBarItem

rough furnace
#

Ugh yeah kind of stuff is tricky; happy to support what is easy until complains/contributions come in to expand functionality

fervent vale
#

should I revert the changes I made to ColorBarItem then? let it "know" that PCMI prefers list of qcolor

#

having setLookupTable support ndarray lets it potentially inter-operate with HistogramLUTItem

rough furnace
#

ok finally got the kids to bed..sort of... ok, let me peak at your diff for colorbaritem

fervent vale
#

PCMI natively prefers a list of QColor. Having ColorBarItem pass it an ndarray just makes PCMI convert it back to list of QColor. However, setting colormap is likely to be once off.

rough furnace
#

i'm indifferent about it, but was just wondering if this is the sort of thing that we could make cleaner w/ the fancy switch syntax that python 3.10 introduces

#

that doesn't answer your question, I'm honestly indifferent about it; I haven't heard any gripes on performance on these items, so I suppose I would opt for the simplest use-case

fervent vale
#

ok, I will limit this PR to not "fix" any api. (besides remove the deprecated cmap argument)

rough furnace
#

can you think of a better way of reassigning the methods from ViewBox to PlotItem?

    ## Wrap a few methods from viewBox. 
    #Important: don't use a settattr(m, getattr(self.vb, m)) as we'd be leaving the viebox alive
    #because we had a reference to an instance method (creating wrapper methods at runtime instead).
    for m in ['setXRange', 'setYRange', 'setXLink', 'setYLink', 'setAutoPan',         # NOTE: 
              'setAutoVisible', 'setDefaultPadding', 'setRange', 'autoRange', 'viewRect', 'viewRange',     # If you update this list, please 
              'setMouseEnabled', 'setLimits', 'enableAutoRange', 'disableAutoRange',  # update the class docstring 
              'setAspectLocked', 'invertY', 'invertX', 'register', 'unregister']:                # as well.
                
        def _create_method(name):
            def method(self, *args, **kwargs):
                return getattr(self.vb, name)(*args, **kwargs)
            method.__name__ = name
            return method
        
        locals()[m] = _create_method(m)
        
    del _create_method

the code-checker doesn't recognizing that these methods exist in PlotItem.

fervent vale
#

I don't even understand the comment about keeping viewbox alive

#

why would creating it at runtime make a difference?

rough furnace
#

I don't know; but keep in mind this comment/code was written a long time ago, python behavior could have changed since then...this was likely written for python 2.6

#

i think the comment implies there would be an extra reference hold on the viewbox with using setattr

fervent vale
#

ok, maybe I get it. the inner method captures "self", but "self.vb" is dereferenced at runtime. so there's no extra reference to the viewbox held

rough furnace
#

what do you think of having a setData method to NonUniformImage? ... not that I expect this plot type to be rapidly updated or anything...

#

(I'm handling the merge conflicts now on my PR 2614 now that I merged your changes)

fervent vale
#

it's useful even if the data is only ever updated once. right now, it wouldn't be possible to build an app that loads data to be viewed

rough furnace
#

k; I'll add it

#

along w/ some better checks for data shapes for x, y and z arrays

fervent vale
#

there's some scope creep. if you add setData(), do you also need to inform HistogramLUTItem and ColorBarItem?

#

HLI looks for a 'sigImageChanged'

#

don't know if CBI works

rough furnace
#

i can certainly emit that easily enough

#

yeah i'm not sure either; I'm starting to fall asleep so Im going to call it a night, thanks for making the changes to NUI and PCMI

fervent vale
#

btw, the coords that the user provides to NonUniformImage are not the pixel centers. E.g. given x coords 1,5,11, the center polygon will have left edge 3 and right edge 8, which is centered at 5.5

rough furnace
#

Ugh eventually I’ll get that ascii art right

fervent vale
#

it is correct that the user is supposed to pass the coords of their sampled data

#

however it is wrong to interpret that the drawn polygon has the sampled data coord as its center

#

we are using the shading of the rectangles as a rendering of "nearest" interpolation

#

it's counter-intuitive... especially when there aren't many data points to begin with

rough furnace
#

Maybe I should do away with the ascii art haha

fervent vale
#

so it's non-uniform sampling data points, but it's not non-uniform rectangular pixels

rough furnace
#

@fervent vale if you have a screenshot of a pyqtgraph based application you develop professionally, that you would like to include in a poster board session at the SciPy conference, please share it with @mortal grotto ...he'll be there next week

fervent vale
#

If I did have such an app, I am sure I wouldn't be permitted to showcase it

rough furnace
#

Fair enough!

rough furnace
#

I feel like I'm doing something really silly;... I'm collecting benchmark data using a slight variant of MultiPlotSpeedTest.py but I can't seem to show any difference with downsampling.

I set it explicitly in the code:

plot.getPlotItem().setDownsampling(ds=True, auto=True, mode="peak")
#

oh; setting it further down the line...

Traceback (most recent call last):
  File "C:\Users\ogi\source\repos\pyqtgraph\pyqtgraph\graphicsItems\PlotItem\PlotItem.py", line 945, in updateDownsampling
    c.setDownsampling(ds, auto, method)
    ^^^^^^^^^^^^^^^^^
AttributeError: 'PlotCurveItem' object has no attribute 'setDownsampling'
#

welp, that might explain it!

#

sure enough, changing my curve from being a PlotCurveItem to a PlotDataItem was sufficient

fervent vale
#

The recent searchsorted fix was helpful in getting good performance with down sampling. I recently had to plot 10e6 samples and it made a big difference

#

I had to manually patch my copy of pyqtgraph though. It was on a Windows 7 system which limits it to Python 3.8

rough furnace
#

yeah 10e6 is where things started getting great in our testing

#

with the improvements you made pijyoi, I was able to test far more combinations than the last time I did this...

#

and now, exporting to SVG, I see a bug in my SVG fix from before 🤦‍♂️

#

ok bedtime....

rough furnace
fervent vale
#

Well, can't be blaming anyone for still using an EOL OS!

rough furnace
#

haha, true; still don't mind doing stuff like that if it helps you...

#

you've done a lot of work w/ PlotCurveItem, do you think that the curve downsampling functionality should be migrated to that object from PlotDataItem?

fervent vale
#

Downsampling also affects ScatterPlotItem

rough furnace
rough furnace
#

redid the asv image benchmarking..now using the fancy parametrization asv offers

fervent vale
#

Cuda takes precedence over numba.

rough furnace
#

ahh; good to know that there's no point in doing both 😅

#

easy fix!

#

alright, going to bed, queued up a run of all the sizes...that should give me the data I need to regenerate the plot

rough furnace
#

this is interesting; cupy is slowest by far with a uint16 dtype, no LUT, and use_levels=False

#

screenshot of table, I need to get into a spreadsheet/pandas dataframe but now I need to do the job that pays me money 😄

rough furnace
#

it's a bit dissapointing that asv doesn't make the raw results easier to access, no csv; ... there is a json file, probably would a little time to write a parser for it...

fervent vale
#

For cuda mode, PlotSpeedTest is benchmarking the scenario where the user image data initially resides on gpu memory

#

For uint16, no lut and no levels, the image data can be immediately wrapped into a GrayScale16 QImage with no processing

#

So cuda mode for this case does nothing besides transferring the image data from gpu memory back to cpu memory.

#

This transfer rate is occurring at 4GB/s on your test machine based on your graph

#

Numba doesn't kick in and is the same as Numpy for this case.

#

If your gfx card was PCIe 3.0 16x lanes, an expected transfer rate from gpu to cpu would be ~10GB/s

rough furnace
runic umbraBOT
#

benchmarks/renderImageItem.py lines 18 to 23

def renderQImage(*args, **kwargs):
    imgitem = pg.ImageItem(axisOrder='row-major')
    if 'autoLevels' not in kwargs:
        kwargs['autoLevels'] = False
    imgitem.setImage(*args, **kwargs)
    imgitem.render()```
rough furnace
#

makes sense, any suggestions on how to rework the benchmark to take that into account? (just created a PR with my working config) ...or I guess, there would be no taking it into account, as if I have cuda array that I want to draw with no levels and no LUT, it's going to get transferred to regular memory no matter what :/

rough furnace
#

On further reflection, I think I'm going to skip that use-case (cupy + no lut + no levels), as you said, only thing it benchmarks is transfer speed from GPU memory to CPU memory

fervent vale
#

So if you are using renderImageItem.py, then you are measuring the round trip cpu- gpu-cpu and you obtained a transfer rate of 8GB/sec

rough furnace
rough furnace
#

just realized I had uint16 LUTs, not uint8 ones 😬

#

I work w/ the image stuff of the library so little, I'm glad I'm doing this benchmarking... normally I'm so confused by the image stuff 😂

fervent vale
#

So if there's no round trip, then 4GB/s on PCIe 4.0 16x is only about 20% of (practical) expected

#

HistogramLUTItem returns 512-entry LUTs if the image data is not uint8. This bumps up the LUT to uint16

rough furnace
#

i don't typically evaluate performance on the hardware that much but sure enough, bus interface is PCIe 4.0 x16

runic umbraBOT
#

pyqtgraph/graphicsItems/ImageItem.py lines 568 to 572

warnings.warn(
    "Using non-uint8 LUTs is an undocumented accidental feature and may "
    "be removed at some point in the future. Please open an issue if you "
    "instead believe this to be worthy of protected inclusion in pyqtgraph.",
    DeprecationWarning, stacklevel=2)```
fervent vale
#

There's some potential for confusion here

#
  1. the number of entries of the lut
#
  1. the bit depth of each entry of the lut
#

The warning refers to (2)

rough furnace
#

ok, yeah, I specified the bit-depth of each entry of the lut in the test (originally) as uint16;

fervent vale
#

In the benchmarking, uint16 lut refers to (1)

rough furnace
#

oh?

fervent vale
#

Let's take a float type image. HistogramLUTItem will provide a 512-entry lut

rough furnace
#

oh oh oh

#

I see what's happening

#

i mis-read my own code (or I should say, I misread Martin's code I copied/pasted)

fervent vale
#

So float first gets releveled to a 512 indexed image, which means it becomes uint16

#

Then this uint16 indexed image is replaced with the lut entries to become an RGB uint8 image

#

It's possible to have luts other than 256 or 512 entries

rough furnace
#

do you think there is value in benchmarking size of LUTS that are both uint8 and uint16? or should I just benchmark the uint16 case (and the None case)

fervent vale
#

256 entry is the more common value used for colormaps. But 512 entry is the one that people will encounter while using ImageView widget with float data

rough furnace
#

easy enough to do both

fervent vale
#

But calling them 8-bit or 16-bit is a big source of confusion

rough furnace
#

lut_length ?

fervent vale
#

You could even pass in a 70_000 entry lut and then ImageItem should create an uint32 indexed interim image

#

Or perhaps it doesn't

#

Of course, in Qt6, there's support for RGBA64 QImage. That would be the real 16-bit depth

rough furnace
#

Thanks for the comments on that PR

rough furnace
#

this is new... right click and select export, no dialog comes up, but get this in the console:

XXX lineno: 135, opcode: 149
XXX lineno: 313, opcode: 151
Error in sys.excepthook:
SystemError: unknown opcode

Original exception was:
SystemError: unknown opcode
XXX lineno: 148, opcode: 150
XXX lineno: 313, opcode: 151
Error in sys.excepthook:
SystemError: unknown opcode

Original exception was:
SystemError: unknown opcode
XXX lineno: 58, opcode: 150
XXX lineno: 313, opcode: 151
Error in sys.excepthook:
SystemError: unknown opcode
#

looks like I was doing something silly w/ my instantiation of QApplication

rough furnace
#

here is the recreated plot on my machine from the first paper; it's remarkable how well numba fits between cuda and pure numpy performance wise.. also remarkale how little impact having a LUT has (if you have levels) once your images get large

rough furnace
#

pijyoi. as likely super obvious I haven't looked at the imageitem code much, but if I am following things correctly before, historically, almost all calls to ImageItem.setImage() would eventually get routed through makeARGB; but over recent years; you've implemented various code optimization paths that uses input conditions that are suitable for direct rendering, and sets the format accordingly, bypassing...

now, the only time makeARGB is called is when _try_make_qimage returns None, meaning we have a non-supported configuration

in your comment on the asv PR, you mentioned that hopefully none of the parametrized inputs would call makeARGB, but if I'm reading this right, wouldn't any combination that uses levels or LUTs with 3 channel data, regardless of dtype, cause makeARGB to run?

fervent vale
#

Right, 3 channel data with levels is not short circuited. But I think the asv tests did not test this case to begin with. I am not sure what operation multichannel data with LUT is supposed to represent, or if it is even supported by makeARGB.

fervent vale
#

By default QImage uses ARGB32 format internally, so that's probably why we have a makeARGB. But that's also the cause of the additional complexity in handling endian-ness

rough furnace
#

Oh that’s right, that asv test only does 1 channel data

fervent vale
#

test_ImageItemFormat.py enumerates all the short circuited cases

fervent vale
#

From there I see that a 3-channel uint8/uint16/float32 image with a single levels for all 3-channels and no lut does get short circuited

#

It's not that 3-channel data with their individual levels couldn't be short-circuited. It was a matter of optimising only the more common cases. You can see that there are enough combinations as it were.

#

I think 3 channel data with individual levels couldn't be made fast if the processing code were to handle each channel separately. Unfortunately, handling them in one pass could only be done in numba

#

From your timings chart, numpy is processing at 500 megapix/sec, while numba is processing at 1000 megapix/sec

#

Numba is able to do the rescale operation in 1 memory pass

rough furnace
#

appreciate the context/explanations 👍

rough furnace
#

oh wait, we're using RGBA64 format in the repo already...

#

oh, but what's new in Qt 6.2 is Format_RGBA32FPx4

fervent vale
#

According to test_ImageItemFormat.py, if the user passes in an RGBA64 image with no levels and no lut, then it gets wrapped into RGBA64 with no processing.

#

Similarly, uint16 single channel with no levels and no lut gets wrapped into Grayscale16

rough furnace
#

yeah; got that part ... was thinking once I clean up the asv code I may make a PR attempt to implement Format_RGBA32FPx4 since I'm already looking at this

fervent vale
#

If any processing takes place, the final output will be of dtype uint8. We don't output a higher bit depth

#

I think normally for floating point output, whether for image or for audio, the data is expected to be normalized to (0.0, 1.0) and (-1.0, 1.0) respectively

#

So if the user were to pass in 4-channel floating point data, to wrap and pass it through with no further processing would require the input data to be pre-normalized to (0.0, 1.0)

rough furnace
#

yeah, I would imagine (0, 1)

#

when I clean up the ASV stuff and am ready to merge it, I'll poke at the float32 format as see what happens when you pass values outside of that range

fervent vale
#

and furthermore, it doesn't make sense to apply levels to the alpha channel

rough furnace
#

...yeah not sure what the expected behavior there would be for that 😬

fervent vale
#

It might have been useful to have a GrayscaleFP32 format

#

but less so for RGBA32FPx4

#

for scientific data, that is

rough furnace
#

I should poke at Luke and see if he deals w/ those formats ever, ... he's knows that neuroscience imaging equipment pretty well...

fervent vale
#

if you are thinking of monochannel input being processed to RGBA32FPx4 output, that would only make sense if the LUT colormap was in float

rough furnace
#

in audio data, yeah, I deal w/ single channel data values, and LUTs and Levels

fervent vale
#

but you would end up with a QImage that's 4x the size of your original mono-channel input

rough furnace
#

I think I'm still missing something here; so in ImageItem._try_make_qimage() it's looking at attributes of the incoming data, levels, lut and trying to see if there is a QImage format that can be used, where we can create a QImage from the data directly, avoiding any kind of processing; ...

what I'm missing here is why wouldn't it be beneficial to add checks for the RGBA32FPx4 format; see if the incoming data conforms to that, and if so, set the format to that, and generate a QImage that way (like with RGB888, Grayscale8, Indexed8 and so on

fervent vale
#

RGBA32FPx4 has stricter requirements than uint8 and uint16. The data has to be pre-normalized to (0, 1)

rough furnace
#

ahh, so issue is w/ the other formats, we're effectively bound by the limit of values that can be represented, so that pyqtgraph can effectively cover all the cases w/ those data formats...

fervent vale
#

In the current pyqtgraph API, there is a requirement that float data comes with levels

rough furnace
#

oh, yeah, that too 😆

fervent vale
#

So, if the user just so happened to have a fully-compatible RGBA32FPx4 data array, they can't communicate this fact to pyqtgraph

rough furnace
#

got it, that makes sense

fervent vale
#

In fact, that's true for mono-channel float too. The user can't communicate that they have already pre-scaled to (0, 1)

rough furnace
#

wonder if we can put in the docs to use levels=[float("nan"), float("nan")] to indicate that data is pre-scaled accordingly.

#

feels weird and out of place, guess this is just a solution looking for a problem as this hasn't been requested by anyone

fervent vale
#

the float needs levels requirement is in makeARGB

#

it could possibly be changed to "levels is None for float means (0, 1)", but that could just end up being more surprising behavior

rough furnace
#

yeah, i'm convinced that absent a good use case that this isn't a good use of time/effort.

fervent vale
#

looking at test_ImageItemFormat.py, for LUTs that have <= 256 entries, the code will output an Format_Indexed8 image

#

This results in much lower memory usage than an RGB[AX]8888 image

#

In the first timings table that you posted on 2023/07/13, you will see that small LUTs have a significant timing advantage over big LUTs

#

The thing is that HistogramLUTItem defaults to giving big LUTs (512-entry) for float images

#

So for someone using ImageView to interact with their data, they don't get this small LUT speed advantage

rough furnace
#

You think we should change the default LUT entries for HistogramLUTItem ?

rough furnace
#

@elder eagle thanks for providing help for folks asking pyqtgraph questions in the help channel; every few weeks I remember to search this server for mentions of pyqtgraph outside of this channel...

if someone asks a tougher question, feel free to direct them here 👍

fervent vale
rough furnace
#

I don't know the answer to this one, but this code is pretty old, and likely Luke would have an answer (if one exists)... he'll answer on slack when pinged (eventually)

fervent vale
#

VideoSpeedTest even asks for a 4096-entry LUT

rough furnace
#

That…. Seems excessive

strong dust
#

Hi, I have a question on the GUI side of things. I noticed that when I have a HistogramLUTItem connected to an ImageItem I can click on the ticks to bring up a colour selector to change the colour on the tick (which is a great feature btw). If I then click somewhere else and the colour selector window ends up behind my program, then it's not brought forward by clicking on the tick again. Is this intentional, and, in that case, are there any workarounds you can think of?

fervent vale
#

Adding self.colorDialog.raise_() to the end of GradientEditorItem::raiseColorDialog() seems to achieve what you want.

fervent vale
#

I am working on a HistogramColorMapItem that fills a gap not served by HistogramLUTItem nor ColorBarItem. In scientific images, it is useful to see a histogram so that the user can adjust the levels to remove the noise or background. ColorBarItem doesn't have that and HistogramLUTItem assumes gradients with small number of stops rather than 256-entry colormaps

#

The ability to edit the gradient on-the-fly isn't needed

rough furnace
#

TIL about Glumpy, a numpy <-> opengl plotting library

https://glumpy.github.io/

wonder if there is a collaboration to be had or things we can 'borrow' from each other. Author is fairly active on social media

#

hmm... the images look to be the same as vispy?

fervent vale
#

I tried out glumpy maybe 10 years ago. I think it was to do a pcolormeshitem.

#

pcolormeshitem can be really fast on opengl

#

the pyqtgraph one is relatively slow

rough furnace
#

I like how it seems to handle a variety of non-linear transformations

fervent vale
#

back then I was using wxPython

rough furnace
#

In preparation for an upcoming move, we have started doing “bucket list” items on weekends with the kids; going to be slow to review/merge stuff.

rough furnace
#

filed a support request w/ github to take a look and see why github doesn't identify pyqtgraph as being used as a dependency on any library (hence we don't have a "used by" counter)

fervent vale
#

The discord button link reports "invalid server"

rough furnace
#

Saw that, when I tried clicking on it, it seemed to still work?

rough furnace
#

used by section in our project page now works!

fervent vale
#

hmm, CodeQL doesn't like my (first) use of the walrus operator?

rough furnace
#

I’ll try and take a look closer today. Generally i view CodeQL warnings/errors as things I should take a closer look at.

fervent vale
#
if (cmap := get_cmap()) is None:
    cmap = default_cmap
grad = cmap.getGradient()  # cmap potentially not initialized
rough furnace
#

heh, i think before the SVG fixes, using QGraphicsRectItem would result in a bad SVG export, but because ColorBarItem used ImageItem under the hood, that's why the SVG export worked there (I in theory have fixed that now so it should be a non-issue now.

fervent vale
#

ColorBarItem uses 256 as its 100% full scale range, which I think seems to be partially caused by the use of ImageItem

#

You will find these values sprinkled in the code: 256, 63, 191, 64

rough furnace
#

what is 191 supposed to represent?

fervent vale
#

It's about 3/4 of 256

#

I think it's tricky. If 1.0 maps to 255, then what do 0.25, 0.50 and 0.75 map to?

#

In pyqtgraph, when rescaling, each lut entry gets an equal proportion of the pie. E.g. in a 4-entry LUT, 0..0.25 maps to 0 while 0.75..1 maps to 3

rough furnace
#

What exactly is the difference between a lut and a gradient. My understanding is that is LUT is a color map with points on the scale mapping to specific colors, and values between adjacent points in the LUT have their color interpolated. Does a gradient just have fewer “fixed” points?

#

Or with the LUT is there no interpolation?

fervent vale
#

For a LUT, the data is quantized to the number of entries of the LUT and then mapping occurs with no interpolation

#

For a gradient, the data is not quantized and each data point is smoothly interpolated to the stops defining the gradient

#

pg.ColorMap internal structure is a gradient

#

You can ask it to give you a LUT from the gradient

#

However, all the on disk colormaps are defined with 256 entries, which get loaded by ColorMap as a gradient with 256 stops

rough furnace
#

I'm getting it, I don't have a clue of what should be the preferred or default behavior tho, I just don't do that much scientific work w/ images.

rough furnace
#

taking a peek at stackoverflow, who is relent95 that's answering so many pyqtgraph questions I wonder

fervent vale
#

It seems like HistogramLevelsItem would fit this use case

rough furnace
#

when chatting w/ Thomas (mpl maintainer), sounds like they just do LUTs, no interpolation

rough furnace
#

QGraphicsPixmapItem; ...TIL...didn't know that was a thing 😂 probably the right item to draw for your PR

rough furnace
#

getting a surprising amount of activity (not a lot, but more than none, hence surprising) with the #pyqtgraph hashtag on mastodon

fervent vale
#

Is the following True or False?

cupy.zeros(10).dtype == numpy.float64
rough furnace
#

Give me a minute….

fervent vale
#

thanks. otherwise there would have been difference in behavior if the user (unknowingly) received an cupy array and wanted to check whether it was a certain type.

#

there is such a code in HistogramLUTItem::getLookupTable

warped abyss
#

hey

#

hey

bold thicket
#

hey i jus finished a bootcamp course in python from udemy what should i do now to move ahead?

rough furnace
bold thicket
#

alright

copper lily
#

where could i learn pyqt6
any youtube videos?

rough furnace
slow cedar
#

that's from where I learned pyqt6

rough furnace
fervent vale
#

I must say, it's only after working on #2779 that I am now understanding pyqtgraph's UML inheritance diagram. I don't think I understood before the difference between GraphicsWidget and GraphicsObject

rough furnace
#

haha it's so handy, right?!

#

this was a constant sore point with me, so when it got proposed, I knew it had to merged, even if the diagram was not generated in CI, but pre-generated in a proprietary application

pseudo yarrow
#

Hi @rough furnace, I just noticed that the link in the readme to this discord server is broken. Thought I’d let you know

rough furnace
#

hey there, thanks for pointing that out. I did see that, but as far as I could tell, the link worked; but as I'm already on this server it's tough to tell, I suppose I could try leaving the server and see if that link brings me back here and to this channel...

rough furnace
#

@fervent vale multiple inheritance has never worked for us with pypyside, correct?

#

on that issue, Christian Tismer is asking a question which implies that they think that issue was resolved at some point... but if memory serves from your CI runs, it never worked in our use-case?

fervent vale
#

LHS inheritance has crashed PyPySide since its first release

#

The fix at the time was to switch GraphicsObject and GraphicsWidget to inherit GraphicsItem on the RHS

#

On the other hand RHS inheritance does not allow method overriding in {C,Py}PySide

#

So if a Qt class has a virtual method, RHS inheritance will not override that method

#

Apparently, PyQt supports method overriding with inheritance on the RHS

#

But that feature is contrary to python method resolution order

rough furnace
#

just so I'm clear, LHS inheritance would be where the Qt object we're inheriting is on the left?

class MyObject(QObject, object)
#

or wait, I have it backwards...

fervent vale
#

Python Mixin on the left. Qt class on the right

fervent vale
#

According to the last section of the above link, L and D colormaps should have been named with 2 digits, with a leading 0 if necessary

#

Our files are named without the leading 0

rough furnace
#

Ahh. Should git mv them then…

fervent vale
#

In fact, we can't rename them either, because the well known colormap name (e.g. "CET-L2") is derived from the filename

rough furnace
#

o....yeah that won't work then 😬

merry lintel
rough furnace
#

Maybe! I’ll update it later today.

distant maple
#

Yes?

#

I was pinged

rough furnace
#

perhaps we should consider different default parameters for line plot update benchmark:

#

greater than 2,000 fps isn't a particularly meaningful number 😂

torpid mica
#

Hello #pyqtgraph ,
I would like to use PySide2 with python 2.7, however Pip install requiert higher Python version.
I have being told to build Pyside2 for python 2.7 but I have no idea how to do.
Does anyone can explain me how to do ?

#

Am I on the correct channel ?

rough furnace
#

We can’t help with building of pyside packages; although I thought some versions of pyside2 supported Python 2.7 so I’d be surprised if you had to build from source

torpid mica
#

Do you know any guide on how to build packages from source ?

rough furnace
#

The pyside wiki gives instructions but if you’re looking for an older version for Python 2.7 you may have a hard time finding it

torpid mica
#

Thanks ! I will give a look

rough furnace
#

@fervent vale I saw your comment; I’m good with a rename of the existing color map to gradient, throw a warning in the docstring or something like that. Need to review that PR more closely for sure.

fervent vale
#

1 behavior changing decision: change HistogramLUTItem to return 256-entry LUT instead of 512-entry?

#

The change can also be made in ImageItem to explicitly request 256-entry LUT

rough furnace
#

Yeeeesssss

#

Absolutely

rough furnace
#

<@&267628507062992896> yo, please nuke the above ☝️ (EDIT: thank you!!)

rough furnace
#

realized I tagged the wrong group, my apologies

rough furnace
#

pijyoi, I finally added your fork to my remotes on my new laptop, saw you had a now several years back for trying to adopt modern opengl stuff; would that be an effort you would like to continue w/ some collaboration?

#

(also going through your color map display item PR now)

fervent vale
#

Looking at the code, I was trying to learn some trivial modern opengl to see if the RawImageWidget could run on OpenGL ES on the raspberry pi.

rough furnace
#

I briefly looked at OpenGL ES, but ruled it out as I had the impression it was aimed more for mobile platforms, didn't consider the raspberry pi; which I think there is a good argument to try and support hardware acceleration there

fervent vale
#

Actually the 64-bit distros for rpi come with Qt compiled for OpenGL (not ES)

rough furnace
#

i've been slowly going through https://learnopengl.com I am hoping that we can use it to handle non-linear transformations

fervent vale
#

I think using opengl native painting will eventually stop working on Qt?

rough furnace
#

I would be surprised if that got removed from Qt, but I did see a comment discussing QRhi support for QPainter, which is apparently being worked on, would a lot like the current OpenGL QPainter support

#

oh, but if 64-bit distros of RPi come w/ Qt compiled for OpenGL, then yeah, we can stick to OpenGL 3.3-4.1

fervent vale
#

The real "meat" of the colormap PR is ColorMapMenu and ColorMapDisplayMixin

#

They reside in widgets/ColorMapButton.py

#

They can be used to build a colormap menu for use in other places

#

Like for ColorMapDisplayItem (which could actually be removed from the PR)

#

If the old gradients are to be considered "legacy", I would suggest that the new "turbo" colormap to not be included in gradient form.

rough furnace
#

Had to put the review on hold. With repurposing names the one concern I have is we have no warning or deprecation period assigned. I need to review more closely to better evaluate what is most appropriate

#

(Put on hold due to taking my daughter for a walk on the beach).

rough furnace
#

So what's the preferred method of showing a ColorMap as a GraphicsItem, I guess that would be ColorBarItem? Right now the PR shows PColorMeshItem using ColorBarDisplayItem, and NonUniformImage uses HistogramLUTItem;

fervent vale
#

For image applications where a histogram is visually useful to interactively adjust the levels, HistogramLUTItem is still the item to use

#

All existing uses of HistogramLUTItem continue to work but now have access to the lut colormaps. Including those from mpl and colorcet

#

ColorBarDisplayItem is more of an example of how else ColorMapMenu could be used

#

For bigger applications that include use of a parameter tree, the colormap could be selected from the ColorMap parameter

#

At the moment I don't have applications where ColorBarItem would be a good fit

#

But a HistogramLUTItem without the ability to adjust the stops is usually what I need

#

If taking over the "colormap" parameter is not a good idea, it can be reverted. And use a better name than "colormapex"

rough furnace
#

i think it's fine

copper lily
#

test

rough furnace
mystic bison
#

sorry i forgot to delete - it was someone automating their account and they're now banned

rough furnace
pure grove
#

Hello

#

How are you guys??

#

@zealous magnet how are you??

rough furnace
#

if I'm reading about geometry shaders right; looks like we can handle non-linear transformations that way? likely can't handle arbitrary transformations, but can pre-define them that way?

rough furnace
rough furnace
#

pijyoi, I think you're right to target RawImageGLWidget for testing the waters w/ modern opengl tho

rough furnace
#

also, to get more opengl benefit, ideally (ignoring the amount of work involved) wouldn't we want to move makeARGB computation to the fragment shader?

rough furnace
#

lastly, I'm not sure there is much benefit for keeping support for opengl < 4.1; opengl 4.1 has been out for a long time. On apple machines, macOS 10.9 added support for OpenGL 3.3, 4.0, and 4.1; I'm fairly sure you cannot get a pyqtgraph supported version of numpy on OS X 10.9 (not without substantial work anyway).

probably should see what version of linux added support for OpenGL 4.1

#

looks like OpenGL 4.1 is supported on Nvidia Geforce 400 series cards, AMD 5000 series cards (so looks like my nvidia Geforce GT 330M in my mid-2010 macbook pro wouldn't get the cut 😂 , it only goes to 3.3)

not sure what the situation is on more embedded platforms like the Raspberry Pi

fervent vale
#

makeARGB() was created at a time when there wasn't an Format_Indexed8

rough furnace
#

not sure how that helps for RawImageGLWidget tho, in the paintGL method there is an explicit call to fn.makeARGB(img, *args, **kwds), and the output from that gets eventually relayed to

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, self.image)

suppose we could "fastpath" stuff here by having some codepaths that match up w/ the texture arguments

#

o, the format options aren't particularly helpful to us, only one that likely impacts us is GL_RGBA anyway...

fervent vale
#

One slowdown is in the output of ARGB32 format

#

The generic code to make populate the numpy array to make ARGB32 is slow

#

this could actually be done by converting the cmaplut to ARGB

#

then the output of np.take would already be correct

#

(or in this case, RGBA)

#

actually makeARGB() could be entirely removed in favour of makeRGBA()

#

there are 2 useful formats for OpenGL Texture

#

RGBA and the single channel format

#

but really, who's using RawOpenGLImageWidget?

rough furnace
#

nobody 😄

#

but figure its a good place to tinker w/ modern opengl stuff and see if there is much benefit in trying to modernize the opengl stuff elsewhere

fervent vale
#

there's also a leakage of OpenGL object ids

#

I think they don't get destroyed

rough furnace
#

for context, I cherry-picked the first commit on your modern-opengl branch

rough furnace
#

Not seeing anything about raspberry pi supporting non-ES versions of OpenGL, not sure if we should try supporting OpenGL ES just for the Raspberry Pi tho... then again, I have no idea what we give up from going from standard OpenGL to OpenGL ES

compact matrix
#

please i need help on these

rough furnace
fervent vale
#

The RPI3 hardware supports both OpenGL 2.0 and ES 2.0. What matters is whether the Qt library is compiled for Desktop GL or ES. There was also the issue of the OEM graphics drivers being 32-bit. So on 64-bit distros, Qt is compiled for Desktop GL. I am not sure whether GL is running software emulated in that scenario though

rough furnace
#

Do you know of an easy way to check if it’s software emulation or actual hardware acceleration? I don’t have a RPi 3 or 4, but I can likely track one down for testing

fervent vale
#

I think the info that glinfo.py prints out would tell you. Specifically the GL_VENDOR and GL_RENDERER

#

I see that the turbo cmap and gradient got merged. Would it be right to say that gradients aren't considered "legacy"?

rough furnace
#

I really need to get to making that user survey, I honestly have no idea how often parts of the library are used. I suspect acq4 uses gradients … but likely for legacy reasons

fervent vale
#
isOpenGLES: False
VENDOR: Mesa/X.org
RENDERER: llvmpipe (LLVM 11.0.1, 128 bits)
VERSION: 3.1 Mesa 20.3.5
#

Raspberry Pi OS 64-bits on an RPI3 over an ssh connection (X11 forwarding). Using distro provided Qt and PyQt5 5.15.2. We can see that Qt is compiled for Desktop GL. But OpenGL is emulated because of ssh.

#

Too much of a hassle to plug in a monitor and keyboard.

fervent vale
#

When connecting over VNC

isOpenGLES: False
VENDOR: Broadcom
RENDERER: VC4 V3D 2.1
VERSION: 2.1 Mesa 20.3.5
rough furnace
#

hmm.... in that case I think we should probably preserve the current OpenGL implementation which works on OpenGL 2.1, and add a codepath for "modern" GL (4.1?) ... there are very few devices out there that support 3.3, 4.0 but not 4.1 (but they do exist)

fervent vale
#

Modern OpenGL refers to >= 3.3 right?

#

An old 2014 thread where Vispy was the way going forward

rough furnace
#

I was thinking back on the issue involving font lettering in OpenGL being not as sharp as the non-OpenGL variant, seems like most recommendations are to use FreeType, ... which seems like that's what matplotlib depends on as well...

fervent vale
#

I think that was because the non-OpenGL engine knew how to use sub-pixel rendering?

rough furnace
#

Yeah sounds about right, we may not be able to use quainter::drawtext

#

When using the OpenGL renderer

rough furnace
#

pijyoi, maybe you know this, but I can't understand what would be the purpose of using the QOpenGLWidget/QOpenGLWindow but using paint() methods instead of paintGL() ... I know with paint() you can reuse the non-opengl draw code, but why would anyone want to do that?

fervent vale
#

I suppose GLPainter.py example would be one use case. Do all the OpenGL rendering followed by a single over-painting pass.

compact matrix
#

hello anyone here to help

rough furnace
fervent vale
torn minnow
#

imagine pyqtgraph+shader code

rough furnace
rough furnace
#

just getting a sense for how deep the rabit hole is for thick lines in "modern" opengl, @torn minnow do you have much familiarity with working with opengl?

rough furnace
#

i'm right now going through this GitHub looking at how to generate thick lines: https://github.com/mhalber/Lines

Don't want to use the CPU based method due to by far the slowest implementation, I can't use the SSBO since that's a OpenGL 4.3+ feature (and with macOS I'm stuck on OpenGL <=4.1) which leaves the Geometry Shader Lines, the Instancing Lines and the Texture Buffer Lines methods; anyone here think that we should lean towards one method more than another?