]> git.saurik.com Git - wxWidgets.git/blame - wxPython/wx/lib/buttonpanel.py
Give some more specific info if PyWin can't be imported when frozen in
[wxWidgets.git] / wxPython / wx / lib / buttonpanel.py
CommitLineData
66dae888
RD
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"""
30With `ButtonPanel` class you have a panel with gradient coloring
31on it and with the possibility to place some buttons on it. Using a
32standard panel with normal wx.Buttons leads to an ugly result: the
33buttons are placed correctly on the panel - but with grey area around
34them. Gradient coloring is kept behind the images - this was achieved
35due to the PNG format and the transparency of the bitmaps.
36
37The image are functioning like a buttons and can be caught in your
38code using the usual self.Bind(wx.EVT_BUTTON, self.OnButton) method.
39
40The control is generic, and support theming (well, I tested it under
41Windows with the three defauls themes: grey, blue, silver and the
42classic look).
43
44
45Usage
46-----
47
48The following example shows a simple implementation that uses ButtonPanel
49inside 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
103License And Version:
104
105ButtonPanel Is Freeware And Distributed Under The wxPython License.
106
107Latest Revision: Andrea Gavana @ 02 Oct 2006, 17.00 GMT
108Version 0.1.
109
110"""
111
112
113import wx
114
115# Some constants
116BP_CAPTION_COLOR = 0,
117BP_CAPTION_GRADIENT_COLOR = 1
118BP_CAPTION_BORDER_COLOR = 2
119BP_TEXT_COLOR = 3
120
121# Buttons states
122BP_BTN_NONE = 100
123BP_BTN_PRESSED = 101
124BP_BTN_HOVER = 102
125
126# Flags for HitTest() method
127BP_HT_BUTTON = 200
128BP_HT_NONE = 201
129
130# Alignment of buttons in the panel
131BP_ALIGN_RIGHT = 1
132BP_ALIGN_LEFT = 2
133
134# ButtonPanel default style
135BP_DEFAULT_STYLE = 2
136
137
138def 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
167class 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
9d45af36 244class ButtonPanel(wx.PyPanel):
66dae888
RD
245
246 def __init__(self, parent, id=wx.ID_ANY, text="", style=BP_DEFAULT_STYLE,
247 alignment=BP_ALIGN_RIGHT, name="buttonPanel"):
248 """
249 Default class constructor.
250
251 - parent: parent window
252 - id: window ID
253 - text: text to draw
254 - style: window style
255 - alignment: alignment of buttons (left or right)
256 - name: window class name
257 """
258
9d45af36 259 wx.PyPanel.__init__(self, parent, id, wx.DefaultPosition, wx.DefaultSize,
66dae888
RD
260 wx.NO_BORDER, name=name)
261 self._vButtons = []
262
263 self._text = text
264 self._nStyle = style
265 self._alignment = alignment
266 self._nPadding = 6
267 self._nBmpSize = 16
268 self._colorFrom = wx.SystemSettings.GetColour(wx.SYS_COLOUR_ACTIVECAPTION)
269 self._colorTo = wx.WHITE
270 self._colorBorder = wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DFACE)
271 self._colorText = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT)
272 self._firsttime = True
273 self._borderPenWidth = 3
274
275 self.Bind(wx.EVT_PAINT, self.OnPaint)
276 self.Bind(wx.EVT_SIZE, self.OnSize)
277 self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
278 self.Bind(wx.EVT_MOTION, self.OnMouseMove)
279 self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
280 self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)
281 self.Bind(wx.EVT_LEAVE_WINDOW, self.OnMouseLeave)
282 self.Bind(wx.EVT_ENTER_WINDOW, self.OnMouseEnterWindow)
283
284
285 def AddButton(self, btnInfo):
286 """
287 Adds a button to ButtonPanel. Remember to pass a ButtonInfo instance to
288 this method. See the demo for details.
289 """
290
291 self._vButtons.append(btnInfo)
292 self.Refresh()
293
294
295 def RemoveAllButtons(self):
296 """ Remove all the buttons from ButtonPanel. """
297
298 self._vButtons = []
299 self.Refresh()
300
301
302 def GetAlignment(self):
303 """ Returns the button alignment (left, right). """
304
305 return self._alignment
306
307
308 def SetAlignment(self, alignment):
309 """ Sets the button alignment (left, right). """
310
311 self._alignment = alignment
312
313
314 def DoGetBestSize(self):
315 w = h = 0
316 if self._text:
317 dc = wx.ClientDC(self)
318 boldFont = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
319 boldFont.SetWeight(wx.FONTWEIGHT_BOLD)
320 dc.SetFont(boldFont)
321 tw, th = dc.GetTextExtent(self._text)
322 h = max(h, th)
323 w += tw + self._nPadding
324
325 if self._vButtons:
326 bh = self._vButtons[0].GetBitmap().GetHeight()
327 bw = self._vButtons[0].GetBitmap().GetWidth()
328
329 bh += 2*self._nPadding + 2*self._borderPenWidth
330 h = max(h, bh)
331
332 bw = (len(self._vButtons)+1) * (bw + 2*self._nPadding)
333 w += bw
334
335 return (w, h)
336
337
338
339 def OnPaint(self, event):
340 """ Handles the wx.EVT_PAINT event for ButtonPanel. """
341
342 dc = wx.BufferedPaintDC(self)
343 rect = self.GetClientRect()
344
345 ##print rect, self.GetRect(), self.GetBestSize(), self.GetMinSize()
346
347 # Draw gradient color in the background of the panel
348 self.FillGradientColor(dc, rect)
349
350 backBrush = wx.TRANSPARENT_BRUSH
351 borderPen = wx.Pen(self._colorBorder)
352 size = self.GetSize()
353 borderPen.SetWidth(self._borderPenWidth)
354
355 # Draw a rectangle around the panel
356 dc.SetBrush(backBrush)
357 dc.SetPen(borderPen)
358 dc.DrawRectangleRect(rect)
359
360 # Draw the text
361 textWidth, textHeight = 0, 0
362
363 if self._text != "":
364
365 dc.SetTextForeground(self._colorText)
366 borderPen.SetWidth(2)
367 boldFont = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
368 boldFont.SetWeight(wx.FONTWEIGHT_BOLD)
369 dc.SetFont(boldFont)
370
371 textWidth, textHeight = dc.GetTextExtent(self._text)
372
373 if self._alignment == BP_ALIGN_RIGHT:
374 textX = self._nPadding
375 else:
376 textX = rect.width - textWidth - self._nPadding
377
378 textY = (rect.GetHeight() - textHeight)/2
379 dc.DrawText(self._text, textX, textY)
380
381 if self._vButtons:
382
383 height = self._vButtons[0].GetBitmap().GetHeight()
384 self._nBmpSize = self._vButtons[0].GetBitmap().GetWidth()
385 height += 2*self._nPadding + 2*self._borderPenWidth
386
387 if self._firsttime: # this probably isn't needed anymore now that DoGetBestSize is implemented...
388 self.GetContainingSizer().Layout()
389 self._firsttime = False
390
391 # Draw all buttons
392 # [ Padding | Text | .. Buttons .. | Padding ]
393
394 totalWidth = rect.width - self._nPadding*2 - textWidth
395
396 # The button is drawn inside a circle with padding of self._nPadding around it
397 # so the width of each image = imageWidth + 2 * self._nPadding
398 nImageWidth = self._nBmpSize + 2*self._nPadding
399
400 if self._alignment == BP_ALIGN_RIGHT:
401
402 leftEndX = self._nPadding + textWidth
403 posx = rect.width - nImageWidth - self._nPadding
404
405 for ii in xrange(len(self._vButtons)):
406
407 # Make sure we can keep drawing
408 if posx < leftEndX:
409 break
410
411 # Draw a rectangle around the buttons
412 if self._vButtons[ii].GetStatus() == BP_BTN_HOVER:
413
414 dc.SetBrush(wx.Brush(wx.Color(225, 225, 255)))
415 dc.SetPen(wx.Pen(wx.SystemSettings.GetColour(wx.SYS_COLOUR_ACTIVECAPTION)))
416 dc.DrawRectangle(posx, self._borderPenWidth, nImageWidth, nImageWidth)
417 dc.DrawBitmap(self._vButtons[ii].GetBitmap(), posx + self._nPadding, self._nPadding + self._borderPenWidth, True)
418
419 elif self._vButtons[ii].GetStatus() == BP_BTN_PRESSED:
420
421 dc.SetBrush(wx.Brush(wx.Color(225, 225, 255)))
422 dc.SetPen(wx.Pen(wx.SystemSettings.GetColour(wx.SYS_COLOUR_ACTIVECAPTION)))
423 dc.DrawRectangle(posx, self._borderPenWidth, nImageWidth, nImageWidth)
424 dc.DrawBitmap(self._vButtons[ii].GetBitmap(), posx + self._nPadding, self._nPadding + self._borderPenWidth + 1, True)
425
426 else:
427
428 dc.DrawBitmap(self._vButtons[ii].GetBitmap(), posx + self._nPadding, self._nPadding + self._borderPenWidth, True)
429
430 self._vButtons[ii].SetRect(wx.Rect(posx, self._borderPenWidth, nImageWidth, nImageWidth))
431 posx -= nImageWidth
432
433 else:
434
435 rightStartX = textX - self._nPadding - nImageWidth
436 posx = self._nPadding
437
438 for ii in xrange(len(self._vButtons)):
439
440 # Make sure we can keep drawing
441 if posx > rightStartX:
442 break
443
444 # Draw a rectangle around the buttons
445 if self._vButtons[ii].GetStatus() == BP_BTN_HOVER:
446
447 dc.SetBrush(wx.Brush(wx.Color(225, 225, 255)))
448 dc.SetPen(wx.Pen(wx.SystemSettings.GetColour(wx.SYS_COLOUR_ACTIVECAPTION)))
449 dc.DrawRectangle(posx, self._borderPenWidth, nImageWidth, nImageWidth)
450 dc.DrawBitmap(self._vButtons[ii].GetBitmap(), posx + self._nPadding, self._nPadding + self._borderPenWidth, True)
451
452 elif self._vButtons[ii].GetStatus() == BP_BTN_PRESSED:
453
454 dc.SetBrush(wx.Brush(wx.Color(225, 225, 255)))
455 dc.SetPen(wx.Pen(wx.SystemSettings.GetColour(wx.SYS_COLOUR_ACTIVECAPTION)))
456 dc.DrawRectangle(posx, self._borderPenWidth, nImageWidth, nImageWidth)
457 dc.DrawBitmap(self._vButtons[ii].GetBitmap(), posx + self._nPadding, self._nPadding + self._borderPenWidth + 1, True)
458
459 else:
460
461 dc.DrawBitmap(self._vButtons[ii].GetBitmap(), posx + self._nPadding, self._nPadding + self._borderPenWidth, True)
462
463 self._vButtons[ii].SetRect(wx.Rect(posx, self._borderPenWidth, nImageWidth, nImageWidth))
464 posx += nImageWidth
465
466 # Update all other buttons that they are not drawn (by setting the rect to 0)
467 for cur in xrange(ii+1, len(self._vButtons)):
468 self._vButtons[cur].SetRect(wx.Rect(0, 0, 0, 0))
469
470
471 def OnEraseBackground(self, event):
472 """ Handles the wx.EVT_ERASE_BACKGROUND event for ButtonPanel (does nothing). """
473
474 pass
475
476
477 def OnSize(self, event):
478 """ Handles the wx.EVT_SIZE event for ButtonPanel. """
479
480 self.Refresh()
481 event.Skip()
482
483
484 def SetColor(self, switch, color):
485 """
486 Sets the color depending on switch:
487 - BP_CAPTION_COLOR: caption color;
488 - BP_CAPTION_GRADIENT_COLOR: gradient color;
489 - BP_CAPTION_BORDER_COLOR; border color;
490 - BP_TEXT_COLOR: text color.
491 """
492
493 if switch == BP_CAPTION_COLOR:
494 self._colorFrom = color
495 elif switch == BP_CAPTION_GRADIENT_COLOR:
496 self._colorTo = color
497 elif switch == BP_CAPTION_BORDER_COLOR:
498 self._colorBorder = color
499 elif switch == BP_TEXT_COLOR:
500 self._colorText = color
501
502
503 def FillGradientColor(self, dc, rect):
504 """ Gradient fill from colour 1 to colour 2 with top to bottom. """
505
506 if rect.height < 1 or rect.width < 1:
507 return
508
509 size = rect.height
510
511 # calculate gradient coefficients
512 style = self.GetParent().GetWindowStyleFlag()
513 col2 = self._colorFrom
514 col1 = self._colorTo
515
516 rf, gf, bf = 0, 0, 0
517 rstep = float((col2.Red() - col1.Red()))/float(size)
518 gstep = float((col2.Green() - col1.Green()))/float(size)
519 bstep = float((col2.Blue() - col1.Blue()))/float(size)
520
521 for y in xrange(rect.y, rect.y + size):
522
523 currCol = wx.Colour(col1.Red() + rf, col1.Green() + gf, col1.Blue() + bf)
524 dc.SetBrush(wx.Brush(currCol, wx.SOLID))
525 dc.SetPen(wx.Pen(currCol))
526 dc.DrawLine(rect.x, y, rect.x + rect.width, y)
527 rf += rstep
528 gf += gstep
529 bf += bstep
530
531
532 def OnMouseMove(self, event):
533 """ Handles the wx.EVT_MOTION event for ButtonPanel. """
534
535 # Check to see if we are hovering a button
536 for ii in xrange(len(self._vButtons)):
537 if self._vButtons[ii].GetRect().Inside(event.GetPosition()):
538 self._vButtons[ii].SetStatus(BP_BTN_HOVER)
539 else:
540 self._vButtons[ii].SetStatus(BP_BTN_NONE)
541
542 self.Refresh()
543 event.Skip()
544
545
546 def OnLeftDown(self, event):
547 """ Handles the wx.EVT_LEFT_DOWN event for ButtonPanel. """
548
549 tabId, hit = self.HitTest(event.GetPosition())
550
551 if hit == BP_HT_BUTTON:
552
553 self._vButtons[tabId].SetStatus(BP_BTN_PRESSED)
554 self.Refresh()
555
556
557 def OnLeftUp(self, event):
558 """ Handles the wx.EVT_LEFT_UP event for ButtonPanel. """
559
560 tabId, hit = self.HitTest(event.GetPosition())
561
562 if hit == BP_HT_BUTTON:
563 if self._vButtons[tabId].GetStatus() == BP_BTN_PRESSED:
564 # Fire a button click event
565 btnEvent = wx.CommandEvent(wx.wxEVT_COMMAND_BUTTON_CLICKED, self._vButtons[tabId].GetId())
566 self.GetEventHandler().ProcessEvent(btnEvent)
567
568 # Update the button status to be hovered
569 self._vButtons[tabId].SetStatus(BP_BTN_HOVER)
570 self.Refresh()
571
572
573 def OnMouseLeave(self, event):
574 """ Handles the wx.EVT_LEAVE_WINDOW event for ButtonPanel. """
575
576 # Reset all buttons statuses
577 for ii in xrange(len(self._vButtons)):
578 self._vButtons[ii].SetStatus(BP_BTN_NONE)
579
580 self.Refresh()
581 event.Skip()
582
583
584 def OnMouseEnterWindow(self, event):
585 """ Handles the wx.EVT_ENTER_WINDOW event for ButtonPanel. """
586
587 event.Skip()
588
589
590 def HitTest(self, pt):
591 """
592 HitTest method for ButtonPanel. Returns the button (if any) and
593 a flag (if any).
594 """
595
596 btnIdx = -1
597
598 for ii in xrange(len(self._vButtons)):
599 if self._vButtons[ii].GetRect().Inside(pt):
600 return ii, BP_HT_BUTTON
601
602 return -1, BP_HT_NONE
603
604
605