0
# GUI Widgets
1
2
Interactive UI components: buttons, sliders, text entry, and containers.
3
4
## Quick Reference
5
6
```python
7
from pyglet import gui
8
9
# Frame (manages widget events)
10
frame = gui.Frame(window)
11
12
# Button
13
button = gui.PushButton(x=100, y=100, pressed=pressed_img, unpressed=unpressed_img)
14
frame.add_widget(button)
15
16
@button.event
17
def on_press():
18
print("Button pressed!")
19
20
# Slider
21
slider = gui.Slider(x=100, y=50, base=base_img, knob=knob_img)
22
frame.add_widget(slider)
23
24
@slider.event
25
def on_change(value):
26
print(f"Value: {value}")
27
28
# Text entry
29
text_entry = gui.TextEntry('Initial text', x=100, y=150, width=200)
30
frame.add_widget(text_entry)
31
```
32
33
## Frame
34
35
```python
36
class pyglet.gui.Frame:
37
"""Container managing widget event dispatching"""
38
__init__(window, enable=True, cell_size=64, order=0)
39
40
# Methods
41
def add_widget(widget)
42
def remove_widget(widget)
43
44
# Properties
45
enable: bool # Enable/disable event handling
46
widgets: list # All managed widgets
47
```
48
49
## Buttons
50
51
```python
52
class pyglet.gui.PushButton(WidgetBase):
53
"""Clickable button"""
54
__init__(x, y, pressed, unpressed, hover=None, batch=None, group=None)
55
56
# Properties
57
value: bool # Current pressed state
58
enabled: bool
59
60
# Events
61
@button.event
62
def on_press():
63
"""Button pressed"""
64
65
@button.event
66
def on_release():
67
"""Button released"""
68
69
class pyglet.gui.ToggleButton(PushButton):
70
"""Button with on/off state"""
71
# Same as PushButton but maintains toggle state
72
```
73
74
## Slider
75
76
```python
77
class pyglet.gui.Slider(WidgetBase):
78
"""Horizontal slider"""
79
__init__(x, y, base, knob, edge=0, batch=None, group=None)
80
81
# Properties
82
value: float # 0.0 to 1.0
83
min, max: float # Value range
84
enabled: bool
85
86
# Events
87
@slider.event
88
def on_change(value):
89
"""Value changed"""
90
```
91
92
## Text Entry
93
94
```python
95
class pyglet.gui.TextEntry(WidgetBase):
96
"""Single-line text input"""
97
__init__(text, x, y, width, color=(255,255,255,255), text_color=(0,0,0,255),
98
caret_color=(0,0,0), batch=None, group=None)
99
100
# Properties
101
text: str
102
focus: bool # Has keyboard focus
103
enabled: bool
104
105
# Events
106
@text_entry.event
107
def on_commit(text):
108
"""Enter pressed"""
109
```
110
111
## NinePatch
112
113
```python
114
class pyglet.gui.NinePatch:
115
"""Scalable UI element from 9-slice image"""
116
__init__(texture_region, left, right, top, bottom)
117
118
def get_subregion(x, y, width, height) -> TextureRegion
119
```
120
121
## Widget Base
122
123
All widgets inherit from:
124
125
```python
126
class pyglet.gui.WidgetBase:
127
"""Base widget class"""
128
129
# Properties
130
x, y: int
131
width, height: int
132
enabled: bool
133
batch: Batch
134
group: Group
135
136
# Events
137
def on_mouse_press(x, y, button, modifiers)
138
def on_mouse_release(x, y, button, modifiers)
139
def on_mouse_drag(x, y, dx, dy, buttons, modifiers)
140
def on_mouse_motion(x, y, dx, dy)
141
```
142
143
## Examples
144
145
### Button Grid
146
```python
147
from pyglet import gui, image, graphics
148
149
window = pyglet.window.Window()
150
batch = graphics.Batch()
151
frame = gui.Frame(window)
152
153
# Load button images
154
pressed = image.load('button_pressed.png')
155
unpressed = image.load('button_unpressed.png')
156
157
# Create button grid
158
buttons = []
159
for row in range(3):
160
for col in range(3):
161
x = 50 + col * 120
162
y = 400 - row * 80
163
button = gui.PushButton(x, y, pressed, unpressed, batch=batch)
164
button.id = row * 3 + col
165
frame.add_widget(button)
166
buttons.append(button)
167
168
@button.event
169
def on_press(btn=button):
170
print(f"Button {btn.id} pressed")
171
172
@window.event
173
def on_draw():
174
window.clear()
175
batch.draw()
176
177
pyglet.app.run()
178
```
179
180
### Volume Slider
181
```python
182
slider = gui.Slider(x=100, y=100, base=base_img, knob=knob_img, batch=batch)
183
frame.add_widget(slider)
184
185
@slider.event
186
def on_change(value):
187
# value is 0.0 to 1.0
188
music_player.volume = value
189
volume_label.text = f"Volume: {int(value * 100)}%"
190
```
191
192
### Text Input Dialog
193
```python
194
text_entry = gui.TextEntry(
195
'Enter name...',
196
x=100, y=200, width=300,
197
batch=batch
198
)
199
frame.add_widget(text_entry)
200
text_entry.focus = True
201
202
@text_entry.event
203
def on_commit(text):
204
print(f"Submitted: {text}")
205
text_entry.text = '' # Clear
206
207
@window.event
208
def on_key_press(symbol, modifiers):
209
if symbol == pyglet.window.key.ESCAPE:
210
text_entry.focus = False
211
```
212
213
### Custom Widget
214
```python
215
class CustomButton(gui.WidgetBase):
216
def __init__(self, x, y, width, height, text):
217
super().__init__(x, y, width, height)
218
self.text = text
219
self.pressed = False
220
221
# Create visual elements
222
self.bg = pyglet.shapes.Rectangle(
223
x, y, width, height,
224
color=(100, 100, 200)
225
)
226
self.label = pyglet.text.Label(
227
text,
228
x=x + width//2, y=y + height//2,
229
anchor_x='center', anchor_y='center'
230
)
231
232
def on_mouse_press(self, x, y, button, modifiers):
233
if self._check_hit(x, y):
234
self.pressed = True
235
self.bg.color = (150, 150, 255)
236
self.dispatch_event('on_press')
237
238
def on_mouse_release(self, x, y, button, modifiers):
239
if self.pressed:
240
self.pressed = False
241
self.bg.color = (100, 100, 200)
242
243
def _check_hit(self, x, y):
244
return (self.x <= x <= self.x + self.width and
245
self.y <= y <= self.y + self.height)
246
247
CustomButton.register_event_type('on_press')
248
```
249
250
## Common Patterns
251
252
### Menu System
253
```python
254
class Menu:
255
def __init__(self, window):
256
self.window = window
257
self.frame = gui.Frame(window)
258
self.batch = pyglet.graphics.Batch()
259
self.buttons = []
260
261
def add_button(self, text, callback, y_offset):
262
# Create button (simplified)
263
button = gui.PushButton(
264
x=window.width // 2 - 100,
265
y=window.height // 2 + y_offset,
266
pressed=pressed_img,
267
unpressed=unpressed_img,
268
batch=self.batch
269
)
270
button.callback = callback
271
self.frame.add_widget(button)
272
self.buttons.append(button)
273
274
@button.event
275
def on_press():
276
callback()
277
278
def draw(self):
279
self.batch.draw()
280
281
# Usage
282
menu = Menu(window)
283
menu.add_button("Start", lambda: print("Start game"), 50)
284
menu.add_button("Options", lambda: print("Options"), 0)
285
menu.add_button("Quit", lambda: pyglet.app.exit(), -50)
286
```
287
288
## Performance Tips
289
290
1. **Use batching**: Add widgets to a Batch
291
2. **Frame overhead**: Frame uses spatial hashing for efficient hit testing
292
3. **Disable when hidden**: Set `enabled=False` on hidden widgets
293
4. **Custom widgets**: Inherit from WidgetBase for consistency
294
295
## Common Issues
296
297
1. **Events not firing**: Ensure widget added to Frame
298
2. **Z-order**: Later widgets drawn on top
299
3. **Focus**: Only one TextEntry can have focus
300
4. **Hit testing**: Widgets use rectangular bounds
301
5. **Image requirements**: Button needs at least pressed/unpressed images
302