Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware API
      • ZBrush GoZ API
      • Code Examples on Github
    • Forum
    • Downloads
    • Support
      • Support Procedures
      • Registered Developer Program
      • Plugin IDs
      • Contact Us
    • Unread
    • Recent
    • Tags
    • Users
    • Login

    Adding image to GeDialog - Python Plugin/Script

    Cinema 4D SDK
    python
    3
    8
    970
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • E
      ezeuz
      last edited by

      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

      1 Reply Last reply Reply Quote 0
      • DunhouD
        Dunhou
        last edited by

        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

        https://boghma.com
        https://github.com/DunHouGo

        1 Reply Last reply Reply Quote 1
        • E
          ezeuz
          last edited by ezeuz

          @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?_=1721638654806

          Btw, checked out your plugins, and it made me realize I can do a lot more on the GUI side using C4D Python API.

          1 Reply Last reply Reply Quote 0
          • ferdinandF
            ferdinand
            last edited by ferdinand

            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,
            Ferdinand

            Result

            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.
            046a66e3-010e-43c0-8ae5-0d9e30f294d6-image.png

            """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)
            

            MAXON SDK Specialist
            developers.maxon.net

            DunhouD 1 Reply Last reply Reply Quote 2
            • E
              ezeuz
              last edited by

              @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.

              1 Reply Last reply Reply Quote 0
              • DunhouD
                Dunhou @ferdinand
                last edited by

                @ferdinand just a quick question, how can we draw the _label text perfect above the image with alpha but not with a bg color?

                https://boghma.com
                https://github.com/DunHouGo

                ferdinandF 1 Reply Last reply Reply Quote 0
                • ferdinandF
                  ferdinand @Dunhou
                  last edited by ferdinand

                  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:

                  1. Scale your image to draw down to the size of your GeUserArea drawing region, using something like BaseBitmap.ScaleBicubic.
                  2. Now init a GeClipMap canvas with that bitmap.
                  3. Draw the text.
                  4. 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 the GeClipMap, loosing access to things like GeUserArea.DrawCustomButton.

                  Cheers,
                  Ferdinand

                  MAXON SDK Specialist
                  developers.maxon.net

                  DunhouD 1 Reply Last reply Reply Quote 0
                  • DunhouD
                    Dunhou @ferdinand
                    last edited by

                    @ferdinand fine for that, like I think, this is not perfect solution for this, thanks for your confirm!

                    Cheers~
                    DunHou

                    https://boghma.com
                    https://github.com/DunHouGo

                    1 Reply Last reply Reply Quote 0
                    • First post
                      Last post