I'd like to create a live graph within a split-window in wxPython (using matplotlib?) --
So far I have the code that deals with logic: I'm creating a PID controller, and I'm looking to add a live graph (lets say 1 second delay between samples, with pseudo/random samples for now)
my code is here so far (below) - please advise
import wx
import matplotlib as mpl
mpl.use('WXAgg')
import matplotlib.pyplot as plt
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as Canvas
# global constants
Kp = 10
Ki = 2
Kd = 1
# functions used
def AttentionBox( self, message ):
dialog = wx.MessageDialog(self, message, 'Attention!', wx.ICON_ERROR)
dialog.ShowModal()
dialog.Destroy()
def StatusBar( self, message=None):
statusMessage = "PID constants( " + str(Kp)+","+str(Ki)+","+str(Kd)+" ): "
self.statusbar.SetStatusText( statusMessage + message )
class PIDFrame(wx.Frame):
def __init__(self, parent, title):
wx.Frame.__init__(self, parent, title="PID Flow Controller", size=(1000,550))
# split the screen
self.sp = wx.SplitterWindow(self)
self.p1 = wx.Panel(self.sp, style = wx.SUNKEN_BORDER)
self.p2 = wx.Panel(self.sp, style = wx.SUNKEN_BORDER)
self.sp.SplitVertically(self.p1, self.p2, 860)
self.statusbar = self.CreateStatusBar()
StatusBar(self, "System Idle")
# define Temperature and Flow Control
wx.StaticBox(self.p2, -1, "System Control", pos=(5, 5), size=(100, 240))
wx.StaticText(self.p2, -1,"Target (litres/min):", pos=(15, 100))
self.Heater = wx.CheckBox(self.p2, -1 , "Heater", pos=(20,35))
self.Chiller = wx.CheckBox(self.p2, -1 , "Chiller", pos=(20,65))
self.TargetFlow = wx.SpinCtrl(self.p2, -1, "100", pos=(15,120), size=(80, 20), min=0, max=350)
self.ActivateButton = wx.Button(self.p2, -1, "Activate",pos=(20,180))
self.ActivateButton.Bind(wx.EVT_BUTTON, self.ActivateSystem)
self.StopButton = wx.Button(self.p2, -1, "Stop", pos=(20,215))
self.StopButton.Bind(wx.EVT_BUTTON, self.StopSystem)
############################################ BUTTONS ######################################################
def ActivateSystem(self, event):
if( self.Heater.GetValue() and self.Chiller.GetValue() ):
StatusBar(self,"Target Flow: " + str(self.TargetFlow.GetValue()) + " litres/min" )
self.Heater.SetValue(0)
self.Chiller.SetValue(0)
AttentionBox( self, "Heater and Chiller? Not Possible - Neither Activated" )
elif( self.Heater.GetValue() ):
StatusBar(self,"Heater Active. Target Flow: " + str(self.TargetFlow.GetValue()) + " litres/min" )
elif( self.Chiller.GetValue() ):
StatusBar(self,"Chiller Active. Target Flow: " + str(self.TargetFlow.GetValue()) + " litres/min" )
else:
StatusBar(self,"Target Flow: " + str(self.TargetFlow.GetValue()) + " litres/min" )
def StopSystem(self, event):
StatusBar(self, "System Idle" )
self.Heater.SetValue(0)
self.Chiller.SetValue(0)
self.TargetFlow.SetValue(0)
############################################ GRAPHING ######################################################
app = wx.App(redirect=False)
frame = PIDFrame(None, -1)
frame.Show()
app.MainLoop()
Related
In the part of the code included between ################ Code ######### I try to do a control with if but doesn't work.
I would like that when the variable 'ts' is equal to the variable 'prova' the line(item) in the same list ctrl becomes red. For all item which I insert in the ctrl list. Thanks so much!
import wx
import wx.gizmos as gizmos
import time
import datetime
from datetime import timedelta
class CWindow(wx.Frame):
def __init__(self, parent, id, title):
wx.Frame.__init__(self, parent, id, title, style = wx.DEFAULT_FRAME_STYLE & ~ wx.CLOSE_BOX ^ wx.MAXIMIZE_BOX ^ wx.RESIZE_BORDER, size=(600,500))
dataCorrente = datetime.datetime.now()
self.Panel = wx.Panel(self, -1)
self.index = 0
self.CTesto = wx.TextCtrl(self.Panel, 1, pos=(10,40), style=wx.TE_PROCESS_ENTER)
self.CTesto.Bind(wx.EVT_TEXT_ENTER,self.add_line)
self.list_ctrl = wx.ListCtrl(self.Panel, pos=(10,90),size=(-1,300),style=wx.LC_REPORT|wx.BORDER_SUNKEN)
self.list_ctrl.InsertColumn(0, 'Name')
self.list_ctrl.InsertColumn(1, 'Time START')
self.list_ctrl.InsertColumn(2, 'Time FINISH', width=100)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.list_ctrl, 0, wx.ALL|wx.EXPAND, 5)
self.led = gizmos.LEDNumberCtrl(self.Panel, -1, pos = (350,25), size = (200,50), style = gizmos.LED_ALIGN_CENTER)
self.led.SetBackgroundColour("#c0c0c0")
self.led.SetForegroundColour("black")
self.OnTimer(None)
self.timer = wx.Timer(self, -1)
self.timer.Start(1000)
self.Bind(wx.EVT_TIMER, self.OnTimer)
style = gizmos.LED_ALIGN_CENTER
def OnTimer(self, event):
current = time.localtime(time.time())
global ts
ts = time.strftime("%H:%M:%S", current)
self.led.SetValue(ts)
#print (ts) # In Loop it's OK
############################################################################################################################
if ts == prova:
self.list_ctrl.SetItemBackgroundColour(self.index, wx.RED)
############################################################################################################################
def add_line(self,event):
val = str(self.CTesto.GetValue())
if val== '':
msg = wx.MessageDialog(self, "Error", "Error", wx.OK| wx.ICON_ERROR)
msg.ShowModal()
msg.Destroy()
else:
dataCorrente = datetime.datetime.now()
oraAttuale =(dataCorrente.strftime("%H:%M:%S"))
plus = (datetime.datetime.strptime(oraAttuale, "%H:%M:%S") + datetime.timedelta(minutes=1))
global plus2
plus2 = plus.strftime("%H:%M:%S")
self.list_ctrl.InsertItem(self.index, val)
self.list_ctrl.SetItem(self.index, 1, oraAttuale)
self.list_ctrl.SetItem(self.index, 2, str(plus2))
self.index += 1
InsVal = (val + " - " + oraAttuale + " - " + plus2 + '\n')
self.CTesto.Clear()
print (InsVal)
prova = InsVal[-9:]
app = wx.App()
frame = CWindow(None, -1, "Example")
frame.Show()
frame.Center()
app.MainLoop()
repr() is your friend in this question.
print(repr(self.prova)) would reveal that it contains a newline character, so that needs to be stripped off.
Also note that the ListCtrl index is zero based, so to apply the colour, you have to use index - 1.
With these minor adjustments your code works, as I hope you planned it to.
import wx
import wx.gizmos as gizmos
import time
import datetime
from datetime import timedelta
class CWindow(wx.Frame):
def __init__(self, parent, id, title):
wx.Frame.__init__(self, parent, id, title, style = wx.DEFAULT_FRAME_STYLE & ~ wx.CLOSE_BOX ^ wx.MAXIMIZE_BOX ^ wx.RESIZE_BORDER, size=(600,500))
dataCorrente = datetime.datetime.now()
self.Panel = wx.Panel(self, -1)
self.index = 0
self.prova = {}
self.ts = ""
self.CTesto = wx.TextCtrl(self.Panel, 1, pos=(10,40), style=wx.TE_PROCESS_ENTER)
self.CTesto.Bind(wx.EVT_TEXT_ENTER,self.add_line)
self.list_ctrl = wx.ListCtrl(self.Panel, pos=(10,90),size=(-1,300),style=wx.LC_REPORT|wx.BORDER_SUNKEN)
self.list_ctrl.InsertColumn(0, 'Name')
self.list_ctrl.InsertColumn(1, 'Time START')
self.list_ctrl.InsertColumn(2, 'Time FINISH', width=100)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.list_ctrl, 0, wx.ALL|wx.EXPAND, 5)
self.led = gizmos.LEDNumberCtrl(self.Panel, -1, pos = (350,25), size = (200,50), style = gizmos.LED_ALIGN_CENTER)
self.led.SetBackgroundColour("#c0c0c0")
self.led.SetForegroundColour("black")
self.OnTimer(None)
self.timer = wx.Timer(self, -1)
self.timer.Start(1000)
self.Bind(wx.EVT_TIMER, self.OnTimer)
style = gizmos.LED_ALIGN_CENTER
def OnTimer(self, event):
current = time.localtime(time.time())
self.ts = time.strftime("%H:%M:%S", current)
self.led.SetValue(self.ts)
############################################################################################################################
if self.ts in self.prova:
self.list_ctrl.SetItemBackgroundColour(self.prova.pop(self.ts), wx.RED)
############################################################################################################################
def add_line(self,event):
val = str(self.CTesto.GetValue())
if val== '':
msg = wx.MessageDialog(self, "Error", "Error", wx.OK| wx.ICON_ERROR)
msg.ShowModal()
msg.Destroy()
else:
dataCorrente = datetime.datetime.now()
oraAttuale =(dataCorrente.strftime("%H:%M:%S"))
plus = (datetime.datetime.strptime(oraAttuale, "%H:%M:%S") + datetime.timedelta(minutes=1))
plus2 = plus.strftime("%H:%M:%S")
if plus2 in self.prova:
msg = wx.MessageDialog(self, "Duplicated Time", "Error", wx.OK| wx.ICON_ERROR)
msg.ShowModal()
msg.Destroy()
return
self.list_ctrl.InsertItem(self.index, val)
self.list_ctrl.SetItem(self.index, 1, oraAttuale)
self.list_ctrl.SetItem(self.index, 2, str(plus2))
self.index += 1
InsVal = (val + " - " + oraAttuale + " - " + plus2 + '\n')
self.CTesto.Clear()
self.prova[plus2] = self.index -1
app = wx.App()
frame = CWindow(None, -1, "Example")
frame.Show()
frame.Center()
app.MainLoop()
Edit:
If you want to test for all of the entries, turning them red as they occur, you will have to change the nature of prova.
One way would be to make prova a dictionary and use the edited code above.
This provides a method to prevent duplicate entries and a simple method for deleting entries in the dictionary prova reducing the amount of checking required. In this way the code can run continuously for days without hitting duplicates. The dictionary entries are the Finish time and the listctrl's index value, used to perform the highlighting.
The act of deleting the dictionary entry (pop), returns the index number for the highlight.
I'm new to Python and I want to implement a scrolling plot for a very long time series data. I've found an example from Matplotlib as follows.
http://scipy-cookbook.readthedocs.io/items/Matplotlib_ScrollingPlot.html
When I run the example from the link, I found every time I scroll the plot and release the scrollbar, the scrollbar returns to the beginning. Want to scroll to the next position? I need to start to scroll from the beginning again.
I want to understand why it happens and how to fix it.
Here's an improved version of the example. (Disclaimer: I started digging into it half an hour ago, never before used wx/matplotlib scrollbars so there might be a much better solution.)
The path I took: first I checked the wx scroll events, then found out that the canvas is FigureCanvasWxAgg derived from wxPanel, inheriting wxWindow methods. There you may find the scroll position handling methods GetScrollPos and SetScrollPos.
from numpy import arange, sin, pi, float, size
import matplotlib
matplotlib.use('WXAgg')
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg
from matplotlib.figure import Figure
import wx
class MyFrame(wx.Frame):
def __init__(self, parent, id):
wx.Frame.__init__(self,parent, id, 'scrollable plot',
style=wx.DEFAULT_FRAME_STYLE ^ wx.RESIZE_BORDER,
size=(800, 400))
self.panel = wx.Panel(self, -1)
self.fig = Figure((5, 4), 75)
self.canvas = FigureCanvasWxAgg(self.panel, -1, self.fig)
self.scroll_range = 400
self.canvas.SetScrollbar(wx.HORIZONTAL, 0, 5,
self.scroll_range)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.canvas, -1, wx.EXPAND)
self.panel.SetSizer(sizer)
self.panel.Fit()
self.init_data()
self.init_plot()
self.canvas.Bind(wx.EVT_SCROLLWIN, self.OnScrollEvt)
def init_data(self):
# Generate some data to plot:
self.dt = 0.01
self.t = arange(0,5,self.dt)
self.x = sin(2*pi*self.t)
# Extents of data sequence:
self.i_min = 0
self.i_max = len(self.t)
# Size of plot window:
self.i_window = 100
# Indices of data interval to be plotted:
self.i_start = 0
self.i_end = self.i_start + self.i_window
def init_plot(self):
self.axes = self.fig.add_subplot(111)
self.plot_data = \
self.axes.plot(self.t[self.i_start:self.i_end],
self.x[self.i_start:self.i_end])[0]
def draw_plot(self):
# Update data in plot:
self.plot_data.set_xdata(self.t[self.i_start:self.i_end])
self.plot_data.set_ydata(self.x[self.i_start:self.i_end])
# Adjust plot limits:
self.axes.set_xlim((min(self.t[self.i_start:self.i_end]),
max(self.t[self.i_start:self.i_end])))
self.axes.set_ylim((min(self.x[self.i_start:self.i_end]),
max(self.x[self.i_start:self.i_end])))
# Redraw:
self.canvas.draw()
def update_scrollpos(self, new_pos):
self.i_start = self.i_min + new_pos
self.i_end = self.i_min + self.i_window + new_pos
self.canvas.SetScrollPos(wx.HORIZONTAL, new_pos)
self.draw_plot()
def OnScrollEvt(self, event):
evtype = event.GetEventType()
if evtype == wx.EVT_SCROLLWIN_THUMBTRACK.typeId:
pos = event.GetPosition()
self.update_scrollpos(pos)
elif evtype == wx.EVT_SCROLLWIN_LINEDOWN.typeId:
pos = self.canvas.GetScrollPos(wx.HORIZONTAL)
self.update_scrollpos(pos + 1)
elif evtype == wx.EVT_SCROLLWIN_LINEUP.typeId:
pos = self.canvas.GetScrollPos(wx.HORIZONTAL)
self.update_scrollpos(pos - 1)
elif evtype == wx.EVT_SCROLLWIN_PAGEUP.typeId:
pos = self.canvas.GetScrollPos(wx.HORIZONTAL)
self.update_scrollpos(pos - 10)
elif evtype == wx.EVT_SCROLLWIN_PAGEDOWN.typeId:
pos = self.canvas.GetScrollPos(wx.HORIZONTAL)
self.update_scrollpos(pos + 10)
else:
print "unhandled scroll event, type id:", evtype
class MyApp(wx.App):
def OnInit(self):
self.frame = MyFrame(parent=None,id=-1)
self.frame.Show()
self.SetTopWindow(self.frame)
return True
if __name__ == '__main__':
app = MyApp()
app.MainLoop()
You may adjust e.g. the increments for PAGEUP/PAGEDOWN if you feel it too slow.
Also if you wish, the events can be handled separately setting up the specific event handlers instead of their collection EVT_SCROLLWIN, then instead of if/elifs there will be OnScrollPageUpEvt etc.
I have an issue with graphing data within wxPython - this code below works but it's not exactly right: For now I plot random numbers which are multiples of an entry box, this is done every 100ms.
My issue is that the entire history of numbers is shown as a pose is a (say) running window of 25 samples. I initially tried redrawing the graph on each collection of 100 samples, something like, if( length(data)%100 ): drawGraph but again this didn't look correct.
Thoughts and suggestions welcome.
My code:
print( "\n- Please Wait -- Importing Matplotlib and Related Modules...\n" )
import random
import matplotlib
import numpy
import wx
import u3
import numpy as np
matplotlib.use('WXAgg')
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
from matplotlib.backends.backend_wx import NavigationToolbar2Wx
from matplotlib.figure import Figure
class TemperaturePanel( wx.Panel ) :
def __init__( self, parent, position ) :
wx.Panel.__init__( self, parent, pos=position, size=(800,320) )
# initialize matplotlib
self.figure = matplotlib.figure.Figure( None, facecolor="white" )
self.canvas = matplotlib.backends.backend_wxagg.FigureCanvasWxAgg( self, -1, self.figure )
self.axes = self.figure.add_subplot(111)
self.axes.grid(True, color="gray")
self.axes.set_xbound( (0,5) )
self.axes.set_ybound( (3,80) )
self.axes.set_xlabel( "Minutes" )
self.axes.set_ylabel( "Temperature ($^\circ$C)" )
self.axes = self.figure.add_subplot(111)
self.axes.grid(True, color="gray")
self._SetSize()
self.Bind( wx.EVT_SIZE, self._SetSize )
self.TemperatureData = []
def updateTemperature(self, value):
self.TemperatureData.append( value )
length = len(self.TemperatureData)
x = np.arange( length )
y = np.array(self.TemperatureData)
yMin = round(min(y)) - 2
yMax = round(max(y)) + 2
self.axes.plot(x,y, "-k")
self.axes.set_ybound( (yMin,yMax) )
self.canvas = FigureCanvas(self, -1, self.figure)
#-----------------------------------------------------------------------------------
def _SetSize( self, event=None ):
pixels = self.GetSize()
self.SetSize( pixels )
self.canvas.SetSize( pixels )
dpi = self.figure.get_dpi()
self.figure.set_size_inches( float( pixels[0] ) / dpi,float( pixels[1] ) / dpi )
#------------------------------------------------------------------------------------
class MainWindow(wx.Frame):
def __init__(self, parent):
#wx.Frame.__init__(self, *args, **kwargs)
wx.Frame.__init__(self, parent, title="Graph Issue", size=(1000,600))
self.panel = wx.Panel(self)
self.spin = wx.SpinCtrl(self.panel)
self.button = wx.Button(self.panel, label="Update")
self.stop = wx.Button(self.panel, label="Stop")
self.sizer = wx.BoxSizer()
self.sizer.Add(self.spin)
self.sizer.Add(self.button)
self.sizer.Add(self.stop)
self.TemperatureGraph = TemperaturePanel( self, position=(20, 50) )
self.panel.SetSizerAndFit(self.sizer)
self.Show()
# Use EVT_CHAR_HOOK on Frame insted of wx.EVT_KEY_UP on SpinCtrl
# to disable "on Enter go to next widget" functionality
self.Bind(wx.EVT_CHAR_HOOK, self.OnKey)
self.button.Bind(wx.EVT_BUTTON, self.OnUpdate)
self.stop.Bind(wx.EVT_BUTTON, self.OnStop)
self.timer = wx.Timer(self)
self.Bind(wx.EVT_TIMER, self.OnTimer, self.timer)
self.Bind(wx.EVT_TIMER, self.updateTemperature, self.timer)
self.timer.Start(100)
self.value = 0
def OnKey(self, e):
if e.GetKeyCode() == wx.WXK_RETURN: # Is the key ENTER?
self.value = self.spin.GetValue() # Read SpinCtrl and set internal value
else: # Else let the event out of the handler
e.Skip()
def OnUpdate(self, e):
self.value = self.spin.GetValue() # Read SpinCtrl and set internal value
def OnTimer(self, e):
# Show internal value
print(self.value)
def updateTemperature(self, e):
Temperature = self.value*random.uniform(-1,1) # obtain currnt temperature
self.TemperatureGraph.updateTemperature(Temperature) # add temperature to graph
def OnStop(self, e):
self.timer.Stop()
self.Destroy()
app = wx.App(False)
win = MainWindow(None)
app.MainLoop()
If I understood the question correctly, you need 25 last temperature values only to be shown in your graph instead of all history of values. If that's what you want, then in the updateTemperature subroutine only the last 25 values should be plotted:
if length < 25:
x = np.arange(length)
y = np.array(self.TemperatureData)
else:
x = np.arange(length-25, length)
y = np.array(self.TemperatureData)[-25:]
To make the plot look better, x axis can be adjusted the same way you do it with y axis:
xMin = 0 if length < 25 else length-25
xMax = 25 if length < 25 else length
self.axes.set_xbound( (xMin,xMax) )
If the plot looks OK to you, and the issue is about the memory leak that causes the graph to freeze after ~200 iterations, that's due to the creation of FigureCanvas on every temperature update. Instead, you can re-use your existing FigureCanvas, changing the last line of updateTemperature to
self.canvas.draw()
I want to create and start multiple timers in a for loop. My approach has been as follows:
import wx
trials = range(1, 3)
timers = range(7)
name = 'timer'
class TimersClass(wx.Frame):
def __init__(self, *args, **kwargs):
wx.Frame.__init__(self, *args, **kwargs)
panel = wx.Panel(self)
self.button1 = wx.Button(panel, label = 'Go')
self.Bind(wx.EVT_BUTTON, self.Timers, self.button1)
def Timers(self, event):
for trial in trials:
for timer in timers:
setattr(self, name + str(timer) + '_' + 'iteration' + str(trial), wx.Timer(self))
print name + str(timer) + '_' + 'iteration' + str(trial)
eval(name + str(timer) + '_' + 'iteration' + str(trial) + '.Start(' + str(timer * 1000, ) + ', OneShoot = True)')
self.Bind(wx.EVT_TIMER, self.Hi)
def Hi(self, event):
print 'Hi, bastard!'
app = wx.App()
frame = TimersClass(None)
frame.Center()
frame.Show()
app.MainLoop()
But the self.timers objects appear not to be created:
Traceback (most recent call last):
File "Escritorio/iteration_timers.py", line 24, in Timers
eval(name + str(timer) + '_' + 'iteration' + str(trial) + '.Start(' + str(timer * 1000, ) + ', OneShoot = True)')
File "", line 1, in
NameError: name 'timer0_iteration1' is not defined
Someone knows why this code doesn't run, or have an alternative approach?
Thanks a lot!!
I recommend using a reasonable structure within the class to store the timers (rather than creating a bunch of class attributes with complicated encoded names). Here's an example:
import wx
trial_range = range(1, 3)
timer_range = range(7)
class TimersClass(wx.Frame):
def __init__(self, *args, **kwargs):
wx.Frame.__init__(self, *args, **kwargs)
panel = wx.Panel(self)
self.button1 = wx.Button(panel, label = 'Go')
self.Bind(wx.EVT_BUTTON, self.BuildTimers, self.button1)
self.timers = {} # a structure to hold the timers
def BuildTimers(self, event):
for trial in trial_range:
for timer in timer_range:
new_timer = wx.Timer(self)
new_timer.Start(1 + timer*1000, oneShot=True)
key = (trial, timer)
self.timers[key] = new_timer # to keep the timer in the class
new_timer.mykey = key #optional: to know the key of the timer via the event (see Hi())
self.Bind(wx.EVT_TIMER, self.Hi)
def Hi(self, event):
print "timer:",
print event.GetEventObject().mykey
app = wx.PySimpleApp()
frame = TimersClass(None)
frame.Center()
frame.Show()
app.MainLoop()
When this runs it produces:
timer: (1, 0)
timer: (2, 0)
timer: (1, 1)
timer: (2, 1)
timer: (1, 2)
# etc
I'm trying to get the button to be right of the label. I set the tuple and am still not sure why it covers the label.
Also is there a good tutorial available on wxpython geometry?
import wx
import wx.lib.agw.gradientbutton as GB
def GetRoundBitmap( w, h, r ):
maskColor = wx.Color(0,0,0)
shownColor = wx.Color(5,5,5)
b = wx.EmptyBitmap(w,h)
dc = wx.MemoryDC(b)
dc.SetBrush(wx.Brush(maskColor))
dc.DrawRectangle(0,0,w,h)
dc.SetBrush(wx.Brush(shownColor))
dc.SetPen(wx.Pen(shownColor))
dc.DrawRoundedRectangle(0,0,w,h,r)
dc.SelectObject(wx.NullBitmap)
b.SetMaskColour(maskColor)
return b
def GetRoundShape( w, h, r ):
return wx.RegionFromBitmap( GetRoundBitmap(w,h,r) )
class FancyFrame(wx.Frame):
def __init__(self):
style = ( wx.CLIP_CHILDREN | wx.STAY_ON_TOP | wx.FRAME_NO_TASKBAR |
wx.NO_BORDER | wx.FRAME_SHAPED )
wx.Frame.__init__(self, None, title='Fancy', style = style)
self.SetSize( (250, 40) )
self.SetPosition( (500,500) )
self.SetTransparent( 160 )
self.Bind(wx.EVT_KEY_UP, self.On_Esc)
self.Bind(wx.EVT_MOTION, self.OnMouse)
self.Bind(wx.EVT_PAINT, self.OnPaint)
if wx.Platform == '__WXGTK__':
self.Bind(wx.EVT_WINDOW_CREATE, self.SetRoundShape)
else:
self.SetRoundShape()
self.Show(True)
geo = wx.GridBagSizer()
self.label = wx.StaticText(self,-1,label=u'Hello !')
self.label.SetBackgroundColour("#000000")
self.label.SetForegroundColour(wx.WHITE)
self.label.SetSize( (50, 10) )
geo.Add(self.label, (0,0))
self.button = GB.GradientButton(self,label="button")
self.label.SetBackgroundColour("#9e9e9e")
geo.Add(self.button, (0,1))
def SetRoundShape(self, event=None):
w, h = self.GetSizeTuple()
self.SetShape(GetRoundShape( w,h, 10 ) )
def OnPaint(self, event):
dc = wx.PaintDC(self)
dc = wx.GCDC(dc)
w, h = self.GetSizeTuple()
r = 10
dc.SetPen( wx.Pen("#000000", width = 4 ) )
dc.SetBrush( wx.Brush("#9e9e9e") )
dc.DrawRoundedRectangle( 0,0,w,h,r )
def On_Esc(self, event):
"""quit if user press Esc"""
if event.GetKeyCode() == 27 : #27 is Esc
self.Close(force=True)
else:
event.Skip()
def OnMouse(self, event):
"""implement dragging"""
if not event.Dragging():
self._dragPos = None
return
self.CaptureMouse()
if not self._dragPos:
self._dragPos = event.GetPosition()
else:
pos = event.GetPosition()
displacement = self._dragPos - pos
self.SetPosition( self.GetPosition() - displacement )
app = wx.App()
f = FancyFrame()
app.MainLoop()
You forgot to set FancyFrame to have the given layout sizer.
In other words you need to add one line to the end of your FancyFrame's __init__ method.
self.SetSizerAndFit(geo)