Why does my buffered GraphicsContext application have a flickering problem? - python

import wx
class MainFrame(wx.Frame):
def __init__(self,parent,title):
wx.Frame.__init__(self, parent, title=title, size=(640,480))
self.mainPanel=DoubleBufferTest(self,-1)
self.Show(True)
class DoubleBufferTest(wx.Panel):
def __init__(self,parent=None,id=-1):
wx.Panel.__init__(self,parent,id,style=wx.FULL_REPAINT_ON_RESIZE)
self.SetBackgroundColour("#FFFFFF")
self.timer = wx.Timer(self)
self.timer.Start(100)
self.Bind(wx.EVT_TIMER, self.update, self.timer)
self.Bind(wx.EVT_PAINT,self.onPaint)
def onPaint(self,event):
event.Skip()
dc = wx.MemoryDC()
dc.SelectObject(wx.EmptyBitmap(640, 480))
gc = wx.GraphicsContext.Create(dc)
gc.PushState()
gc.SetBrush(wx.Brush("#CFCFCF"))
bgRect=gc.CreatePath()
bgRect.AddRectangle(0,0,640,480)
gc.FillPath(bgRect)
gc.PopState()
dc2=wx.PaintDC(self)
dc2.Blit(0,0,640,480,dc,0,0)
def update(self,event):
self.Refresh()
app = wx.App(False)
f=MainFrame(None,"Test")
app.MainLoop()
I've come up with this code to draw double buffered GraphicsContext content onto a panel, but there's a constant flickering across the window. I've tried different kinds of paths, like lines and curves but it's still there and I don't know what's causing it.

You get flicker because each Refresh() causes the background to get erased before calling onPaint. You need to bind to EVT_ERASE_BACKGROUND and make it a no-op.
class DoubleBufferTest(wx.Panel):
def __init__(self,parent=None,id=-1):
# ... existing code ...
self.Bind(wx.EVT_ERASE_BACKGROUND, self.onErase)
def onErase(self, event):
pass
# ... existing code ...

If you're using a relatively modern wxWidgets, you can use wx.BufferedPaintDC and avoid having to muck around with the memory DC and painting and blitting on your own. Also, on windows, FULL_REPAINT_ON_RESIZE often causes flickering even when you're not resizing the window due to funny things going on under the covers - if you don't need it, going with NO_FULL_REPAINT_ON_RESIZE may help. Otherwise, you'll want to simplify your code some to make sure you can get the simplest thing to work, and perhaps take a look at the DoubleBufferedDrawing wiki page at wxpython.org.

Related

wxPython BusyInfo widget no longer works

In my wxPython GUIs, the wx.BusyInfo widget no longer works. I'm working on OSX, and I recently upgraded to El Capitan.
This simple code below doesn't work anymore with either of the wx versions that I have available ('3.0.2.0' or '2.9.2.4'). As far as I can tell, wx.BusyInfo simply no longer shows up. Unfortunately, I don't know exactly when the widget stopped appearing.
class MyFrame(wx.Frame):
def __init__(self, parent, title):
super(MyFrame, self).__init__(parent, size=(450, 350))
self.panel = wx.Panel(self)
btn = wx.Button(self.panel, wx.ID_ANY, "Do thing")
self.Bind(wx.EVT_BUTTON, self.do_thing)
self.Centre()
self.Show()
def do_thing(self, event):
wait = wx.BusyInfo('Please wait...')
time.sleep(5)
del wait
Any ideas on the cause or solution to this problem?
It looks like something may have changed with respect to when the paint events for the busy info window are processed. What you are seeing is simply that the paint event is not being delivered until after your sleep is done. If you give it a chance to be painted before you block with your busyness (such as calling wx.Yield(True) before) then you should see it working like with earlier versions of OSX. Better yet, if you can organize your busy task so it periodically yields then the system can do things like keep the busy info panel updated and show a real busy cursor instead of the spinning beachball.
I tested the suggested "Yield" workaround.
I also tried using "WindowDisabler", but that didn't work.
My band-aid fix was to refresh the wx.BusyInfo itself, instead of updating it.
For example (in Python):
busy=wx.BusyInfo("Loading corresponding data.")
#then do some work.
busy=wx.BusyInfo("Processing data for display.") #instead of busy.UpdateLabel("text")
#then do some different work.
#work done, time to let the BusyInfo object go.
del busy

