C4D插件:Py4D Icon Button UI Give Away
What’s it?
Below you can find the source code for a user-area that implements a button-like interface, but additionally displays an icon on the left or right side. This icon can be a simple color or a bitmap.
IconButton UA Example
Source Code
The following is the source code for the above example. You can copy&paste the IconButton class and the AddIconButton function into your source code. As of version 1.2, the code is released under the WTFPL license, so do what the fuck you want with it!
- # Copyright (C) 2013, Niklas Rosenstein
- # http://niklasrosenstein.de/
- #
- # This work is free. You can redistribute it and/or modify it under the
- # terms of the Do What The Fuck You Want To Public License, Version 2,
- # as published by Sam Hocevar. See http://www.wtfpl.net/ for more details.
- #
- # Changelog:
- #
- # 1.1: Corrected action msg for Command() method, information like qualifiers
- # and other input event information should now be correctly received in
- # the Command() method.
- # 1.2: Changed license to WTFPL, do what the fuck you want with it!
- # 1.3: Thanks to Jet Kawa to tell me about the flickering when holding and
- # dragging. As he suggested correctly, OffScreenOn() fixes this.
- # 1.4: Added mouse hover feature
- # 1.5: Fixed drawing algorithm to correctly update only parts of the user
- # area. The x1, y1, x2, y2 only reflect the part of the user area
- # that is being redrawn, not the actual dimensions of the area. Since
- # these were used to caclculate width and height of the area, rendering
- # issues occured when only parts of the user area were updated (eg.
- # in a scroll group).
- # The standard button has been renamed to "Close" and closes the dialog
- # when clicked.
-
- import time
- import c4d
- from c4d.gui import GeUserArea, GeDialog
-
- class IconButton(GeUserArea):
-
- VERSION = (1, 4)
-
- M_NOICON = 0
- M_ICONLEFT = 1
- M_ICONRIGHT = 2
- M_FULL = 3
-
- C_TEXT = c4d.COLOR_TEXT
- C_BG = c4d.COLOR_BGEDIT
- C_HIGHLIGHT = c4d.COLOR_BGFOCUS
- C_BGPRESS = c4d.COLOR_BG
-
- S_ICON = 24
- S_PADH = 4
- S_PADV = 4
-
- def __init__(self, paramid, text, icon, mode=M_ICONLEFT):
- super(IconButton, self).__init__()
- self.paramid = paramid
- self.text = text
- self.icon = icon
- self.mode = mode
- self.pressed = False
-
- self.last_t = -1
- self.mouse_in = False
- self.interval = 0.2
-
- def _CalcLayout(self):
- text_x = self.S_PADH
- text_w = self.DrawGetTextWidth(str(self.text))
- text_h = self.DrawGetFontHeight()
- icon_x = self.S_PADH
-
- width = text_w + self.S_PADH * 2
- height = max([text_h, self.S_ICON]) + self.S_PADV * 2
-
- draw_icon = True
- if self.mode == self.M_ICONLEFT:
- icon_x = self.S_PADH
- text_x = self.S_PADH + self.S_ICON + self.S_PADH
- width += self.S_ICON + self.S_PADH
- elif self.mode == self.M_ICONRIGHT:
- icon_x = self.GetWidth() - (self.S_PADH + self.S_ICON)
- text_x = self.S_PADH
- width += self.S_ICON + self.S_PADH
- else:
- draw_icon = False
-
- return locals()
-
- def _DrawIcon(self, icon, x1, y1, x2, y2):
- # Determine if the icon is a simple color.
- if not icon:
- pass
- if isinstance(icon, (int, c4d.Vector)):
- self.DrawSetPen(icon)
- self.DrawRectangle(x1, y1, x2, y2)
- # or if it is a bitmap icon.
- elif isinstance(icon, c4d.bitmaps.BaseBitmap):
- self.DrawBitmap(icon, x1, y1, (x2 - x1), (y2 - y1),
- 0, 0, icon.GetBw(), icon.GetBh(), c4d.BMP_ALLOWALPHA)
- else:
- return False
-
- return True
-
- def _GetHighlight(self):
- delta = time.time() - self.last_t
- return delta / self.interval
-
- def _GetColor(self, v):
- if isinstance(v, c4d.Vector):
- return v
- elif isinstance(v, int):
- d = self.GetColorRGB(v)
- return c4d.Vector(d['r'], d['g'], d['b']) ^ c4d.Vector(1.0 / 255)
- else:
- raise TypeError('Unexpected value of type %s' % v.__class__.__name__)
-
- def _InterpolateColors(self, x, a, b):
- if x < 0: x = 0.0
- elif x > 1.0: x = 1.0
-
- a = self._GetColor(a)
- b = self._GetColor(b)
- return a * x + b * (1 - x)
-
- # GeUserArea Overrides
-
- def DrawMsg(self, x1, y1, x2, y2, msg):
- self.OffScreenOn() # Double buffering
-
- # Draw the background color.
- bgcolor = self.C_BG
- if self.pressed:
- bgcolor = self.C_BGPRESS
- elif self.mode == self.M_FULL and self.icon:
- bgcolor = self.icon
- else:
- h = self._GetHighlight()
- ca, cb = self.C_HIGHLIGHT, self.C_BG
- if not self.mouse_in:
- ca, cb = cb, ca
-
- # Interpolate between these two colors.
- bgcolor = self._InterpolateColors(h, ca, cb)
-
- w, h = self.GetWidth(), self.GetHeight()
- self._DrawIcon(bgcolor, 0, 0, w, h)
-
- # Determine the drawing position and size of the
- # colored icon and the text position.
- layout = self._CalcLayout()
-
- if layout['draw_icon']:
- x = layout['icon_x']
- y = min([h / 2 - self.S_ICON / 2, self.S_PADV])
-
- # Determine if the icon_DrawIcon
- self._DrawIcon(self.icon, x, y, x + self.S_ICON, y + self.S_ICON)
-
- if 'draw_text':
- self.DrawSetTextCol(self.C_TEXT, c4d.COLOR_TRANS)
- x = layout['text_x']
- y = max([h / 2 - layout['text_h'] / 2, self.S_PADV])
- self.DrawText(str(self.text), x, y)
-
- def GetMinSize(self):
- layout = self._CalcLayout()
- return layout['width'], layout['height']
-
- def InputEvent(self, msg):
- device = msg.GetLong(c4d.BFM_INPUT_DEVICE)
- channel = msg.GetLong(c4d.BFM_INPUT_CHANNEL)
-
- catched = False
- if device == c4d.BFM_INPUT_MOUSE and channel == c4d.BFM_INPUT_MOUSELEFT:
- self.pressed = True
- catched = True
-
- # Poll the event.
- tlast = time.time()
- while self.GetInputState(device, channel, msg):
- if not msg.GetLong(c4d.BFM_INPUT_VALUE): break
-
- x, y = msg.GetLong(c4d.BFM_INPUT_X), msg.GetLong(c4d.BFM_INPUT_Y)
- map_ = self.Global2Local()
- x += map_['x']
- y += map_['y']
-
- if x < 0 or y < 0 or x >= self.GetWidth() or y >= self.GetHeight():
- self.pressed = False
- else:
- self.pressed = True
-
- # Do not redraw all the time, this would be useless.
- tdelta = time.time() - tlast
- if tdelta > (1.0 / 30): # 30 FPS
- tlast = time.time()
- self.Redraw()
-
- if self.pressed:
- # Invoke the dialogs Command() method.
- actionmsg = c4d.BaseContainer(msg)
- actionmsg.SetId(c4d.BFM_ACTION)
- actionmsg.SetLong(c4d.BFM_ACTION_ID, self.paramid)
- self.SendParentMessage(actionmsg)
-
- self.pressed = False
- self.Redraw()
-
- return catched
-
- def Message(self, msg, result):
- if msg.GetId() == c4d.BFM_GETCURSORINFO:
- if not self.mouse_in:
- self.mouse_in = True
- self.last_t = time.time()
- self.SetTimer(30)
- self.Redraw()
- return super(IconButton, self).Message(msg, result)
-
- def Timer(self, msg):
- self.GetInputState(c4d.BFM_INPUT_MOUSE, c4d.BFM_INPUT_MOUSELEFT, msg)
- g2l = self.Global2Local()
- x = msg[c4d.BFM_INPUT_X] + g2l['x']
- y = msg[c4d.BFM_INPUT_Y] + g2l['y']
-
- # Check if the mouse is still inside the user area or not.
- if x < 0 or y < 0 or x >= self.GetWidth() or y >= self.GetHeight():
- if self.mouse_in:
- self.mouse_in = False
- self.last_t = time.time()
-
- h = self._GetHighlight()
- if h < 1.0:
- self.Redraw()
- elif not self.mouse_in:
- self.Redraw()
- self.SetTimer(0)
-
-
- def AddIconButton(dialog, paramid, text, icon, mode=IconButton.M_ICONLEFT,
- auto_store=True):
- r"""
- Creates an Icon Button on the passed dialog.
- """
-
- ua = IconButton(paramid, text, icon, mode)
-
- if auto_store:
- if not hasattr(dialog, '_icon_buttons'):
- dialog._icon_buttons = []
- dialog._icon_buttons.append(ua)
-
- dialog.AddUserArea(paramid, c4d.BFH_SCALEFIT)
- dialog.AttachUserArea(ua, paramid)
- return ua
-
-
- # Example
- # =======
-
- class Dialog(GeDialog):
-
- def __init__(self):
- super(Dialog, self).__init__()
-
- # GeDialog Overrides
-
- def CreateLayout(self):
- self.SetTitle("Dialog")
- self.ScrollGroupBegin(9000, c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT, c4d.SCROLLGROUP_HORIZ | c4d.SCROLLGROUP_VERT, 0, 0)
- self.GroupBegin(0, c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT, 0, 0)
-
- # Create a small example text + number field.
- self.GroupBegin(1000, c4d.BFH_SCALEFIT, 0, 1, "", 0, 0)
- self.AddStaticText(1001, 0, name="Value")
- self.AddEditNumberArrows(1002, 0)
- self.GroupEnd()
-
- # Create six different IconButton's
- self.GroupBegin(2000, c4d.BFH_SCALEFIT, 3, 0, "", 0, 0)
-
- # With an icon.
- path = 'C:/Users/niklas/Desktop/foo.png'
- bmp = c4d.bitmaps.BaseBitmap()
- bmp.InitWith(path)
- AddIconButton(self, 2001, "Image Icon Left", bmp, IconButton.M_ICONLEFT)
- AddIconButton(self, 2002, "Image Icon Right", bmp, IconButton.M_ICONRIGHT)
- AddIconButton(self, 2003, "No Icon Button", None, IconButton.M_NOICON)
-
- # With a color.
- color = c4d.Vector(0.2, 0.5, 0.3)
- AddIconButton(self, 2004, "Color Left", color, IconButton.M_ICONLEFT)
- AddIconButton(self, 2005, "Color Right", color, IconButton.M_ICONRIGHT)
- AddIconButton(self, 2006, "Color Full", c4d.COLOR_TEXTFOCUS, IconButton.M_FULL)
- self.GroupEnd()
-
- # Create a button at the bottom of the dialog-
- self.GroupBegin(3000, c4d.BFH_SCALEFIT, 0, 1, "", 0, 0)
- self.AddStaticText(3001, c4d.BFH_SCALEFIT, name="") # Filler
- self.AddButton(3002, c4d.BFH_RIGHT, name="Close")
- self.GroupEnd()
-
- self.GroupEnd()
- self.GroupEnd() # Scroll Group
- return True
-
- def Command(self, paramid, msg):
- if paramid == 3002:
- self.Close()
- print "Button with ID", paramid, "pressed."
- return True
-
- dlg = Dialog()
- dlg.Open(c4d.DLG_TYPE_MODAL_RESIZEABLE, defaultw=180)
复制代码 The buttons act just like normal Cinema 4D buttons. If the user presses the button, you will receive a call to your GeDialog.Command() method with the button’s ID. The IconButton interface supports three different types of icons and four different display modes.
Icon types
The icon is passed as the foruth parameter to AddIconButton()
Color constant (eg. c4d.COLOR_TEXTFOCUS)
Vector color (eg. c4d.Vector(1.0, 0.66, 0.12'))
BaseBitmap instance
Display modes
The display mode is passed as the fifth argument to AddIconButton(). The default value is IconButton.M_ICONLEFT.
IconButton.M_NOICON: Display no icon at all, just the text
IconButton.M_ICONLEFT: Display the icon on the left-hand side
IconButton.M_ICONLEFT: Display the icon on the right-hand side
IconButton.M_FULL: Scale the icon to the full size of the button.
- def get_some_basebitmap():
- bmp = c4d.bitmaps.BaseBitmap()
- bmp.InitWith('C:/Users/niklas/Desktop/foo.png')
- return bmp
-
- class Dialog(GeDialog):
-
- def CreateLayout(self):
- AddIconButton(self, 1000, "Button One", c4d.COLOR_SYNTAX_STRING)
- AddIconButton(self, 1001, "Button Two", c4d.Vector(1.0, 0.66, 0.12), IconButton.M_ICONRIGHT)
- AddIconButton(self, 1002, "Button Three", get_some_basebitmap(), IconButton.M_ICONRIGHT)
- AddIconButton(self, 1003, "Button Four", None, IconButton.M_NOICON)
- return True
-
- def Command(self, paramid, msg):
- if paramid == 1000:
- print "Button One pressed."
- # ...
- return True
复制代码 Adjustments
You can do any adjustments to the code you like to fit your personal needs. For example, if you want the icon to be of another size, just modify the IconButton.S_ICON value either directly on the IconButton class or on an instance.
|
|
Comment :3