This is Part 2. In Part 1 we wrote a Cassowary demo in Python using the kiwisolver package. Now we’ll apply what we learned by using kiwisolver to handle a wxPython layout.
In brief we need to:
- Create a
wx.App()
object. - Create our wxPython objects.
- Create a
kiwisolver.Solver()
object. - Create a
Widget
object for each wxPython object. The Widget is pretty simple: it just stores x, y, width, and height in akiwisolver.Variable
. - Create a dictionary to store each Widget object with its wxPython object.
- Create our constraints: each constraint is a linear equation which describes how one object relates to another.
- Call
addConstraint()
to add each constraint to the Solver. - The
wx.Frame
is set usingaddEditVariable()
because it will be our input (the system will take in a new frame size and output the changes). - The
on_resize()
event handler is called every time the frame is resized.- Set the wframe.width and height variables to the wx.Frame’s new size.
- Call
updateVariables()
to perform the calculations on each Widget’s x, y, width, and height. - Update each wx object with its new x, y, width, and height.
Python code
import kiwisolver
import wx
class Widget(object):
def __init__(self, identifier):
self.x = kiwisolver.Variable('x-' + identifier)
self.y = kiwisolver.Variable('y-' + identifier)
self.width = kiwisolver.Variable('width-' + identifier)
self.height = kiwisolver.Variable('height-' + identifier)
def on_resize(event):
width, height = frame.GetSize()
solver.suggestValue(wframe.width, width)
solver.suggestValue(wframe.height, height)
solver.updateVariables()
for obj in db:
widget = db[obj]
obj.SetSize(
x=widget.x.value(),
y=widget.y.value(),
width=widget.width.value(),
height=widget.height.value())
event.Skip() # Allow default event handling.
def create_constraints():
box = db[textbox]
b1 = db[button1]
b2 = db[button2]
constraints = [
box.x == 10,
box.y == 10,
box.width == wframe.width - 20,
box.height == wframe.height - 100,
b1.width == button1.GetBestSize()[0],
b1.height == button1.GetBestSize()[1],
b2.width == button2.GetBestSize()[0],
b2.height == button2.GetBestSize()[1],
b1.x == box.x,
b1.y == box.y + box.height + 25,
b2.x == box.x + box.width - b2.width,
b2.y == b1.y,
]
for constraint in constraints:
solver.addConstraint(constraint)
solver.addEditVariable(wframe.width, 'strong')
solver.addEditVariable(wframe.height, 'strong')
if __name__ == '__main__':
app = wx.App()
frame = wx.Frame(
parent=None,
id=wx.ID_ANY,
title='Cassowary Demo')
panel = wx.Panel(parent=frame, id=wx.ID_ANY)
textbox = wx.TextCtrl(
parent=panel,
id=wx.ID_ANY,
value='When in the course of human events...',
style=wx.TE_MULTILINE)
button1 = wx.Button(
parent=panel,
id=wx.ID_ANY,
label='Button1')
button2 = wx.Button(
parent=panel,
id=wx.ID_ANY,
label='Button2')
wframe = Widget('frame')
db = {}
db[textbox] = Widget('textbox')
db[button1] = Widget('button1')
db[button2] = Widget('button2')
solver = kiwisolver.Solver()
create_constraints()
frame.Bind(wx.EVT_SIZE, on_resize)
frame.Show()
app.MainLoop()