wxPython - Button size being ignored on OSX

I'm taking the first steps to move from .NET to Python but I'm already having a few headaches regarding the GUI design.
For some reason, passing the size attribute to a wx.Button seems to be kind of ignored. And I say "kind of" because the actual space seems to change but the actual button keeps occupying the same space:
import wx
class Example(wx.Frame):
def __init__(self, *args, **kwargs):
super(Example, self).__init__(*args, **kwargs)
self.InitUI()
def InitUI(self):
self.SetSize((800, 600))
self.SetTitle('Main Menu')
self.Centre()
self.Show(True)
''' Fill the form '''
self.lblUsername = wx.StaticText(self, size=(80, -1), pos=(20,20), label="Username:" )
self.txtUsername = wx.TextCtrl(self, size=(140, -1), pos=(100,20), style=wx.TE_PROCESS_ENTER)
self.lblPassword = wx.StaticText(self, size=(80, -1), pos=(20,50), label="Password:" )
self.txtPassword = wx.TextCtrl(self, size=(140, -1), pos=(100,50), style=wx.TE_PROCESS_ENTER)
self.btnOK = wx.Button( self, label="OK", pos=(260, 16), size=(50,50))
self.btnOK.Bind(wx.EVT_BUTTON, self.onClickOK)
self.statusbar = self.CreateStatusBar()
self.statusbar.SetStatusText('Ready')
def onClickOK(self, e):
print "Button triggered"
def main():
ex = wx.App()
Example(None)
ex.MainLoop()
if __name__ == '__main__':
main()
No matter what size I set, the Button won't stretch (it will be centered as if all the space was actually being used, but will still be small).
Can anyone spot what am I doing wrong?
This is a limit imposed by OSX. The way the native button widget is drawn only allows it to be stretched horizontally, and the vertical size is fixed. Or rather, as you've discovered, the widget itself can be larger than normal vertically, but it will only draw itself at a fixed height within that space. It seems less neccessary with modern versions of OSX, but if you look at buttons in OSX from a few years ago you can probably see why this is so. The esthetic graphical effect of the "tic-tack" or "capsule" buttons would be totally ruined if they were a non-standard vertical size, causing the images used to draw the buttons to be stretched. wxWidgets follows the native plaform look and feel standards where possible, in this case it happens that Apple's standard is imposed upon us and wx can't offer the same level of flexibility that it usually does.
You do have some options however if you really want taller than normal buttons. The native widgets have a few different standard sizes, which you can select using the SetWindowVariant method, although I don't think the variants would get as tall as you want. Or you could use a generic button widget instead of a native one, such as wx.lib.buttons.ThemedGenButton.
Same problem in my little Software EventSoundControl.
Just a workaround: Use a multiline label and sizes of wxButton will work as desired!
If you want the button to stretch when you resize the frame, then you cannot use static sizes and positioning. You will need to put your widgets in a sizer. Then the sizer will manage the position / size of the widget(s) as you change the size of the frame. There are many examples on the wxPython wiki that demonstrate how to use sizers. You might also find the following tutorial helpful:
http://zetcode.com/wxpython/layout/

In wxpython, how to make frame fully transparent, while line is visible?

