Given the following simple program:
import wx
class TestDraw(wx.Panel):
def __init__(self,parent=None,id=-1):
wx.Panel.__init__(self,parent,id,style=wx.TAB_TRAVERSAL)
self.SetBackgroundColour("#FFFFFF")
self.Bind(wx.EVT_PAINT,self.onPaint)
self.SetDoubleBuffered(True)
self.circleX=320
self.circleY=240
def onPaint(self, event):
event.Skip()
dc=wx.PaintDC(self)
dc.BeginDrawing()
dc.DrawCircle(self.circleX,self.circleY,100)
dc.EndDrawing()
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()
How can I change it so that I can execute logic and repaint the panel at a constant rate? I'd like the circle to bounce around the screen, but I just can't figure out the place I would change its x and y variables.
Your can use a wxTimer to periodically call an onTimer(self) method.
Related
I am having trouble posting an erase background event to draw to the screen. In my full code, I want to draw a bitmap (DC.DrawBitmap()) when a button is clicked. I do that by posting an EVT_ERASE_BACKGROUND event which is caught by a custom bound method. However, once it is in that method, the event.GetDC() method that normally works fails.
Here is a simplified code that has the same result:
import wx
class Foo(wx.Frame):
def __init__(self, parent, title):
wx.Frame.__init__ (self, parent, -1, title, size=(500,300))
self.panel = wx.Panel(self, -1)
self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
self.Bind(wx.EVT_ENTER_WINDOW, self.onEnter)
self.Show()
def OnEraseBackground(self, e):
DC = e.GetDC()
def onEnter(self, e):
wx.PostEvent(self, wx.PyCommandEvent(wx.wxEVT_ERASE_BACKGROUND))
app = wx.App()
Foo(None, 'foo')
app.MainLoop()
This raises:
AttributeError: 'PyCommandEvent' object has no attribute 'GetDC'
How can I solve this?
Worked on it for an hour without success before posting, then solved it myself five minutes later...
Here's my solution, creating a ClientDC if the event doesn't have its own DC:
import wx
class Foo(wx.Frame):
def __init__(self, parent, title):
wx.Frame.__init__ (self, parent, -1, title, size=(500,300))
self.panel = wx.Panel(self, -1)
self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
self.Bind(wx.EVT_ENTER_WINDOW, self.onEnter)
self.Show()
def OnEraseBackground(self, e):
try:
DC = e.GetDC()
except:
DC = wx.ClientDC(self)
DC.Clear()
def onEnter(self, e):
wx.PostEvent(self, wx.PyCommandEvent(wx.wxEVT_ERASE_BACKGROUND))
app = wx.App()
Foo(None, 'foo')
app.MainLoop()
I'am a new bie in python,I have to call a frame "Frame2" when I clic on a button from Frame1,I have this error:
this I my code:
global Frame2 fr
def OnButton4Button(self, event):
fr.Show()
even.Skip()
NB:I work with wxpython,and boa constructor
thanks fro help
You have several typos in your code. Here's a corrected example:
from Frame2 import fr
def OnButton4Button(self, event):
fr.Show()
event.Skip() # you need to spell event correctly
This assumes that Frame2 is a module. Most of the time, you don't need to use globals in Python.
To make this a bit easier to follow, I wrote an example that has a MainFrame class and a Frame2 class in the same module so you don't have to import anything or use globals:
import wx
########################################################################
class Frame2(wx.Frame):
""""""
#----------------------------------------------------------------------
def __init__(self):
"""Constructor"""
wx.Frame.__init__(self, None, title="Frame2")
panel = wx.Panel(self)
########################################################################
class MainFrame(wx.Frame):
""""""
#----------------------------------------------------------------------
def __init__(self):
"""Constructor"""
wx.Frame.__init__(self, None, title="Main Frame")
panel = wx.Panel(self)
button = wx.Button(panel, label="Open Frame2")
button.Bind(wx.EVT_BUTTON, self.onButton)
self.Show()
#----------------------------------------------------------------------
def onButton(self, event):
""""""
frame = Frame2()
frame.Show()
event.Skip()
if __name__ == "__main__":
app = wx.App(False)
frame = MainFrame()
app.MainLoop()
In your short code you have an indentation on the second line, this is an error, you must write it like:
from Frame2 import fr
def OnButton4Button(self, event):
fr.Show()
event.Skip()
You may respect the indentation in Python like following example:
global var
def function():
#indented block
#should be always on the same column
condition = 1
if condition:
#new indented block
#is also aligned on a column
print "something"
#this is out of the IF block
#call the function
function()
In the PEP8 recommendations you will find the rules to avoid indenting errors.
i have two py files that each have its own frame made using wxPython, MainWindow and RecWindow. MainWindow has the RecWindow python file included using the keyword "recovery".
MainWindow code:
class MainWindow(wx.Frame):
def __init__(self,parent,id,title):
wx.Frame.__init__(self, parent, wx.ID_ANY,title,pos=(500,200), size = (650,500), style = wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE)
self.Bind(wx.EVT_CLOSE,self.OnExit)
self.SetIcon(wx.Icon('etc\icons\download.ico', wx.BITMAP_TYPE_ICO))
panel = wx.Panel(self)
RecWindow code:
class RecWindow(wx.Frame):
def __init__(self,parent,id,title):
wx.Frame.__init__(self, parent, wx.ID_ANY,title,pos=(400,200), size = (700,600), style = wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE)
self.SetIcon(wx.Icon('etc\icons\download.ico', wx.BITMAP_TYPE_ICO))
self.count = 0
when i click on a button in MainWindow , it will hide the MainWindow create an instance of RecWindow, shown below;
def OpenRec(self,event):#this will be used to open the next frame
OR = recovery(None,-1,"RAVE")
OR.Show(True)
MainWindow.Hide()
now, what i am unsure of is how i can return to the MainWindow once i close the RecWindow. RecWindow has a cancel and finish button which both map to a self.close() function. How would i then get MainWindow to show itself again?
Use pubsub to send a message to the main window telling it to Show itself again. I actually have an example of how to do that here:
http://www.blog.pythonlibrary.org/2010/06/27/wxpython-and-pubsub-a-simple-tutorial/
Note that this tutorial is using slightly older API that was available in wxPython 2.8. If you're using wxPython 2.9, then you'll have to use the slightly different API that I detail in this article:
http://www.blog.pythonlibrary.org/2013/09/05/wxpython-2-9-and-the-newer-pubsub-api-a-simple-tutorial/
When you create an instance of RecWindow keep a reference to it on main_window and bind to its close event.
In the main_window's close handler check if the window closed was the RecWindow.
If it was, clear the reference to it and show the main_window.
Elif the closed window was main_window carry out any required code.
Finally call event.Skip() so the windows get destroyed.
import wx
class MainWindow(wx.Frame):
def __init__(self, parent, title):
wx.Frame.__init__(self, parent, -1, title, (500, 200), (650, 500),
wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE)
panel = wx.Panel(self)
button = wx.Button(panel, wx.ID_OPEN)
panel.sizer = wx.BoxSizer(wx.VERTICAL)
panel.sizer.Add(button, 0, wx.ALL, 7)
panel.SetSizer(panel.sizer)
button.Bind(wx.EVT_BUTTON, self.on_button)
self.Bind(wx.EVT_CLOSE, self.on_close)
self.rec_window = None
def on_button(self, event):
rec_window = RecWindow(self, 'Rec window')
rec_window.Show()
self.Hide()
rec_window.Bind(wx.EVT_CLOSE, self.on_close)
self.rec_window = rec_window
def on_close(self, event):
closed_window = event.EventObject
if closed_window == self.rec_window:
self.rec_window = None
self.Show()
elif closed_window == self:
print 'Carry out your code for when Main window closes'
event.Skip()
class RecWindow(wx.Frame):
def __init__(self, parent, title):
wx.Frame.__init__(self, parent, -1, title, (400, 200), (700, 600),
wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE)
app = wx.App(False)
main_window = MainWindow(None, 'Main window')
main_window.Show()
app.MainLoop()
complete newbie here to Python as I only started learning the language a few days ago (with no programming experience beforehand).
I'm basically bashing my skull against the desk here, trying to create one menu with a button that will lead you to another menu, which is supposed to replace/hide/destroy the previous menu (either works, so long as the process can be reversed).
What I've come up with so far:
import wx
class MainFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None)
self.Centre()
self.main_menu = MainMenu(self)
self.intro_screen = IntroScreen(self)
self.intro_screen.Hide()
class MainMenu(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent=parent)
self.main_menu = MainMenu
panel = wx.Panel(self)
sizer = wx.BoxSizer(wx.VERTICAL)
nextscreen = wx.Button(panel, label='Next Screen', size=(150,30))
nextscreen.Bind(wx.EVT_BUTTON, self.NextScreen)
sizer.Add(nextscreen, 0, wx.CENTER|wx.ALL, 5)
self.Show()
self.Centre()
def NextScreen(self, event):
self.main_menu.Hide(self)
self.intro_screen.Show()
class IntroScreen(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent=parent)
self.intro_screen = IntroScreen
panel = wx.Panel(self)
sizer = wx.BoxSizer(wx.VERTICAL)
gobackscreen = wx.Button(panel, label='Go Back a Screen', size=(150,30))
gobackscreen.Bind(wx.EVT_BUTTON, self.GoBackScreen)
sizer.Add(gobackscreen, 0, wx.CENTER|wx.ALL, 5)
self.Show()
self.Centre()
def GoBackScreen(self, event):
self.intro_screen.Hide()
self.main_menu.Show()
if __name__ == "__main__":
app = wx.App(False)
frame = MainFrame()
#frame.Show()
app.MainLoop()
From what I can tell, the NextScreen button does not see the intro_screen class, and is therefore unable to show it. But I am clueless as to how to fix this.
Indeed, I have absolutely no idea if this is on the right way to do it. Any help is greatly appreciated
Using Python 2.7
intro_screen is an attribute of MainFrame instances; not of MainMenu instances.
Your MainMenu.__init__() method is passed in a MainFrame instance as parent. I am not certain if self.parent is set by the line wx.Frame.__init__(self, parent=parent), but if it is not, do add self.parent = parent in MainMenu.__init__(.
You can then refer to self.parent on MainMenu instances, and the following should work:
self.parent.intro_screen.Show()
I am not sure why you are setting the current class as an instance attribute:
self.main_menu = MainMenu
and
self.intro_screen = IntroScreen
Instead of self.main_menu.Hide(self) you can just call self.Hide(), the reference to the class is not needed.
I've got a wxPython program that uses a ScrolledPanel and it's doing something really strange on some resizes. If you drag the resize around a bit, one of the buttons will start looking messed up.
Before:
http://s7.postimage.org/stoc27517/Good_Window.png
After:
http://s9.postimage.org/s2apf38m7/Bad_Window.png
Here's the code:
import wx
import wx.lib.scrolledpanel as scrolled
class TestApp(wx.Frame):
def __init__(self, *args, **kwargs):
super(TestApp,self).__init__(*args,**kwargs)
self.setupGUI()
def setupGUI(self):
#Fires on window resize
self.Bind(wx.EVT_SIZE, self.OnSize)
self.box = wx.BoxSizer(wx.VERTICAL)
self.SetSizer(self.box)
self.scrolling_window = scrolled.ScrolledPanel( self )
self.scrolling_window.SetAutoLayout(1)
self.scrolling_window.SetupScrolling()
self.gb = wx.GridBagSizer(5,5)
self.scrolling_window.SetSizer(self.gb)
self.btnOne = wx.Button(self.scrolling_window,label='One')
self.btnTwo = wx.Button(self.scrolling_window,label='Two')
self.gb.Add(self.btnOne, pos=(0,0), flag=wx.EXPAND)
self.gb.Add(self.btnTwo, pos=(1,1), flag=wx.ALIGN_BOTTOM)
self.gb.AddGrowableCol(0)
self.gb.AddGrowableRow(1)
self.box.Add(self.scrolling_window,1,wx.EXPAND)
self.Show(True)
def OnSize(self, e):
#This refresh shouldn't be necessary
#self.Refresh()
#Pass event up the chain so window still resizes
e.Skip()
if __name__ == '__main__':
app = wx.App(redirect=False)
TestApp(None)
app.MainLoop()
The only way I could prevent this behavior was to uncomment the self.Refresh() in the OnSize method. This would essentially force a repaint on every window resize. But I don't think this should be necessary. Is this a bug, or am I doing something wrong?