Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix inverted LUT and blending #5487

Merged
merged 30 commits into from Feb 23, 2023
Merged

Conversation

brisvag
Copy link
Contributor

@brisvag brisvag commented Jan 17, 2023

Description

Followup on discussion #3914 (comment). This is an attempt at solving those issues.

This change blends "correctly" with the canvas in a translucent way, no matter the color of the canvas, and it does so transparently to the user (everything happens in the vispy backend). So you can actually change opacity of every layer and have it behave as expected, even with a non-black canvas.

Unfortunately, openGL does not use blending parameters for the MIN and MAX blending functions. So, if you have multiple channels in separate minimum blending image layers, you have 2 options:
- the bottommost one is not minimum -> you get ugly stuff if you change opacity to not be 1
- the bottommost layer is minimum -> you cannot change opacity

As mentioned in the discussion, a way around it may be to actually reroute the changes to opacity on a layer with minimum blending so that they actually modify the contrast limits. But that's even more magic :p

cc @psdobolewskiPhD

Type of change

  • Bug-fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

References

How has this been tested?

  • example: the test suite for my feature covers cases x, y, and z
  • example: all tests pass with my change
  • example: I check if my changes works with both PySide and PyQt backends
    as there are small differences between the two Qt bindings.

Final checklist:

  • My PR is the minimum possible work for the desired functionality
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • I have added tests that prove my fix is effective or that my feature works
  • If I included new strings, I have used trans. to make them localizable.
    For more information see our translations guide.

@brisvag brisvag changed the title simple switch of blending func Fix inverted LUT and blending Jan 17, 2023
@brisvag
Copy link
Contributor Author

brisvag commented Jan 17, 2023

Note that both in main and here, anything below a minimum blending layer (say, some innocent points...) will wreak havoc to everything else due to the image being blended with the point :P

@codecov
Copy link

codecov bot commented Jan 17, 2023

Codecov Report

Merging #5487 (0856db2) into main (1477132) will increase coverage by 0.06%.
The diff coverage is 100.00%.

@@            Coverage Diff             @@
##             main    #5487      +/-   ##
==========================================
+ Coverage   89.35%   89.42%   +0.06%     
==========================================
  Files         609      609              
  Lines       51170    51227      +57     
==========================================
+ Hits        45724    45809      +85     
+ Misses       5446     5418      -28     
Impacted Files Coverage Δ
napari/_vispy/utils/gl.py 88.09% <ø> (ø)
napari/_qt/qt_viewer.py 79.30% <100.00%> (+0.32%) ⬆️
napari/_tests/test_with_screenshot.py 91.61% <100.00%> (+1.05%) ⬆️
napari/_vispy/layers/base.py 96.61% <100.00%> (+0.41%) ⬆️
napari/_qt/dialogs/qt_package_installer.py 81.81% <0.00%> (+0.39%) ⬆️
napari/utils/theme.py 94.04% <0.00%> (+0.59%) ⬆️
napari/_qt/qt_event_loop.py 80.95% <0.00%> (+0.68%) ⬆️
napari/utils/interactions.py 75.47% <0.00%> (+2.83%) ⬆️
napari/_qt/widgets/qt_color_swatch.py 74.79% <0.00%> (+3.25%) ⬆️
napari/utils/info.py 85.10% <0.00%> (+4.25%) ⬆️
... and 2 more

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

@psobolewskiPhD
Copy link
Member

Wow! Way to steal my thunder! 🤣

Being able to freely swap layers is pretty cool. And this would permit opening multichannel images with all the same blending mode, rather than the current behavior of translucent for bottom, then additive.

