Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
Exemple of GeUserArea who imitate browser
# coding: utf-8
A GeUserArea Exemple for display multiple content.
Hardly based on from Niklas
__author__ = 'Adam Maxime - Graphos <gr4ph0s(at)>'
__version__ = '1.0'
import c4d
import collections
PLUGIN_ID = 1039317 #PoseMaster
Coord = collections.namedtuple('Coord', 'x y')
class UiPose(object):
This class represents a pose in the GeUserArea.
.. attribute:: id
The id of the pose in the DB
.. attribute:: bmp
c4d.bitmaps.BaseBitmap of the pose
.. attribute:: name
The name of the pose. Can be shrinked in the display function according the size of a miniature
.. attribute:: selected
True if the pose is actually selected on the GeUserArea
__slots__ = ('id', 'bmp', 'name', 'selected')
def __init__(self, id, bmp, name):
super(UiPose, self).__init__() = id
self.bmp = bmp = name
self.selected = False
def __repr__(self):
return str(
def setId(self, id): = id
def setBmp(self, bmp):
self.bmp = bmp
def select(self):
self.selected = True
def toggle_select(self):
self.selected = not bool(self.selected)
def deselect(self):
self.selected = False
class UiPoseList(object):
This class hold all :class: UiPose
def __init__(self):
super(UiPoseList, self).__init__()
self.all_poses = list()
#Adding pose for exemple usage only !
self.add_pose(0, None, "myName")
self.add_pose(1, None, "myNfdfdsfdame")
self.add_pose(2, None, "myName")
self.add_pose(3, None, "myName")
def reset(self):
self.all_poses = list()
def add_pose(self, pose_id, pose_bmp, pose_name):
Add a UiPose in the list all_poses.
Returns True if the pose were created
self.all_poses.append(UiPose(pose_id, pose_bmp, pose_name))
return True
def select_pose(self, pose_id):
if self.get_number_of_poses() <= pose_id:
return False
def deselect_pose(self, pose_id):
if self.get_number_of_poses() <= pose_id:
return False
def toggle_select_pose(self, pose_id):
if self.get_number_of_poses() <= pose_id:
return False
def deselect_all(self):
for pose in self.iter_poses():
def get_number_of_poses(self):
return len(self.all_poses)
def iter_poses(self):
iter_poses() -> iterator of UiPose
Use this function to iterate over all poses
for pose in self.all_poses:
yield pose
# Cinema 4D GUI
class PoseLibrary_View(c4d.gui.GeUserArea):
This class implements the visual representation of PoseList
def __init__(self, ui_pose_list, tile_size=100, tile_space=8,):
super(PoseLibrary_View, self).__init__()
self.pose_list = ui_pose_list
self.tile_space = tile_space
#only used for speed performance in draw_pose
self.total_tile_width = 0
self.max_x_row = 0
def set_tile_size(self, tile_size):
self.tile_width = tile_size
self.tile_height = tile_size * 1.25
def get_color_vector(self, color_id):
get_color_vector(color_id) -> c4d.Vector
Returns a color :class:`c4d.Vector` for *color_id*. The
existing :meth:`GetColorRGB` method returns a dictionary
with RGB values in range [0,255] which is rather inconvenient.
data = self.GetColorRGB(color_id)
rgbv = c4d.Vector(data['r'], data['g'], data['b'])
return rgbv ^ c4d.Vector(1.0 / 255.0)
def calc_pose_pos(self, index):
Helper function to compute the pixel offset of a miniature.
:param index: integer representing the order of the miniature
:return: Coord obj egual to the left top corner of a miniature
if not self.max_x_row:
return Coord(0, 0)
x_row = index % self.max_x_row
y_row = index // self.max_x_row
x = (self.tile_space // 2) + ((x_row * self.tile_width) + (x_row * self.tile_space))
y = (self.tile_space // 2) + ((y_row * self.tile_height) + (y_row * self.tile_space))
return Coord(x, y)
def draw_pose(self, pose_id, pose_name, pose_bmp, pose_selected):
Helper function to draw a pose.
:param pose_id: integer
the pose_id in the grid !
Not equivalent to wich is the db one
:param pose_name: string
The actual pose name
:param pose_bmp: c4d.bitmaps.BaseBitmap
The bitmap to be used.
:param pose_selected: Bool
The state of the selection of the pose
# Calculate the tile's position on the User Area for both
# components and convert it to a Coord object immediately.
pos = self.calc_pose_pos(pose_id)
size = Coord(self.tile_width, self.tile_height)
#Draw the top rectangle // Will be BaseBitmpap after
color = self.get_color_vector(c4d.COLOR_BGFOCUS)
self.DrawRectangle(pos.x, pos.y, pos.x + size.x, pos.y + size.y * 0.75)
#write pose id
self.DrawSetTextCol(c4d.COLOR_TEXT, c4d.COLOR_TRANS)
str(pose_id), pos.x + size.x / 2, pos.y + (size.y * 0.75) / 2, flags)
#Draw the bottom rectangle
color = self.get_color_vector(c4d.COLOR_BGEDIT)
self.DrawRectangle(pos.x, pos.y + size.y * 0.75, pos.x + size.x, pos.y + size.y)
#Draw the name of the pose
if pose_selected:
self.DrawSetTextCol(c4d.COLOR_TEXTFOCUS, c4d.COLOR_TRANS)
self.DrawSetTextCol(c4d.COLOR_TEXT, c4d.COLOR_TRANS)
#reduce the string for matching our block
while pose_name:
if self.DrawGetTextWidth(pose_name) > self.tile_width:
pose_name = pose_name[:-1]
str(pose_name), pos.x + size.x / 2, pos.y + size.y * 0.875 , flags)
#Draw outline if selected
if pose_selected:
self.DrawBorder(c4d.BORDER_ACTIVE_4, pos.x, pos.y, pos.x + size.x, pos.y + size.y)
def DrawMsg(self, x1, y1, x2, y2, msg):
This method is called to render the content of the view.
# Enables double buffering to avoid flickering.
# Draw the background.
self.DrawRectangle(x1, y1, x2, y2)
# Set the text font, we only need to do this once.
#set data for drawing
self.total_tile_width = self.tile_width + self.tile_space
self.max_x_row = self.GetWidth() // self.total_tile_width
# Draw all the poses.
for tile in self.pose_list.iter_poses():
self.draw_pose(,, tile.bmp, tile.selected)
def calc_pose_id_by_coord(self, coord):
Retrieve the actual UiPose id from a coord in UiPoseList.all_poses
:param coord: the coord in the GeUserArea
:return: int. Calc_pos_id can retrieve a pose_id which is not into the list ALWAYS CHECK!
x_row = coord.x // self.total_tile_width
y_row = coord.y // self.tile_height
return int(x_row + y_row * self.max_x_row)
def get_ctrl_shift_alt(self, msg):
Get if ctrl / shift or alt is pressed
bc_keyboard = c4d.BaseContainer()
ctrl = False
shift = False
alt = False
self.GetInputState(c4d.BFM_INPUT_KEYBOARD, c4d.BFM_INPUT_CHANNEL, bc_keyboard)
if bc_keyboard[c4d.BFM_INPUT_QUALIFIER] & c4d.QCTRL:
ctrl = True
if bc_keyboard[c4d.BFM_INPUT_QUALIFIER] & c4d.QSHIFT:
shift = True
if bc_keyboard[c4d.BFM_INPUT_QUALIFIER] & c4d.QALT:
alt = True
return ctrl, shift, alt
def get_coord_clicked(self, msg):
Get where user click in our GeUserArea
bc_click = c4d.BaseContainer()
self.GetInputState(c4d.BFM_INPUT_MOUSE, c4d.BFM_INPUT_MOUSELEFT, bc_click)
# Get position clicked
base = self.Local2Global()
coord = Coord(bc_click.GetLong(c4d.BFM_INPUT_X) - base['x'],
bc_click.GetLong(c4d.BFM_INPUT_Y) - base['y'])
return coord
def Message(self, msg, result):
if msg.GetId() == c4d.BFM_INPUT:
#Get state of ctrl / shift / alt
ctrl, shift, alt = self.get_ctrl_shift_alt(msg)
#Get position clicked and convert it into pose_id
pose_id_click = self.calc_pose_id_by_coord(self.get_coord_clicked(msg))
#Click into a pose
if self.pose_list.get_number_of_poses() - 1 >= pose_id_click:
#if only shift is pressed
if shift and not ctrl and not alt:
#if only ctrl is pressed
elif ctrl and not shift and not alt:
#click normal
#redraw change
#Click elsewhere on the GeUserArea
return super(PoseLibrary_View, self).Message(msg, result)
class MainWindows(c4d.gui.GeDialog):
This dialog contains the :class:`PoseLibrary_View` class and displays
it in its very own window.
# Symbolic IDs for parameters in the dialog.
ID_SCORE = 1000
ID_SLIDER = 1002
def __init__(self):
super(MainWindows, self).__init__()
self.poses_list = UiPoseList()
self.view = PoseLibrary_View(self.poses_list)
def Command(self, id, data):
if id == self.ID_SLIDER:
self.view.set_tile_size(int(100* self.GetFloat(self.ID_SLIDER)))
return True
def CreateLayout(self):
self.SetTitle("Pose Master")
# Add the field to display the score in the menu line of
# the dialog.
self.AddStaticText(self.ID_SCORE, 0)
# Add and attach the TFE_View to the main dialog area.
self.AttachUserArea(self.view, self.ID_POSELIBRARY)
self.AddSlider(self.ID_SLIDER, c4d.BFH_SCALEFIT | c4d.BFV_BOTTOM, 10)
self.SetFloat(self.ID_SLIDER, 1, 0.5, 3, 0.001)
return True
class CommandLuncher(c4d.plugins.CommandData):
This plugin class opens the MainWindows when it is
selected from the Plugin's menu.
def dialog(self):
Returns the MainWindows that is managed by this command
plugin. The dialog is generated on-demand to avoid creating it
when it would never be opened by the user.
dialog = getattr(self, '_dialog', None)
if dialog is None:
dialog = MainWindows()
self._dialog = dialog
return dialog
def register(self):
Registers the plugin command to Cinema 4D.
flags = 0
icon = None
help_ = "Pose Library for Cinema 4D"
return c4d.plugins.RegisterCommandPlugin(
PLUGIN_ID, "Pose Master", flags, icon, help_, self)
def Execute(self, doc):
return self.dialog.Open(c4d.DLG_TYPE_ASYNC, PLUGIN_ID, defaultw=100, defaulth=100)
if __name__ == "__main__":
# Register the Plugin Command.
print "Pose Master registered. Visit"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.