Scaling a GeUserArea in a ScrollGroup
-
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 InputThe 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.
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!
-
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 andGeDialog.SetVisibleArea()
to set its, well, visible area But in the process of writing the answer to your request I stumbled upon some weird behaviour ofSetVisibleArea
. I am currently not yet sure if this is being caused by me just not grasping some intricacies ofSetVisibleArea
or a bug in it. It will take me some time to investigate this. I will come then back with an answer.Cheers,
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:
GeDialog.GetItemDim(ID_SOME_SCROLLGROUP)
takes the scroll bars of a scroll group into account,GeDialog.SetVisibleArea(ID_SOME_SCROLLGROUP)
does not.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()
-
@ferdinand You are an absolute genius. Thank you so so much, Ferdinand. Incredible!!
-
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