Some observations from testing:

  1. Toggling visibility of the bottom layer still breaks things, because it's the literal bottom layer being fixed on the bottom-most visible. This would be expected from looking at the code. Seems fixable?

  2. switching to 3D makes the bottom layer disappear (MIP rendering), even in additive (aka default open Cells3D sample image, switch to 3D). What's wierd if you open Kidney 3D+3Ch and switch to 3D you will see only the top 2 layers. But if you toggle the visibility of the top one, you will see both of the bottom 2. But, if you toggle the visibility of the middle one, you still just see the top one.
    BUT: swapping the order of the bottom 2 layers twice fixes it. 🤯

  3. The Points & Shapes behavior is weird—even on live. If you have Points between some minimum blending layers (or additive ones) you will see the added points blend with above layers. For shapes, same. So black points/shapes in additive mode are just screen off whats above, a pretty cool effect.
    I guess this makes sense, but I'm not sure that it's actually desired.
    Additionally, the area of an empty Point or Shapes layer (aka the background around the objects) appears to be set to that of canvas, so minimum blending in Dark theme and additive in Light of layers above points don't work—except for the actual points 🤣 —no matter what you set the Points blending mode. Note the thumbnail always shows black for empty points/shapes, but you can check on live that additive blending fails, just open Cells 3D in Dark theme, place a Points layer below and switch to Light theme. Now toggle the middle layer (membrane) from translucent to additive. Now add a few black points and move them around and you will see like a search light the layers above 😜

I think the best solution there is to make the empty area of point/shape area of the layers have 0 alpha (empty) instead of being black or white.

@brisvag
Copy link
Contributor Author

brisvag commented Jan 18, 2023

Wow, what a mess xD Thanks for checking everything!

  1. Toggling visibility of the bottom layer still breaks things, because it's the literal bottom layer being fixed on the bottom-most visible. This would be expected from looking at the code. Seems fixable?

Good point, didn't think about that... This is more annoying, cause we need to check everything at a higher level, probably qt_viewer.py (and in future in the VispyCanvas object).

    BUT: swapping the order of the bottom 2 layers twice fixes it. exploding_head

Something's off with the order not being set correctly, it seems; I'll look into this.

3. The Points & Shapes behavior is weird—even on live.

The primary issue here is that mixing minimum blending with inverted LUT and non-mimum blending with normal colors (i.e: points) is just a Bad Idea®. The only sensible way to this is to have any minimum blending layer be together in a block, and the bottommost one use a non-minimum blending with the below.

But really, I don't know if there's really a good way to do it, short of having an extra all-white/all-black layer/minicanvas as a separator. Thios might be more doable once we have layergroups, maybe :P

@psobolewskiPhD
Copy link
Member

psobolewskiPhD commented Jan 18, 2023

The primary issue here is that mixing minimum blending with inverted LUT and non-mimum blending with normal colors (i.e: points) is just a Bad Idea®. The only sensible way to this is to have any minimum blending layer be together in a block, and the bottommost one use a non-minimum blending with the below.

The behavior isn't any different than with additive as far as I can tell.
Both additive and minimum display the same (but inverted) behavior that is dependent on canvas color.
To make it easier to see, open Cells3D in Light theme. Make a shapes Layer and draw a rectangle with black face color.
Drag the Shapes layer to the bottom—it will disappear because Membrane (previous layer[0]) is translucent, 100% opacity. Switch that layer to additive.
You will get white everywhere but the square, which will show the blended layers from above.

Edit: all of the above behavior is also true if you have a Dark viewer and use minimum blending instead of additive, with the square having face white

So there are 2 issues:

  1. the shape color is blending with the layers above (face and edge). I'm not sure if this should be considered a bug or feature at the moment. It's a strange behavior though.
  2. the empty space around the shape/point is also blending and using canvas color.
    One solution would be to allow the user to set the background color of points/shapes layers. Or maybe if the empty space was set to 0 alpha it would work?

@github-actions github-actions bot added the qt Relates to qt label Jan 18, 2023
@brisvag
Copy link
Contributor Author

brisvag commented Jan 18, 2023

All good points... I've hacked at this a bit. Changing blending modes in a few places, maybe I landed on something that makes sense. Still doesn't fix minimum, but maybe the rest looks ok now? It relies on the canvas having zero transparency as a start [automatically set to zero now!] :)

@psobolewskiPhD
Copy link
Member

