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
    • Categories
      • Overview
      • News & Information
      • Cinema 4D SDK Support
      • Cineware SDK Support
      • ZBrush 4D SDK Support
      • Bugs
      • General Talk
    • Unread
    • Recent
    • Tags
    • Users
    • Login

    Scaling a GeUserArea in a ScrollGroup

    Cinema 4D SDK
    python windows r23
    3
    5
    880
    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.
    • ?
      A Former User
      last edited by A Former User

      Hello!
      I am drawing a BaseBitmap in a GeUserArea that is in a scroll group using the workaround @m_adam employed in this post:
      Redrawing GeUserArea in ScrollGroup with Slider Input

      The GeUserArea scales properly with this workaround when not it's smaller than the scrollgroup, but I'm unable to get it to stay in the center of the scrollgroup as it becomes larger. The GeUserArea shifts to the top left, which is undesired.

      GeUserArea

      Code:

      import c4d, random
      from c4d import gui
      
      GADGET_ID_GEUSERAREA = 10000
      SCROLL_ID = 10001
      SLIDER_ID = 10002
      
      class ExampleGeUserArea(c4d.gui.GeUserArea):
          width = 400
          height = 500
          bmp_cache = None
      
          def GetBitmap(self):
              bmp = c4d.bitmaps.BaseBitmap()
              bmp.Init(self.width,self.height)
              for h in range(self.height):
                  for w in range(self.width):
                      r = random.randint(0, 70)
                      bmp.SetPixel(w, h, r, r, r)
              return bmp
      
          def DrawMsg(self, x1, y1, x2, y2, msg):
              print("DrawMsg: x1: %d, y1: %d, x2: %d, y2: %s"%(x1,y1,x2,y2))
              self.OffScreenOn()
              self.SetClippingRegion(x1, y1, x2, y2)
              self.DrawSetPen(c4d.Vector(1,0,0))
              self.DrawRectangle(x1, y1, x2, y2)
              if self.bmp_cache == None:
                  self.bmp_cache = self.GetBitmap()
      
              self.DrawBitmap(self.bmp_cache, x1, y1, x2, y2, 0, 0,
                              w=self.bmp_cache.GetBw(),
                              h=self.bmp_cache.GetBh(),
                              mode=c4d.BMP_NORMAL)        
      
          def Message(self, msg, result):
              # Catch the draw message to cancel it (return True)
              # and call ourself the DrawMsg with the dimension we expect
              if msg.GetId() == c4d.BFM_DRAW:
                  self.DrawMsg(0, 0, self.width, self.height, c4d.BaseContainer())
      
                  return True
          
              return c4d.gui.GeUserArea.Message(self, msg, result)
      
          def GetMinSize(self):
              print("GetMinSize: self.width: %d, self.height: %d"%(self.width,self.height))
              return self.width,self.height
      
      class ExampleDialog(c4d.gui.GeDialog):
          geUserArea = ExampleGeUserArea()
      
          def DrawUA(self,scale):
              print("DrawUserArea", int(400*scale), int(500*scale))
              self.geUserArea.width = int(400*scale)
              self.geUserArea.height = int(500*scale)
              self.LayoutChanged(SCROLL_ID)
      
          def CreateLayout(self):
              self.SetTitle("GeUserArea")
              if self.ScrollGroupBegin(SCROLL_ID, c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT, c4d.SCROLLGROUP_HORIZ | c4d.SCROLLGROUP_VERT):
                  self.AddUserArea(GADGET_ID_GEUSERAREA, c4d.BFH_CENTER | c4d.BFH_SCALE | c4d.BFV_CENTER | c4d.BFV_SCALE, initw=gui.SizePix(400), inith=gui.SizePix(500))
                  self.AttachUserArea(self.geUserArea, GADGET_ID_GEUSERAREA)
              self.GroupEnd()
              self.AddEditSlider(SLIDER_ID, c4d.BFH_SCALEFIT | c4d.BFV_CENTER)
              return True
      
          def InitValues(self):
              self.SetFloat(SLIDER_ID, 1.0, min = 0.01, max = 2, step = 0.01)
              return True
      
          def Command(self,id,msg):
              if id == SLIDER_ID:
                  self.DrawUA(msg[c4d.BFM_ACTION_VALUE])
              return True
      
      def main():
          global dlg
          dlg = ExampleDialog()
          dlg.Open(c4d.DLG_TYPE_ASYNC, pluginid=1234567, defaultw=300, defaulth=300)
      
      
      if __name__ == "__main__":
          main()
      

      Is there a way to get this GeUserArea to scale up into the scrollgroup from the center?

      Thank you!

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

        Hi @blastframe,

        thank you for reaching out to us. Your problem can be solved in principle with GeDialog.GetItemDim(SCROLL_ID) to measure the diemension of the scroll group and GeDialog.SetVisibleArea() to set its, well, visible area 🙂 But in the process of writing the answer to your request I stumbled upon some weird behaviour of SetVisibleArea. I am currently not yet sure if this is being caused by me just not grasping some intricacies of SetVisibleArea or a bug in it. It will take me some time to investigate this. I will come then back with an answer.

        Cheers,
        Ferdinand

        MAXON SDK Specialist
        developers.maxon.net

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

          Hi @blastframe,

          so, this thread has not been forgotten by me 🙂 Yesterday I finally got around to taking a closer look on that problem. I am still not quite sure yet if this a bug or not (I have reached out to the devs for now). There are two problems here at play:

          1. GeDialog.GetItemDim(ID_SOME_SCROLLGROUP) takes the scroll bars of a scroll group into account, GeDialog.SetVisibleArea(ID_SOME_SCROLLGROUP) does not.
          2. GeDialog.SetVisibleArea(ID_SOME_SCROLLGROUP) is behaving differently than advertised in the documentation and will do nothing for frames larger than the scroll group it is being called on.

          I did provide a working example for your problem at the end which wiggles itself around these problems. I also reached out to the devs if we want to consider this a bug or not. If we don't, we will update the documentation of at least SetVisibleArea to make this need for being precise about the passed frame more clear. For now the little work around should work fine in your case, it is only that the code might look a bit cryptic.

          Cheers,
          Ferdinand

          """Example for centering a scroll group.
          
          I have added the method ExampleDialog._centerAlignScrollGroup() and two
          lines calling that method. For details read _centerAlignScrollGroup().
          
          As discussed in:
              plugincafe.maxon.net/topic/13205
          """
          
          import c4d
          import random
          from c4d import gui
          
          GADGET_ID_GEUSERAREA = 10000
          SCROLL_ID = 10001
          SLIDER_ID = 10002
          
          
          class ExampleGeUserArea(c4d.gui.GeUserArea):
              width = 400
              height = 500
              bmp_cache = None
          
              def GetBitmap(self):
                  bmp = c4d.bitmaps.BaseBitmap()
                  bmp.Init(self.width, self.height)
                  for h in range(self.height):
                      for w in range(self.width):
                          r = random.randint(0, 70)
                          bmp.SetPixel(w, h, r, r, r)
                  return bmp
          
              def DrawMsg(self, x1, y1, x2, y2, msg):
                  # print("DrawMsg: x1: %d, y1: %d, x2: %d, y2: %s" % (x1, y1, x2, y2))
                  self.OffScreenOn()
                  self.SetClippingRegion(x1, y1, x2, y2)
                  self.DrawSetPen(c4d.Vector(1, 0, 0))
                  self.DrawRectangle(x1, y1, x2, y2)
                  if self.bmp_cache == None:
                      self.bmp_cache = self.GetBitmap()
          
                  self.DrawBitmap(self.bmp_cache, x1, y1, x2, y2, 0, 0,
                                  w=self.bmp_cache.GetBw(),
                                  h=self.bmp_cache.GetBh(),
                                  mode=c4d.BMP_NORMAL)
          
              def Message(self, msg, result):
                  # Catch the draw message to cancel it (return True)
                  # and call ourself the DrawMsg with the dimension we expect
                  if msg.GetId() == c4d.BFM_DRAW:
                      self.DrawMsg(0, 0, self.width, self.height, c4d.BaseContainer())
          
                      return True
          
                  return c4d.gui.GeUserArea.Message(self, msg, result)
          
              def GetMinSize(self):
                  # print("GetMinSize: self.width: %d, self.height: %d" %
                  #      (self.width, self.height))
                  return self.width, self.height
          
          
          class ExampleDialog(c4d.gui.GeDialog):
              geUserArea = ExampleGeUserArea()
          
              def _centerAlignScrollGroup(self):
                  """Centers the scroll group.
          
                  There are two problems/bugs here at play which we have to deal with:
          
                      1. GeDialog.GetItemDim(SCROLL_ID) takes the scroll bars of a
                         scroll group into account, GeDialog.SetVisibleArea(SCROLL_ID)
                         does not.
                      2. GeDialog.SetVisibleArea(SCROLL_ID) is behaving differently 
                         than advertised in the documentation frames and does nothing
                         for frames larger than the scroll group it is being called upon.
          
                  My workaround here simply computes the deltas for the unaccounted 
                  scrollbars.
                  """
                  # The width and height of the scroll group and user area gadgets.
                  _, _, sgw, sgh = self.GetItemDim(SCROLL_ID).values()
                  _, _, uaw, uah = self.GetItemDim(GADGET_ID_GEUSERAREA).values()
          
                  # These are the magic numbers we have to apply for now after querying
                  # a scroll group for its dimensions. These are the scroll bars which
                  # are being accounted for in the total values. Which get in the way
                  # when we do not deal with them, due to Get/SetVisibleArea not taking
                  # them into account.
                  # sgw -= 16
                  # sgh -= 15
          
                  # To take the safe route, i.e., to make this OS and somewhat version 
                  # agnostic, we should compute these deltas ourselves.
                  sax1, say1, sax2, say2 = self.GetVisibleArea(SCROLL_ID).values()
                  dx, dy = sgw - (sax2 - sax1), sgh - (say2 - say1) # 16, 15
                  sgw -= dx
                  sgh -= dy
          
                  # When we do not do this, .SetVisibleArea() will fail due not working
                  # as advertised. It cannot handle frames larger than the gadget and
                  # will carry out something different than instructed with such a too
                  # large frame (which in most cases will seem as if it did not do
                  # anything).
          
                  # The top left corner of the centered scroll bar frame.
                  x, y = int((uaw - sgw) * .5), int((uah - sgh) * .5)
                  self.SetVisibleArea(SCROLL_ID, x, y, x + sgw, y + sgh)
          
              def DrawUA(self, scale):
                  # print("DrawUserArea", int(400*scale), int(500*scale))
                  self.geUserArea.width = int(400*scale)
                  self.geUserArea.height = int(500*scale)
                  self.LayoutChanged(SCROLL_ID)
          
              def CreateLayout(self):
                  self.SetTitle("GeUserArea")
                  if self.ScrollGroupBegin(SCROLL_ID, c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT, c4d.SCROLLGROUP_HORIZ | c4d.SCROLLGROUP_VERT):
                      self.AddUserArea(GADGET_ID_GEUSERAREA, c4d.BFH_CENTER | c4d.BFH_SCALE |
                                       c4d.BFV_CENTER | c4d.BFV_SCALE, initw=gui.SizePix(400), inith=gui.SizePix(500))
                      self.AttachUserArea(self.geUserArea, GADGET_ID_GEUSERAREA)
                  self.GroupEnd()
                  self.AddEditSlider(SLIDER_ID, c4d.BFH_SCALEFIT | c4d.BFV_CENTER)
                  return True
          
              def InitValues(self):
                  self.SetFloat(SLIDER_ID, 1.0, min=0.01, max=2, step=0.01)
                  # Center the scroll group.
                  self._centerAlignScrollGroup()
                  return True
          
              def Command(self, id, msg):
                  if id == SLIDER_ID:
                      self.DrawUA(msg[c4d.BFM_ACTION_VALUE])
                  # Center the scroll group.
                  self._centerAlignScrollGroup()
                  return True
          
          
          def main():
              global dlg
              dlg = ExampleDialog()
              dlg.Open(c4d.DLG_TYPE_ASYNC, pluginid=1234567, defaultw=300, defaulth=300)
          
          
          if __name__ == "__main__":
              main()
          

          MAXON SDK Specialist
          developers.maxon.net

          ? 1 Reply Last reply Reply Quote 1
          • ?
            A Former User @ferdinand
            last edited by

            @ferdinand You are an absolute genius. 🏆 Thank you so so much, Ferdinand. Incredible!!

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

              Hello @blastframe,

              I forgot to put the final outcome for this here. So, after some back and forth we decided to not fix this for reasons I unfortunately cannot disclose. But we will update the documentation in an upcoming release to avoid this being as opaque as it currently is. This is probably not what you hoped for, but currently the best course of actions for us.

              Thank you for your understanding,
              Ferdinand

              MAXON SDK Specialist
              developers.maxon.net

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