Adding image to GeDialog - Python Plugin/Script
-
Is it possible to add image to the dialog/window from python plugin/script?
The closest thing I find is Bitmap button, but you can only use pre-defined icons: https://developers.maxon.net/forum/topic/15365/gui-bitmap-button-properties/4 -
Hi,
You can use BitmapButtonCustomGui.SetImage for a bitmap button, you can Init a bitmap with disk file, or use c4d.bitmaps.InitResourceBitmap for icons witch registed with a uniqe id.
Cheers~
DunHou -
@Dunhou Thanks a lot! I checked a bit more after that and I also found out that you are indeed able to put custom image, using
GeUserArea
:
https://developers.maxon.net/forum/topic/7155/8153_bitmap-in-userarea/22?_=1721638654806Btw, checked out your plugins, and it made me realize I can do a lot more on the GUI side using C4D Python API.
-
Hello @ezeuz,
Welcome to the Maxon developers forum and its community, it is great to have you with us!
Getting Started
Before creating your next postings, we would recommend making yourself accustomed with our forum and support procedures. You did not do anything wrong, we point all new users to these rules.
- Forum Overview: Provides a broad overview of the fundamental structure and rules of this forum, such as the purpose of the different sub-forums or the fact that we will ban users who engage in hate speech or harassment.
- Support Procedures: Provides a more in detail overview of how we provide technical support for APIs here. This topic will tell you how to ask good questions and limits of our technical support.
- Forum Features: Provides an overview of the technical features of this forum, such as Markdown markup or file uploads.
It is strongly recommended to read the first two topics carefully, especially the section Support Procedures: Asking Questions.
About your First Question
The answer to your question depends a bit on the context, what you want to do. One solution can be to use a bitmap button as pointed out by yourself and @Dunhou. And you can indeed set there an image which is not a resource as pointed out by @Dunhou.
The broader answer is that we never needed internally an image gadget, which is why there is none. Or viewed from the opposite side: When we need an image-like gadget, we tend to write specialized UIs like a bitmap button, a shader view or a image file view. The good news is that you can quite easily implement your own UI with GeUserArea, including an image control.
Cheers,
FerdinandResult
Drawing an image using a bitmap button and user area side by side. While we can disable button behaviour like mouse over fading, we cannot truly control the scaling behaviour of the image in such button. With the bitmap button settings we can force a specific size which is smaller or larger than the original size, but we cannot make the image scale with the button or control its alignment within the button. In a
GeUserArea
we can control all that with very little code.
"""Demonstrates how to display an image in a dialog using both a bitmap button and a custom image gadget. """ import c4d import itertools from mxutils import CheckType class ImageArea(c4d.gui.GeUserArea): """Realizes a custom image gadget that displays a bitmap. This would have to be customized to do exactly what you want. Here I just realized an image gadget that both scales up and down the image to fit the user area. WHich is different from what the bitmap button does. """ MIN_SIZE_DEFAULT: tuple[int, int] = (50, 50) def __init__(self, bmp: c4d.bitmaps.BaseBitmap, label: str): """Initializes the image gadget with the given bitmap and label. """ self._bitmap: c4d.bitmaps.BaseBitmap = CheckType(bmp, c4d.bitmaps.BaseBitmap) self._label: str = CheckType(label, str) def DrawMsg(self, x1: int, y1: int, x2: int, y2: int, msg: c4d.BaseContainer) -> None: """Called by Cinema 4D to draw the content of the user area. """ bmp: c4d.bitmaps.BaseBitmap = self._bitmap self.DrawBitmap(bmp, # The image to draw. x1, y1, x2, y2, # The area in the user area to draw the image. 0, 0, bmp.GetBw(), bmp.GetBh(), # The area in the image to draw. mode=c4d.BMP_NORMAL) # The drawing flags, we signal here just fast scaling. # Draw a label on top of the image. textHeight: int = self.DrawGetFontHeight() self.DrawSetTextCol(fg=c4d.Vector(1), bg=c4d.Vector(0.25)) self.DrawText(self._label, x1 + 5, y2 - textHeight - 5, flags=c4d.DRAWTEXT_HALIGN_LEFT) def MinSize(self, msg: c4d.BaseContainer) -> c4d.Vector: """Called by Cinema 4D to determine the minimum size of the user area. """ # Here we could also implement that the user area cannot scale below the size of the image # it wraps. return c4d.Vector(*self.MIN_SIZE_DEFAULT, 0) class ImageDialog(c4d.gui.GeDialog): """Realizes a dialog that displays an image using both a bitmap button and a custom image gadget.""" # The IDs of the dialog elements. ID_BITMAP_BUTTON: int = 1000 ID_IMAGE_AREA: int = 1001 def __init__(self): """Initializes the dialog's member variables. """ self._imageArea: ImageArea | None = None @staticmethod def GetExampleImage(height: int, width: int) -> c4d.bitmaps.BaseBitmap: """Renders a gradient into a bitmap of the given #height and #width. """ bitmap: c4d.bitmaps.BaseBitmap = CheckType(c4d.bitmaps.BaseBitmap()) if not bitmap.Init(width, height) == c4d.IMAGERESULT_OK: raise RuntimeError("Failed to initialize the bitmap.") for y, x in itertools.product(range(height), range(width)): bitmap.SetPixel(x, y, int(x / width * 254.99), int(y / height * 254.99), 255) return bitmap def CreateLayout(self): """Called by Cinema 4D to let the user populate the dialog with GUI elements. """ # Set the title of the dialog, the margin of the implicitly defined outmost group ("border # space"), and the padding between elements ("space") in that outmost group. self.SetTitle("Image Dialog") self.GroupBorderSpace(10, 10, 10, 10) self.GroupSpace(5, 5) bmp: c4d.bitmaps.BaseBitmap = self.GetExampleImage(128, 256) # Add a bitmap button to display the image. It is important here to either disable fading # in the button or not to use SCALEFIT as a layout flag for the button, as the button # background not covered by the bitmap will otherwise be tinted when hovered. What we cannot # influence very well is how the image is scaled. We can enforce a specific scaling size but # we cannot dynamically fit the image to the button size, as that is not the purpose of a # bitmap button. bc: c4d.BaseContainer = c4d.BaseContainer() bc[c4d.BITMAPBUTTON_DISABLE_FADING] = True gui: c4d.gui.BitmapButtonCustomGui = CheckType(self.AddCustomGui( self.ID_BITMAP_BUTTON, c4d.CUSTOMGUI_BITMAPBUTTON, "", c4d.BFH_SCALEFIT | c4d.BFV_TOP, 0, 0, bc), c4d.gui.BitmapButtonCustomGui) gui.SetImage(bmp) # Add a custom image gadget to display the image. Here we can do pretty much what we want # without any restrictions. We can scale the image to fit the user area, or super impose # text as I did here. It is important to make sure that the user area is not destroyed # by storing it in the dialog instance as I did here with self._imageArea. self._imageArea: ImageArea = ImageArea(bmp, "Example Image") self.AddUserArea(self.ID_IMAGE_AREA, c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT, *ImageArea.MIN_SIZE_DEFAULT) self.AttachUserArea(self._imageArea, self.ID_IMAGE_AREA) return True if __name__ == "__main__": dlg: ImageDialog = ImageDialog() dlg.Open(c4d.DLG_TYPE_MODAL_RESIZEABLE, defaultw=400, defaulth=400)
-
@ferdinand Thanks a lot for the reply (and all of your example codes throughout the years)! Yeah I managed to make one using UserArea, I def think this is a crucial feature to be added in the sample code in GitHub (tbh a ton of your codes are just too useful to be left alone in forums). Especially since it's a multi-step process unlike PySide (used by Maya plugin), for example.
-
@ferdinand just a quick question, how can we draw the _label text perfect above the image with alpha but not with a bg color?
-
Hey @Dunhou,
I do not think that this is possible. At least not in a sane way, as there is no way to control the opacity of the text drawing background color alone. What you probably could do is:
- Scale your image to draw down to the size of your
GeUserArea
drawing region, using something likeBaseBitmap.ScaleBicubic
. - Now init a
GeClipMap
canvas with that bitmap. - Draw the text.
- Finalize and get the bitmap for the canvas and draw that result into your
GeUserArea
.
Which is a bit "ehh" performance-wise (could be mitigated by caching the underlying
GeClipMap
result) but will then also force you to do really everything in theGeClipMap
, loosing access to things likeGeUserArea.DrawCustomButton
.Cheers,
Ferdinand - Scale your image to draw down to the size of your
-
@ferdinand fine for that, like I think, this is not perfect solution for this, thanks for your confirm!
Cheers~
DunHou