Works super well, other than the blending with points/shapes at the bottom still being affected by theme in minimum—a (corner case)^2 ? Since minimum ignores alpha, then the alpha shenanigans don't work.
I've tried to look in the code, but I'm still not sure if an empty Points/Shapes simply has alpha 0 or actually a color set off theme canvas. Either way, the only way to fix the minimum blending would be to change the RGB of the color (which is all min uses), based off the colormap of the above layer.
At the moment if my bottom layer has a white->red colormap and I use translucent, then theme still affects opacity. In dark mode the layer blends to black, in light to white (expected, given that colormap).
It's that or give points/shapes a background color settable by the user?

@brisvag
Copy link
Contributor Author

brisvag commented Jan 20, 2023

I changed a few things again, please take a look if the extra issues are solved!

So, here's a more-or-less complete breakdown of how blending in this context works:

Blending takes a src (current layer) and a dst (existing color, so the canvas and layers blended so far). The 4 parameters in blend_func are abcd in the following equations for the final color and alpha (for the add_func blending, which is the case for all of our blending modes except minimum):

color = a*src_color + b*dst_color
alpha = c*src_alpha + d*dst_alpha

Knowing this, this is what we do:

  • canvas color can be set, but will always have alpha channel set to 0. This is irrelevant in terms of rendering the canvas itself, but affects blending of above layers.

  • translucent, translucent_no_depth have the same logic except for depth testing:

    • src_color is multiplied by src_alpha (so if alpha is low, colors are less saturated)
    • dst_color is scaled by 1 - src_alpha. This lets color "shine through" depending on how alpha changes. Note that this will let the canvas color through; unfortunately, the alternative is worse:
      • scale by dst_alpha would allow us to discard the canvas, but this will result in straight up additive blending in any other case (and thus bleached out colors at best).
      • what we really want is to scale it by min(1 - src_alpha, dst_alpha). Unfortunately, there is only the opposite of this available from opengl: src_alpha_saturate = min(src_alpha, 1 - dst_alpha)
    • alpha channels are simply added, cause we want two half-transparent things to add up to an opaque thing
      • actually, ideally we'd want something more complex (like 75%?) but I'm not sure we can handle this from here
  • additive is more mathematically correct, cause dst_color is scaled by dst_alpha, and we get proper additive blending, eventually ignoring the canvas.

  • minimum is a weird one, cause you cannot pass any parameters to it: it simply takes the minimum of rgba from one and the other. This means we cannot scale the dst_color in any way, so it passes through.

  • opaque is trivial

  • an additional detail is that the the bottommost layer gets some special treatment:

    • blending function is set to add_func
    • src_color is scaled by src_alpha to allow blending with the canvas for all modes except additive and minimum; these two simply have one, this way images have their zeros set correctly to black and white depending on the colormap (this was the main reason we started this whole endeavour in the first place).

So I think we have the most correct blending that we could hope for (without doing multi-pass rendering...). AFAIK The one limitation left is that if any translucent layer is below a minimum blending layer, and the translucent layer had any canvas color pass through, we get issues.

The workaround in the case of multichannel is to set the blending of the bottommost image to translucent instead of minimum. I don't think this should happen magically.


This is all well and good theoretically; however, with these changes I get a terrible flickering effect with the canvas color set to non-black, and I can't figure out why :(

@brisvag
Copy link
Contributor Author

brisvag commented Jan 20, 2023

@perlman, maybe you can see if I'm missing something obvious in the above?

@psobolewskiPhD
Copy link
Member

@brisvag I think things are working really well now!
I don't see any flickering, that I can tell anyways.

One odd thing: an empty Labels layer is solid black in Light theme. On live it's white and switching theme switches the shown color. The background of the labels layer should be transparent, so a singular, empty labels layer should be shown as the canvas color. But if that means alpha is 0, then with the changes, you get zeroing of both the src and dst colors, yielding a black box.
This means that any layers above with minimum also disappear.
I've suggested a fix that makes sense to me—and works.