import wx
class DrawPanel(wx.Frame):
def __init__(self):
wx.Frame.__init__(self,None,title="Draw",size=(150,150))
self.SetTransparent(0)
self.Bind(wx.EVT_PAINT, self.OnPaint)
def OnPaint(self, event=None):
dc = wx.PaintDC(self)
dc.SetPen(wx.Pen(wx.BLACK, 5))
# Draw a line
dc.DrawLine(0, 0, 150, 150)
app = wx.App()
frame = DrawPanel()
frame.Show(True)
app.MainLoop()
Now frame and line are all invisible.
Please tell me how to make frame fully transparent, while line is visible ?
When you make the frame transparent, it makes all its children transparent too. Thus, this really isn't possible. You can make semi-transparent drawings though via GraphicsContext. And you could take a look at GLCanvas (a wrapper for OpenGL) or you might be able to cobble something together with Cairo. See the wxPython demo package (available from www.wxpython.org) for examples.

wxPython - PaintDC not refreshing

import wx
class TestDraw(wx.Panel):
def __init__(self,parent=None,id=-1):
wx.Panel.__init__(self,parent,id)
self.SetBackgroundColour("#FFFFFF")
self.Bind(wx.EVT_PAINT,self.onPaint)
def onPaint(self, event):
event.Skip()
dc=wx.PaintDC(self)
dc.BeginDrawing()
width=dc.GetSize()[0]
height=dc.GetSize()[1]
if height<width:
self.drawTestRects(dc)
else:
dc.Clear()
dc.EndDrawing()
def drawTestRects(self,dc):
dc.SetBrush(wx.Brush("#000000",style=wx.SOLID))
dc.DrawRectangle(50,50,50,50)
dc.DrawRectangle(100,100,100,100)
class TestFrame(wx.Frame):
def __init__(self, parent, title):
wx.Frame.__init__(self, parent, title=title, size=(640,480))
self.mainPanel=TestDraw(self,-1)
self.Show(True)
app = wx.App(False)
frame = TestFrame(None,"Test App")
app.MainLoop()
This code should draw the test rectangles only when the height is less than the width, and otherwise the window should remain clear. However, if you mess with resizing the window, the panel isn't actually redrawn unless it is moved off the window. What am I doing wrong?
You can bind a method to handle wx.EVT_SIZE or the panel and invalidate it there. Alternatively simply use the wx.FULL_REPAINT_ON_RESIZE for the panel.
The documentation for a SizeEvent claims that there may be some complications when drawing depends on the dimensions of the window. I do not know exactly what is going on behind the scenes. I followed the suggestion on the link and added the call self.Refresh() to the top of onPaint() and this seems to give the desired behavior. See mghie's answer for a more efficient example of working code.

Problem when using MemoryDC

Why does my code print the lines gray instead of black?
import wx
class MyFrame(wx.Frame):
def __init__(self,*args,**kwargs):
wx.Frame.__init__(self,*args,**kwargs)
self.panel=wx.Panel(self,-1,size=(1000,1000))
self.Bind(wx.EVT_PAINT, self.on_paint)
self.Bind(wx.EVT_SIZE, self.on_size)
self.bitmap=wx.EmptyBitmapRGBA(1000,1000,255,255,255,255)
dc=wx.MemoryDC()
dc.SelectObject(self.bitmap)
dc.SetPen(wx.Pen(wx.NamedColor("black"),10,wx.SOLID))
dc.DrawCircle(0,0,30)
dc.DrawLine(40,40,70,70)
dc.Destroy()
self.Show()
def on_size(self,e=None):
self.Refresh()
def on_paint(self,e=None):
dc=wx.PaintDC(self.panel)
dc.DrawBitmap(self.bitmap,0,0)
dc.Destroy()
if __name__=="__main__":
app=wx.PySimpleApp()
my_frame=MyFrame(parent=None,id=-1)
app.MainLoop()
Beside the frame/panel paint problem already pointed out the color problem is due to the alpha channel of the 32 bit bitmap.
I remember having read to use wx.GCDC instead of wx.DC.
Ok I tested with newer version of wx(2.8.9.2)
and Now I wonder why it is even working on your side.
you are trying to paint Panel but overriding the paint event of Frame
instead do this
self.panel.Bind(wx.EVT_PAINT, self.on_paint)
and all will be fine

Categories

Resources