wxWidgets / Phoenix

wxPython's Project Phoenix. A new implementation of wxPython, better, stronger, faster than he was before.
http://wxpython.org/
2.28k stars 514 forks source link

Visual Glitch From Choice And Button Widgets After Hiding And Showing A Panel #763

Open PendulumDreams opened 6 years ago

PendulumDreams commented 6 years ago

Operating system: Windows 10 Pro Version 1709 Build 16299.192 wxPython version: 4.01 Stock or custom build: Stock Python version: 3.6.3 64 bit. Stock or custom build: Stock

Description of the problem:

Attached is a test file which replicates the issue and provides a partial workaround where I found an additional visual glitch and a full workaround. The window contains a panel on the left which contains a Choice and Button widget. On the right is a wrap sizer with a ScrolledPanel which contains three toggle buttons, one to show the initial visual glitch I found, a second to show the secondary visual glitch I found while trying to fix the first one, and a third button which runs code that works around the issue. I only noticed this issue because I had coloured the background of the scrolledpanel yellow while I was trying to set up the sizers. Used wxpython only once probably about 10ish years ago, but am making an application I will be putting up on github for free and wxpython seemed like a better choice over tkinter because tkinter has issues with becoming an executable. visual_glitch.zip

To replicate the issue press the Visual Glitch toggle button twice. On first click it will hide the panel on the left and on the second click it will show the panel again. This is what I am seeing after the second click. You can tell the stray Combo is a visual glitch if you resize the window to cut off part of what looks like the widget and then resize larger. visual_glitch

If you restart the application and click the partial work around button twice I see the following: visual_glitch2

And then of course if you use the work around button I don't see any visual glitches.

Not sure if other widgets have a simlar issue. If you need any more information let me know.

mesalu commented 6 years ago

It looks like your widget hierarchy is a bit like this:

+-------+    +----------+     +--------------+
| Frame +--+-+Scrolled  +--+--+Vis.Glitch    |
+-------+  | +----------+  |  +--------------+
           |               +--+Partial. W.A. |
           |               |  +--------------+
           |               +--+Work Around   |
           |                  +--------------+
           | +-----+          +--------------+
           + |Panel+-------+--+wx.Choice     |
             +-----+       |  +--------------+
                           +--+Reset Filters |
                              +--------------+

where the Scrolled object and Panel (second level) are competing for space between toggles.

This behavior stems from an old wxWidgets thing wherein "The behavior of overlapping sibling widgets is undefined"

What seems to be happening is that when the ui_filter_container is re-shown the children are being drawn onto the scrolled panel first, which is then being resized with the damage to its drawable area already done. In the second case, the Reset Filters button is the one doing the damage to the ScrolledPanel.

There are two approaches to fixing this:

Other Notes

OnInit instead of __init__

You should do app-level initializaiton in the App's OnInit method, rather than overriding the initializer. Returning True from that method tells wx that everything is fine, and False indicates a failure and will prevent the app from running.

wx.StatusBar

Also, you may want to look into: https://wxpython.org/Phoenix/docs/html/wx.StatusBar.html to see if its sufficient for what you have planned for ui_status_container

Fancier Sizers

I figured while I'm at it, I may as well point you in the direction of some of the more fun sizers too: https://wxpython.org/Phoenix/docs/html/wx.GridBagSizer.html

This sizer allows you to create a grid and then have widgets assigned to it span across flexible rows and columns (that is, rows and columns can be permitted to grow/shrink independently of eachother)

In essence this allows the layout you have going now, which consists of several sizers, boil down to the one top level sizer in the Frame, and then whatever sizers exist in the Frame's children.

PendulumDreams commented 6 years ago

That is the widget hierarchy. Of course there are more widgets in the actual application but those were all that I noticed with glitches. And I just added the buttons to the wrapping area because I temporarily put a bunch of buttons in there to test resizing. I didn't realize I left in the ui_status_container, wasn't needed to show the issue.

As for the StatusBar widget, I'm not planning on using it and I was planning to use a ribbon but have decided not to. If you look at the style of the newer Windows 10 applications (like print 3d or paint 3d) they no longer use a ribbon or status bar. Of course they use windows 10's new acrylic system which looks really nice, but I don't need that.

Because of what the application is being designed to do I need to have a colour theme in it which can be toggled on and off and uses no blue. The application will manage and control wallpapers so that in the morning it will show wallpapers that are bright and blue to help you wake up and nearing the time you want to sleep it will shift towards wallpapers that are darker and with less and less blue. And flat colours (no gradients) will look better on Windows 10. The application uses lots of windows api calls because of the requirements to switch wallpaper but has been built in a way where mac and linux code could be added so the application would work on them.

Thanks for the pointers about the other things. I'll definitely switch to hiding the sizer instead of each widget so that the glitch goes away with less code.

As for the sizer, I've seen and used a couple but didn't notice that one yet. I'll have to take a look. By the time I get everything working the way I want and have it ready for release I'm sure I'll know more about wxPython.

RobinD42 commented 6 years ago

There are two factors at play here. First, Windows will not redraw something if it thinks it doesn't need it, despite the fact that without native layered double buffering it is easier than it should be for something to mess with the on-screen image of something else. Second, overlapping sibling windows (the panel and scrolled panel in this case) is just the sort of thing that can cause that kind of damage.

Fortunately the fix/workaround is usually simple. Just refresh the window that is being damaged. In your example changing the filterToggle method to look like this takes care of the problem for me:

    def filterToggle(self, event):
        # Visual Glitch -- NOT!
        if event.IsChecked():
            self.ui_filter_container.Show()
        else:
            self.ui_filter_container.Hide()
        self.Layout()
        # added this line, removed the PostSizeEvent
        self.ui_wallpapers_container.Refresh()  
infinity77 commented 3 hours ago

@swt2c : it seems to me that @RobinD42 ‘s answer clears this up, I believe this can be closed