Now in Dark mode this still happens with points/shapes/labels at the bottom, because they pass canvas color as you noted. It's a very corner case, but in theory one could permit the user to set the background color of those layers. Or maybe have a checkbox invert or something to you can match those layers to minimum blending.
But again, that only happens with one of those as the bottom, which seems not too likely? Or at least somewhat not common? my intuition is to put labels/points/shapes above the layer I am annotating.

depth_test=True,
cull_face=False,
blend=True,
blend_func=(src_color_blending, 'zero', 'one', 'one'),
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
blend_func=(src_color_blending, 'zero', 'one', 'one'),
blend_func=(src_color_blending, 'one_minus_src_alpha', 'one', 'one'),

I think this is needed to prevent black box when blending a src with 0 alpha.
This way if src has 0 alpha, then the src color is zeroed out and canvas color is used with full alpha.
This makes sense I think.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Nice catch; I'm thinking that maybe this needs to actually be different for minimum and additive like for src_color_blending? Otherwise I think if you have an additive stack with non-black canvas, you'd get leakage if you change opacity of the bottommost layer?

            dst_color_blending = (
                'one_minus_src_alpha'
                if blending not in ('minimum', 'additive')
                else 'zero'
            )

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Can you test the last commit?

Copy link
Member

Choose a reason for hiding this comment

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

