]> git.saurik.com Git - wxWidgets.git/blob - wxPython/wx/lib/buttonpanel.py
d99ebee363dc957b47d5a4ce33c2ea7612b6c84f
[wxWidgets.git] / wxPython / wx / lib / buttonpanel.py
1 # --------------------------------------------------------------------------- #
2 # FANCYBUTTONPANEL Widget wxPython IMPLEMENTATION
3 #
4 # Original C++ Code From Eran. You Can Find It At:
5 #
6 # http://wxforum.shadonet.com/viewtopic.php?t=6619
7 #
8 # License: wxWidgets license
9 #
10 #
11 # Python Code By:
12 #
13 # Andrea Gavana, @ 02 Oct 2006
14 # Latest Revision: 02 Oct 2006, 17.00 GMT
15 #
16 #
17 # For All Kind Of Problems, Requests Of Enhancements And Bug Reports, Please
18 # Write To Me At:
19 #
20 # andrea.gavana@gmail.com
21 # gavana@kpo.kz
22 #
23 # Or, Obviously, To The wxPython Mailing List!!!
24 #
25 #
26 # End Of Comments
27 # --------------------------------------------------------------------------- #
28
29 """
30 With `ButtonPanel` class you have a panel with gradient coloring
31 on it and with the possibility to place some buttons on it. Using a
32 standard panel with normal wx.Buttons leads to an ugly result: the
33 buttons are placed correctly on the panel - but with grey area around
34 them. Gradient coloring is kept behind the images - this was achieved
35 due to the PNG format and the transparency of the bitmaps.
36
37 The image are functioning like a buttons and can be caught in your
38 code using the usual self.Bind(wx.EVT_BUTTON, self.OnButton) method.
39
40 The control is generic, and support theming (well, I tested it under
41 Windows with the three defauls themes: grey, blue, silver and the
42 classic look).
43
44
45 Usage
46 -----
47
48 The following example shows a simple implementation that uses ButtonPanel
49 inside a very simple frame::
50
51 class MyFrame(wx.Frame):
52
53 def __init__(self, parent, id=-1, title="ButtonPanel", pos=wx.DefaultPosition,
54 size=(800, 600), style=wx.DEFAULT_FRAME_STYLE):
55
56 wx.Frame.__init__(self, parent, id, title, pos, size, style)
57
58 mainPanel = wx.Panel(self, -1)
59 self.logtext = wx.TextCtrl(mainPanel, -1, "", style=wx.TE_MULTILINE)
60
61 vSizer = wx.BoxSizer(wx.VERTICAL)
62 mainPanel.SetSizer(vSizer)
63
64 alignment = BP_ALIGN_RIGHT
65
66 titleBar = ButtonPanel(mainPanel, -1, "A Simple Test & Demo")
67
68 btn1 = ButtonInfo(wx.NewId(), wx.Bitmap("png4.png", wx.BITMAP_TYPE_PNG))
69 titleBar.AddButton(btn1)
70 self.Bind(wx.EVT_BUTTON, self.OnButton, btn1)
71
72 btn2 = ButtonInfo(wx.NewId(), wx.Bitmap("png3.png", wx.BITMAP_TYPE_PNG))
73 titleBar.AddButton(btn2)
74 self.Bind(wx.EVT_BUTTON, self.OnButton, btn2)
75
76 btn3 = ButtonInfo(wx.NewId(), wx.Bitmap("png2.png", wx.BITMAP_TYPE_PNG))
77 titleBar.AddButton(btn3)
78 self.Bind(wx.EVT_BUTTON, self.OnButton, btn3)
79
80 btn4 = ButtonInfo(wx.NewId(), wx.Bitmap("png1.png", wx.BITMAP_TYPE_PNG))
81 titleBar.AddButton(btn4)
82 self.Bind(wx.EVT_BUTTON, self.OnButton, btn4)
83
84 titleBar.SetColor(BP_TEXT_COLOR, wx.WHITE)
85 titleBar.SetColor(BP_CAPTION_BORDER_COLOR, wx.WHITE)
86 vSizer.Add(titleBar, 0, wx.EXPAND)
87 vSizer.Add((20, 20))
88 vSizer.Add(self.logtext, 1, wx.EXPAND|wx.ALL, 5)
89
90 vSizer.Layout()
91
92 # our normal wxApp-derived class, as usual
93
94 app = wx.PySimpleApp()
95
96 frame = MyFrame(None)
97 app.SetTopWindow(frame)
98 frame.Show()
99
100 app.MainLoop()
101
102
103 License And Version:
104
105 ButtonPanel Is Freeware And Distributed Under The wxPython License.
106
107 Latest Revision: Andrea Gavana @ 02 Oct 2006, 17.00 GMT
108 Version 0.1.
109
110 """
111
112
113 import wx
114
115 # Some constants
116 BP_CAPTION_COLOR = 0,
117 BP_CAPTION_GRADIENT_COLOR = 1
118 BP_CAPTION_BORDER_COLOR = 2
119 BP_TEXT_COLOR = 3
120
121 # Buttons states
122 BP_BTN_NONE = 100
123 BP_BTN_PRESSED = 101
124 BP_BTN_HOVER = 102
125
126 # Flags for HitTest() method
127 BP_HT_BUTTON = 200
128 BP_HT_NONE = 201
129
130 # Alignment of buttons in the panel
131 BP_ALIGN_RIGHT = 1
132 BP_ALIGN_LEFT = 2
133
134 # ButtonPanel default style
135 BP_DEFAULT_STYLE = 2
136
137
138 def BrightenColor(color, factor):
139 """ Bright the input colour by a factor."""
140
141 val = color.Red()*factor
142 if val > 255:
143 red = 255
144 else:
145 red = val
146
147 val = color.Green()*factor
148 if val > 255:
149 green = 255
150 else:
151 green = val
152
153 val = color.Blue()*factor
154 if val > 255:
155 blue = 255
156 else:
157 blue = val
158
159 return wx.Color(red, green, blue)
160
161
162 # -- ButtonInfo class implementation ----------------------------------------
163 # This class holds information about every button that is added to
164 # ButtonPanel. It is an auxiliary class that you should use
165 # every time you add a button.
166
167 class ButtonInfo:
168
169 def __init__(self, id=wx.ID_ANY, bmp=wx.NullBitmap, status=-1):
170 """
171 Default class constructor.
172
173 Parameters:
174 - id: the button id;
175 - bmp: the associated bitmap;
176 - status: button status (pressed, hovered, None).
177 """
178 if id == wx.ID_ANY:
179 id = wx.NewId()
180 self._id = id
181 self._bmp = bmp
182 self._status = status
183 self._rect = wx.Rect()
184
185
186 def GetBitmap(self):
187 """ Returns the associated bitmap. """
188
189 return self._bmp
190
191
192 def GetRect(self):
193 """ Returns the button rect. """
194
195 return self._rect
196
197
198 def GetStatus(self):
199 """ Returns the button status. """
200
201 return self._status
202
203
204 def GetId(self):
205 """ Returns the button id. """
206
207 return self._id
208
209
210 def SetRect(self, rect):
211 """ Sets the button rect. """
212
213 self._rect = rect
214
215
216 def SetBitmap(self, bmp):
217 """ Sets the associated bitmap. """
218
219 self._bmp = bmp
220
221
222 def SetStatus(self, status):
223 """ Sets the button status. """
224
225 self._status = status
226
227
228 def SetId(self, id):
229 """ Sets the button id. """
230
231 self._id = id
232
233 Bitmap = property(GetBitmap, SetBitmap)
234 Id = property(GetId, SetId)
235 Rect = property(GetRect, SetRect)
236 Status = property(GetStatus, SetStatus)
237
238
239
240
241 # -- ButtonPanel class implementation ----------------------------------
242 # This is the main class.
243
244 BASE = wx.PyPanel
245
246 class ButtonPanel(BASE):
247
248 def __init__(self, parent, id=wx.ID_ANY, text="", style=BP_DEFAULT_STYLE,
249 alignment=BP_ALIGN_RIGHT, name="buttonPanel"):
250 """
251 Default class constructor.
252
253 - parent: parent window
254 - id: window ID
255 - text: text to draw
256 - style: window style
257 - alignment: alignment of buttons (left or right)
258 - name: window class name
259 """
260
261 BASE.__init__(self, parent, id, wx.DefaultPosition, wx.DefaultSize,
262 wx.NO_BORDER, name=name)
263 self._vButtons = []
264
265 self._text = text
266 self._nStyle = style
267 self._alignment = alignment
268 self._nPadding = 6
269 self._nBmpSize = 16
270 self._colorFrom = wx.SystemSettings.GetColour(wx.SYS_COLOUR_ACTIVECAPTION)
271 self._colorTo = wx.WHITE
272 self._colorBorder = wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DFACE)
273 self._colorText = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT)
274 self._firsttime = True
275 self._borderPenWidth = 3
276
277 self.Bind(wx.EVT_PAINT, self.OnPaint)
278 self.Bind(wx.EVT_SIZE, self.OnSize)
279 self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
280 self.Bind(wx.EVT_MOTION, self.OnMouseMove)
281 self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
282 self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)
283 self.Bind(wx.EVT_LEAVE_WINDOW, self.OnMouseLeave)
284 self.Bind(wx.EVT_ENTER_WINDOW, self.OnMouseEnterWindow)
285
286
287 def AddButton(self, btnInfo):
288 """
289 Adds a button to ButtonPanel. Remember to pass a ButtonInfo instance to
290 this method. See the demo for details.
291 """
292
293 self._vButtons.append(btnInfo)
294 self.Refresh()
295
296
297 def RemoveAllButtons(self):
298 """ Remove all the buttons from ButtonPanel. """
299
300 self._vButtons = []
301 self.Refresh()
302
303
304 def GetAlignment(self):
305 """ Returns the button alignment (left, right). """
306
307 return self._alignment
308
309
310 def SetAlignment(self, alignment):
311 """ Sets the button alignment (left, right). """
312
313 self._alignment = alignment
314
315
316 def DoGetBestSize(self):
317 w = h = 0
318 if self._text:
319 dc = wx.ClientDC(self)
320 boldFont = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
321 boldFont.SetWeight(wx.FONTWEIGHT_BOLD)
322 dc.SetFont(boldFont)
323 tw, th = dc.GetTextExtent(self._text)
324 h = max(h, th)
325 w += tw + self._nPadding
326
327 if self._vButtons:
328 bh = self._vButtons[0].GetBitmap().GetHeight()
329 bw = self._vButtons[0].GetBitmap().GetWidth()
330
331 bh += 2*self._nPadding + 2*self._borderPenWidth
332 h = max(h, bh)
333
334 bw = (len(self._vButtons)+1) * (bw + 2*self._nPadding)
335 w += bw
336
337 return (w, h)
338
339
340
341 def OnPaint(self, event):
342 """ Handles the wx.EVT_PAINT event for ButtonPanel. """
343
344 dc = wx.BufferedPaintDC(self)
345 rect = self.GetClientRect()
346
347 ##print rect, self.GetRect(), self.GetBestSize(), self.GetMinSize()
348
349 # Draw gradient color in the background of the panel
350 self.FillGradientColor(dc, rect)
351
352 backBrush = wx.TRANSPARENT_BRUSH
353 borderPen = wx.Pen(self._colorBorder)
354 size = self.GetSize()
355 borderPen.SetWidth(self._borderPenWidth)
356
357 # Draw a rectangle around the panel
358 dc.SetBrush(backBrush)
359 dc.SetPen(borderPen)
360 dc.DrawRectangleRect(rect)
361
362 # Draw the text
363 textWidth, textHeight = 0, 0
364
365 if self._text != "":
366
367 dc.SetTextForeground(self._colorText)
368 borderPen.SetWidth(2)
369 boldFont = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
370 boldFont.SetWeight(wx.FONTWEIGHT_BOLD)
371 dc.SetFont(boldFont)
372
373 textWidth, textHeight = dc.GetTextExtent(self._text)
374
375 if self._alignment == BP_ALIGN_RIGHT:
376 textX = self._nPadding
377 else:
378 textX = rect.width - textWidth - self._nPadding
379
380 textY = (rect.GetHeight() - textHeight)/2
381 dc.DrawText(self._text, textX, textY)
382
383 if self._vButtons:
384
385 height = self._vButtons[0].GetBitmap().GetHeight()
386 self._nBmpSize = self._vButtons[0].GetBitmap().GetWidth()
387 height += 2*self._nPadding + 2*self._borderPenWidth
388
389 if self._firsttime: # this probably isn't needed anymore now that DoGetBestSize is implemented...
390 self.GetContainingSizer().Layout()
391 self._firsttime = False
392
393 # Draw all buttons
394 # [ Padding | Text | .. Buttons .. | Padding ]
395
396 totalWidth = rect.width - self._nPadding*2 - textWidth
397
398 # The button is drawn inside a circle with padding of self._nPadding around it
399 # so the width of each image = imageWidth + 2 * self._nPadding
400 nImageWidth = self._nBmpSize + 2*self._nPadding
401
402 if self._alignment == BP_ALIGN_RIGHT:
403
404 leftEndX = self._nPadding + textWidth
405 posx = rect.width - nImageWidth - self._nPadding
406
407 for ii in xrange(len(self._vButtons)):
408
409 # Make sure we can keep drawing
410 if posx < leftEndX:
411 break
412
413 # Draw a rectangle around the buttons
414 if self._vButtons[ii].GetStatus() == BP_BTN_HOVER:
415
416 dc.SetBrush(wx.Brush(wx.Color(225, 225, 255)))
417 dc.SetPen(wx.Pen(wx.SystemSettings.GetColour(wx.SYS_COLOUR_ACTIVECAPTION)))
418 dc.DrawRectangle(posx, self._borderPenWidth, nImageWidth, nImageWidth)
419 dc.DrawBitmap(self._vButtons[ii].GetBitmap(), posx + self._nPadding, self._nPadding + self._borderPenWidth, True)
420
421 elif self._vButtons[ii].GetStatus() == BP_BTN_PRESSED:
422
423 dc.SetBrush(wx.Brush(wx.Color(225, 225, 255)))
424 dc.SetPen(wx.Pen(wx.SystemSettings.GetColour(wx.SYS_COLOUR_ACTIVECAPTION)))
425 dc.DrawRectangle(posx, self._borderPenWidth, nImageWidth, nImageWidth)
426 dc.DrawBitmap(self._vButtons[ii].GetBitmap(), posx + self._nPadding, self._nPadding + self._borderPenWidth + 1, True)
427
428 else:
429
430 dc.DrawBitmap(self._vButtons[ii].GetBitmap(), posx + self._nPadding, self._nPadding + self._borderPenWidth, True)
431
432 self._vButtons[ii].SetRect(wx.Rect(posx, self._borderPenWidth, nImageWidth, nImageWidth))
433 posx -= nImageWidth
434
435 else:
436
437 rightStartX = textX - self._nPadding - nImageWidth
438 posx = self._nPadding
439
440 for ii in xrange(len(self._vButtons)):
441
442 # Make sure we can keep drawing
443 if posx > rightStartX:
444 break
445
446 # Draw a rectangle around the buttons
447 if self._vButtons[ii].GetStatus() == BP_BTN_HOVER:
448
449 dc.SetBrush(wx.Brush(wx.Color(225, 225, 255)))
450 dc.SetPen(wx.Pen(wx.SystemSettings.GetColour(wx.SYS_COLOUR_ACTIVECAPTION)))
451 dc.DrawRectangle(posx, self._borderPenWidth, nImageWidth, nImageWidth)
452 dc.DrawBitmap(self._vButtons[ii].GetBitmap(), posx + self._nPadding, self._nPadding + self._borderPenWidth, True)
453
454 elif self._vButtons[ii].GetStatus() == BP_BTN_PRESSED:
455
456 dc.SetBrush(wx.Brush(wx.Color(225, 225, 255)))
457 dc.SetPen(wx.Pen(wx.SystemSettings.GetColour(wx.SYS_COLOUR_ACTIVECAPTION)))
458 dc.DrawRectangle(posx, self._borderPenWidth, nImageWidth, nImageWidth)
459 dc.DrawBitmap(self._vButtons[ii].GetBitmap(), posx + self._nPadding, self._nPadding + self._borderPenWidth + 1, True)
460
461 else:
462
463 dc.DrawBitmap(self._vButtons[ii].GetBitmap(), posx + self._nPadding, self._nPadding + self._borderPenWidth, True)
464
465 self._vButtons[ii].SetRect(wx.Rect(posx, self._borderPenWidth, nImageWidth, nImageWidth))
466 posx += nImageWidth
467
468 # Update all other buttons that they are not drawn (by setting the rect to 0)
469 for cur in xrange(ii+1, len(self._vButtons)):
470 self._vButtons[cur].SetRect(wx.Rect(0, 0, 0, 0))
471
472
473 def OnEraseBackground(self, event):
474 """ Handles the wx.EVT_ERASE_BACKGROUND event for ButtonPanel (does nothing). """
475
476 pass
477
478
479 def OnSize(self, event):
480 """ Handles the wx.EVT_SIZE event for ButtonPanel. """
481
482 self.Refresh()
483 event.Skip()
484
485
486 def SetColor(self, switch, color):
487 """
488 Sets the color depending on switch:
489 - BP_CAPTION_COLOR: caption color;
490 - BP_CAPTION_GRADIENT_COLOR: gradient color;
491 - BP_CAPTION_BORDER_COLOR; border color;
492 - BP_TEXT_COLOR: text color.
493 """
494
495 if switch == BP_CAPTION_COLOR:
496 self._colorFrom = color
497 elif switch == BP_CAPTION_GRADIENT_COLOR:
498 self._colorTo = color
499 elif switch == BP_CAPTION_BORDER_COLOR:
500 self._colorBorder = color
501 elif switch == BP_TEXT_COLOR:
502 self._colorText = color
503
504
505 def FillGradientColor(self, dc, rect):
506 """ Gradient fill from colour 1 to colour 2 with top to bottom. """
507
508 if rect.height < 1 or rect.width < 1:
509 return
510
511 size = rect.height
512
513 # calculate gradient coefficients
514 style = self.GetParent().GetWindowStyleFlag()
515 col2 = self._colorFrom
516 col1 = self._colorTo
517
518 rf, gf, bf = 0, 0, 0
519 rstep = float((col2.Red() - col1.Red()))/float(size)
520 gstep = float((col2.Green() - col1.Green()))/float(size)
521 bstep = float((col2.Blue() - col1.Blue()))/float(size)
522
523 for y in xrange(rect.y, rect.y + size):
524
525 currCol = wx.Colour(col1.Red() + rf, col1.Green() + gf, col1.Blue() + bf)
526 dc.SetBrush(wx.Brush(currCol, wx.SOLID))
527 dc.SetPen(wx.Pen(currCol))
528 dc.DrawLine(rect.x, y, rect.x + rect.width, y)
529 rf += rstep
530 gf += gstep
531 bf += bstep
532
533
534 def OnMouseMove(self, event):
535 """ Handles the wx.EVT_MOTION event for ButtonPanel. """
536
537 # Check to see if we are hovering a button
538 for ii in xrange(len(self._vButtons)):
539 if self._vButtons[ii].GetRect().Inside(event.GetPosition()):
540 self._vButtons[ii].SetStatus(BP_BTN_HOVER)
541 else:
542 self._vButtons[ii].SetStatus(BP_BTN_NONE)
543
544 self.Refresh()
545 event.Skip()
546
547
548 def OnLeftDown(self, event):
549 """ Handles the wx.EVT_LEFT_DOWN event for ButtonPanel. """
550
551 tabId, hit = self.HitTest(event.GetPosition())
552
553 if hit == BP_HT_BUTTON:
554
555 self._vButtons[tabId].SetStatus(BP_BTN_PRESSED)
556 self.Refresh()
557
558
559 def OnLeftUp(self, event):
560 """ Handles the wx.EVT_LEFT_UP event for ButtonPanel. """
561
562 tabId, hit = self.HitTest(event.GetPosition())
563
564 if hit == BP_HT_BUTTON:
565 if self._vButtons[tabId].GetStatus() == BP_BTN_PRESSED:
566 # Fire a button click event
567 btnEvent = wx.CommandEvent(wx.wxEVT_COMMAND_BUTTON_CLICKED, self._vButtons[tabId].GetId())
568 self.GetEventHandler().ProcessEvent(btnEvent)
569
570 # Update the button status to be hovered
571 self._vButtons[tabId].SetStatus(BP_BTN_HOVER)
572 self.Refresh()
573
574
575 def OnMouseLeave(self, event):
576 """ Handles the wx.EVT_LEAVE_WINDOW event for ButtonPanel. """
577
578 # Reset all buttons statuses
579 for ii in xrange(len(self._vButtons)):
580 self._vButtons[ii].SetStatus(BP_BTN_NONE)
581
582 self.Refresh()
583 event.Skip()
584
585
586 def OnMouseEnterWindow(self, event):
587 """ Handles the wx.EVT_ENTER_WINDOW event for ButtonPanel. """
588
589 event.Skip()
590
591
592 def HitTest(self, pt):
593 """
594 HitTest method for ButtonPanel. Returns the button (if any) and
595 a flag (if any).
596 """
597
598 btnIdx = -1
599
600 for ii in xrange(len(self._vButtons)):
601 if self._vButtons[ii].GetRect().Inside(pt):
602 return ii, BP_HT_BUTTON
603
604 return -1, BP_HT_NONE
605
606
607