To me the new behavior is still not right.
Try an image like File > Samples > Cell
Try to adjust opacity: no matter theme, it does nada (theme still plays a role
Use Light theme and make a Labels layer: black square.
Try Cells3D and put labels at the bottom—corner case, sure. Use Light theme and make both of the Cells layers minimum with one of the new inverted caps. On live this works fine, with this PR it's a black box.
I don't think it's possible to have a concept of translucent with alpha control for the bottom-most visible—or single—layer without taking into account the canvas.

In playing around with it, I get the most intuitive behavior when I comment out
color.alpha = 0
And:

if self.first_visible:
            # if the first layer, then we should blend differently
            # (ignore the canvas color but allow alpha blending)
            # if blending in ('minimum', 'additive'):
            #     src_color_blending = 'src_alpha'
            #     dst_color_blending = 'one_minus_src_alpha'
            # else:
            #     src_color_blending = 'one'
            #     dst_color_blending = 'zero'
            blending_kwargs = dict(
                depth_test=True,
                cull_face=False,
                blend=True,
                blend_func=(
                    'src_alpha',
                    'one_minus_src_alpha',
                    'one',
                    'one',
                ),
                blend_equation='func_add',
            )

This basically treats bottom-most-visible as a translucent layer. if you make it opaque (alpha = 1), which is the default, it eliminates blending with canvas. I think there is an expectation that if you make it less opaque, you will see an effect. So I think you have to blend with canvas, but at that point you control the effect
Just the minimum+Dark theme+bottom-most-visible is shapes/points/labels makes a black box, but I think that's really (corner case)^2 because those layers don't have their own cmap/background.

2nd caveat is if the bottom most layer is something likeadditive (or with my suggestion translucent) and 0.7 opacity and then switch to minimum. Obviously, we override minimum, so then the opacity can be used, but cannot be changed, because minimum setting overrides the slider. Again a real corner case.

Copy link
Contributor Author

@brisvag brisvag Jan 24, 2023

Choose a reason for hiding this comment

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

Damn, so sorry, I actually inverted that if statement at some point during committing :/ It should be:

            if blending in ('minimum', 'additive'):
                src_color_blending = 'one'
                dst_color_blending = 'zero'
            else:
                src_color_blending = 'src_alpha'
                dst_color_blending = 'one_minus_src_alpha'

This is so that no blending with the canvas happens with additive and minimum (so you have full black or full white as the "background" color).

Copy link
Member

Choose a reason for hiding this comment

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

Gah, I should have caught that—sorry!
Works super nice now and makes a lot more sense!

Copy link
Member

Choose a reason for hiding this comment

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

(Just for the record, the shapes/points/labels at the bottom of a stack with minimum & Dark theme does result in a black box, but pretty sure as mentioned about that requires letting those layers set their own background)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Awesome! Yup, and I think we can leave that out of this PR for now, but let's keep it in mind if this issue comes up :)

@brisvag
Copy link
Contributor Author

brisvag commented Jan 23, 2023

But again, that only happens with one of those as the bottom, which seems not too likely? Or at least somewhat not common? my intuition is to put labels/points/shapes above the layer I am annotating.

My feeling is: let's cross that bridge when we get to it. I don't see any reason to putting the points at the bottom, so we can always suggest switching order if that happens. If we see it coming up more than we think, we can add a switch as you suggest. But I doubt that the fix will be so obvious to solve the confusion about the bug and prevent people from asking "wtf?" xD

I don't see any flickering, that I can tell anyways.

Can you try with the cells 3D sample data and the following changes?

  • gray canvas
  • 3D view
  • points layer at the bottom
  • switch between blending modes a few times and move around the camera (do a full 360, for me there are some angles that behave worse)

It's not super reproducible on my side either, and now that I wanted a video I can't get it to happen :/

@psobolewskiPhD
Copy link
Member

Can't find a way to make flicker, sorry!

@psobolewskiPhD
Copy link
Member

@brisvag Thanks! I had something like that in mind but didn't know where/how to implement. Very clear. I just added a bit of clarity to the comments plus the toggle of visibility of the bottom layer, because I think this is actually a crucial element. On live if you open cells3D (additive over translucent default) it looks fine on white canvas (Light theme), but breaks when you toggle the vis of the bottom layer.

brisvag and others added 3 commits February 17, 2023 14:21
Co-authored-by: Peter Sobolewski <76622105+psobolewskiPhD@users.noreply.github.com>
@brisvag brisvag added the ready to merge Last chance for comments! Will be merged in ~24h label Feb 20, 2023
Copy link
Member

@jni jni left a comment

Choose a reason for hiding this comment

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

Beautiful work @brisvag!

@jni jni merged commit ad76cae into napari:main Feb 23, 2023
@brisvag brisvag removed the ready to merge Last chance for comments! Will be merged in ~24h label Feb 23, 2023
@Czaki Czaki mentioned this pull request Jun 7, 2023
@Czaki Czaki added this to the 0.4.18 milestone Jun 16, 2023
@Czaki Czaki added the bugfix PR with bugfix label Jun 16, 2023
Czaki pushed a commit that referenced this pull request Jun 18, 2023
# Description
Followup on discussion
#3914 (comment).
This is an attempt at solving those issues.

This change blends "correctly" with the canvas in a translucent way, no
matter the color of the canvas, and it does so transparently to the user
(everything happens in the vispy backend). So you can actually change
opacity of every layer and have it behave as expected, even with a
non-black canvas.

Unfortunately, openGL [does not use blending parameters for the MIN and
MAX blending
functions](https://www.khronos.org/opengl/wiki/Blending#Blend_Equations).
So, if you have multiple channels in separate `minimum` blending image
layers, you have 2 options:
- the bottommost one is *not* `minimum` -> you get ugly stuff if you
change opacity to not be 1
	- the bottommost layer is `minimum` -> you cannot change opacity

As mentioned in the discussion, a way around it may be to actually
reroute the changes to `opacity` on a layer with `minimum` blending so
that they actually modify the `contrast limits`. But that's even more
magic :p

---------

Co-authored-by: Peter Sobolewski <76622105+psobolewskiPhD@users.noreply.github.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Czaki pushed a commit that referenced this pull request Jun 19, 2023
# Description
Followup on discussion
#3914 (comment).
This is an attempt at solving those issues.

This change blends "correctly" with the canvas in a translucent way, no
matter the color of the canvas, and it does so transparently to the user
(everything happens in the vispy backend). So you can actually change
opacity of every layer and have it behave as expected, even with a
non-black canvas.

Unfortunately, openGL [does not use blending parameters for the MIN and
MAX blending
functions](https://www.khronos.org/opengl/wiki/Blending#Blend_Equations).
So, if you have multiple channels in separate `minimum` blending image
layers, you have 2 options:
- the bottommost one is *not* `minimum` -> you get ugly stuff if you
change opacity to not be 1
	- the bottommost layer is `minimum` -> you cannot change opacity

As mentioned in the discussion, a way around it may be to actually
reroute the changes to `opacity` on a layer with `minimum` blending so
that they actually modify the `contrast limits`. But that's even more
magic :p

---------

Co-authored-by: Peter Sobolewski <76622105+psobolewskiPhD@users.noreply.github.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Czaki pushed a commit that referenced this pull request Jun 21, 2023
# Description
Followup on discussion
#3914 (comment).
This is an attempt at solving those issues.

This change blends "correctly" with the canvas in a translucent way, no
matter the color of the canvas, and it does so transparently to the user
(everything happens in the vispy backend). So you can actually change
opacity of every layer and have it behave as expected, even with a
non-black canvas.

Unfortunately, openGL [does not use blending parameters for the MIN and
MAX blending
functions](https://www.khronos.org/opengl/wiki/Blending#Blend_Equations).
So, if you have multiple channels in separate `minimum` blending image
layers, you have 2 options:
- the bottommost one is *not* `minimum` -> you get ugly stuff if you
change opacity to not be 1
	- the bottommost layer is `minimum` -> you cannot change opacity

As mentioned in the discussion, a way around it may be to actually
reroute the changes to `opacity` on a layer with `minimum` blending so
that they actually modify the `contrast limits`. But that's even more
magic :p

---------

Co-authored-by: Peter Sobolewski <76622105+psobolewskiPhD@users.noreply.github.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Czaki pushed a commit that referenced this pull request Jun 21, 2023
# Description
Followup on discussion
#3914 (comment).
This is an attempt at solving those issues.

This change blends "correctly" with the canvas in a translucent way, no
matter the color of the canvas, and it does so transparently to the user
(everything happens in the vispy backend). So you can actually change
opacity of every layer and have it behave as expected, even with a
non-black canvas.

Unfortunately, openGL [does not use blending parameters for the MIN and
MAX blending
functions](https://www.khronos.org/opengl/wiki/Blending#Blend_Equations).
So, if you have multiple channels in separate `minimum` blending image
layers, you have 2 options:
- the bottommost one is *not* `minimum` -> you get ugly stuff if you
change opacity to not be 1
	- the bottommost layer is `minimum` -> you cannot change opacity

As mentioned in the discussion, a way around it may be to actually
reroute the changes to `opacity` on a layer with `minimum` blending so
that they actually modify the `contrast limits`. But that's even more
magic :p

---------

Co-authored-by: Peter Sobolewski <76622105+psobolewskiPhD@users.noreply.github.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Czaki pushed a commit that referenced this pull request Jun 21, 2023
# Description
Followup on discussion
#3914 (comment).
This is an attempt at solving those issues.

This change blends "correctly" with the canvas in a translucent way, no
matter the color of the canvas, and it does so transparently to the user
(everything happens in the vispy backend). So you can actually change
opacity of every layer and have it behave as expected, even with a
non-black canvas.

Unfortunately, openGL [does not use blending parameters for the MIN and
MAX blending
functions](https://www.khronos.org/opengl/wiki/Blending#Blend_Equations).
So, if you have multiple channels in separate `minimum` blending image
layers, you have 2 options:
- the bottommost one is *not* `minimum` -> you get ugly stuff if you
change opacity to not be 1
	- the bottommost layer is `minimum` -> you cannot change opacity

As mentioned in the discussion, a way around it may be to actually
reroute the changes to `opacity` on a layer with `minimum` blending so
that they actually modify the `contrast limits`. But that's even more
magic :p

---------

Co-authored-by: Peter Sobolewski <76622105+psobolewskiPhD@users.noreply.github.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bugfix PR with bugfix qt Relates to qt tests Something related to our tests
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants