Redrawing GeUserArea in ScrollGroup with Slider Input



  • Hello,
    I'm trying to resize a GeUserArea within a ScrollGroup with a gui Slider. The GeUserArea's border is not being redrawn properly in my script as I don't think I'm calling the Redraw() / LayoutChanged() in the correct way: it's drawing unwanted lines as I drag the slider and scrollbar.
    Slider_Drag.png

    I just want one solid border. The extra borders go away upon release of the Slider handle but are persistent with the scrollbar. Where would be the right place to call Redraw (or is something else causing the problem)? Thank you!

    import c4d
    from c4d import gui
    
    class ExampleUserArea(c4d.gui.GeUserArea):
        def __init__(self, x, y, width, height):
            super(ExampleUserArea, self).__init__()
            self.x = x
            self.y = y
            self.width = width
            self.height = height
            self.Redraw()
    
        def DrawMsg(self, x1, y1, x2, y2, msg) :
            self.OffScreenOn()
            self.SetClippingRegion(x1, y1, x2, y2)
            self.DrawRectangle(x1, y1, x2, y2)
            self.DrawBorder(c4d.BORDER_IN, x1, y1, x2, y2)
    
        def GetMinSize(self):
            return self.width, self.height
    
        def InputEvent(self, msg) :
            self.Redraw()
            return True     
    
    class ExampleDialog(c4d.gui.GeDialog):
        ID_SLIDER = 10000
        ID_UA = 10001
        ID_SCROLLGRP = 10002
    
        def __init__(self):
            pass
    
        def Command(self, id, data):
            if id == self.ID_SLIDER:
                self.ua.height = int(self.GetFloat(self.ID_SLIDER))
                print(self.ua.width,self.ua.height)
                self.ua.LayoutChanged()
            return True
    
        def CreateLayout(self):
            self.SetTitle("Scrolling UserArea Example")
            self.AddSlider(self.ID_SLIDER, c4d.BFH_SCALEFIT | c4d.BFV_BOTTOM, 10)
            self.SetFloat(self.ID_SLIDER, 200, 1, 1000, 1)
    
            if self.ScrollGroupBegin(self.ID_SCROLLGRP, c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT, scrollflags=c4d.SCROLLGROUP_VERT):
                self.ua = ExampleUserArea(0,0,200,200)
                self.AddUserArea(self.ID_UA, c4d.BFH_LEFT | c4d.BFV_TOP)
                self.AttachUserArea(self.ua, self.ID_UA)
            self.GroupEnd()
            self.ua.Redraw()
            return True
    
    def main():
        dlg = ExampleDialog()
        dlg.Open(c4d.DLG_TYPE_MODAL_RESIZEABLE, xpos=-2, ypos=-2, defaultw=400, defaulth=400)
    
    if __name__=='__main__':
        main()
    


  • Hi @blastframe just as a temporary workaround (since I confirm there is a bug in our side also).

    Here a solution that cancels the initials DrawMsg and replaces with a custom one (with the correct Size).
    This way everything is working as expected (but keep in mind this is a hack, but I see only this solution as a workaround).

    import c4d
    import os
    from c4d import gui
    
    class ExampleUserArea(c4d.gui.GeUserArea):
        def __init__(self, x, y):
            super(ExampleUserArea, self).__init__()
    
            # Init the bitmap
            fn = c4d.storage.GeGetC4DPath(c4d.C4D_PATH_DESKTOP)
            image = "random_img.jpg"
            path = os.path.join(fn,image)
            self.bmp = c4d.bitmaps.BaseBitmap()
            if self.bmp.InitWith(path)[0] != c4d.IMAGERESULT_OK:
                raise ValueError("Cannot load image " + path + ".")
    
            # During the init, set the width and height by the size of the Bitmap
            self.width, self.height = self.bmp.GetSize()
    
        def DrawMsg(self, x1, y1, x2, y2, msg):
            self.OffScreenOn()
            self.SetClippingRegion(x1, y1, x2, y2)
            self.DrawBitmap(self.bmp,
                            x1, y1, x2, y2,
                            0, 0, self.bmp.GetBw(), self.bmp.GetBh(),
                            c4d.BMP_NORMAL | c4d.BMP_ALLOWALPHA)
    
            self.DrawBorder(c4d.BORDER_IN, x1, y1, x2, y2)
    
        def GetMinSize(self):
            return self.width, self.height
        
        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 InputEvent(self, msg) :
            self.Redraw()
            return True
    
    class ExampleDialog(c4d.gui.GeDialog):
        ID_SLIDER = 10000
        ID_UA = 10001
        ID_SCROLLGRP = 10002
    
        def __init__(self):
            pass
    
        def Command(self, id, data):
            if id == self.ID_SLIDER:
                self.ua.height = int(self.GetFloat(self.ID_SLIDER))
                self.ua.LayoutChanged()
                self.LayoutChanged(self.ID_SCROLLGRP)
            return True
    
        def CreateLayout(self):
            self.SetTitle("Scrolling UserArea Example")
            self.AddSlider(self.ID_SLIDER, c4d.BFH_SCALEFIT | c4d.BFV_BOTTOM, 10)
            self.SetFloat(self.ID_SLIDER, 200, 1, 1000, 1)
    
            if self.ScrollGroupBegin(self.ID_SCROLLGRP, c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT, scrollflags=c4d.SCROLLGROUP_VERT):
                self.ua = ExampleUserArea(0, 0)
                self.AddUserArea(self.ID_UA, c4d.BFH_LEFT | c4d.BFV_TOP)
                self.AttachUserArea(self.ua, self.ID_UA)
    
            self.GroupEnd()
            return True
    
    def main():
        global dlg
        dlg = ExampleDialog()
        dlg.Open(c4d.DLG_TYPE_ASYNC, xpos=-2, ypos=-2, defaultw=400, defaulth=400)
    
    if __name__=='__main__':
        main()
    

    Cheers,
    Maxime.



  • Hey @blastframe quickly looking at your code, you redraw the GeUserArea but not the whole group.

        def Command(self, id, data):
            if id == self.ID_SLIDER:
                self.ua.height = int(self.GetFloat(self.ID_SLIDER))
                print(self.ua.width,self.ua.height)
                self.ua.LayoutChanged()
                self.LayoutChanged(self.ID_SCROLLGRP)
            return True
    

    Cheers,
    Maxime



  • @m_adam Thanks for the reply, Maxime! That fixed the issue with the slider, however the issue remains when the group is scrolled. I tried this in the Command method, but it didn't work:

            if id == self.ID_SCROLLGRP:
                self.ua.LayoutChanged()
                self.LayoutChanged(self.ID_SCROLLGRP)
    

    There doesn't seem to be any id or data passed to the Command method when the group is scrolled.

    I also tried listening for the BFM_SCROLLGROUP_SCROLLED message:

            def Message(self, msg, result):
                if msg.GetId() == c4d.BFM_SCROLLGROUP_SCROLLED:
                    self.ua.LayoutChanged()
                    self.LayoutChanged(self.ID_SCROLLGRP)
                return c4d.gui.GeDialog.Message(self, msg, result)
    

    This created a loop and froze Cinema 4D (I believe because self.LayoutChanged(self.ID_SCROLLGRP) may also be sending this Message?).

    Can you please help me understand how to redraw the Scrollgroup & GeUserArea on scroll? Thank you!



  • Hi @blastframe, just to inform you I didn't forget you,

    I thinks this may be another issue related to GeUserArea wrong coordinate topic:

    However, I still needs some time to investigate from where it come.
    I will keep you updated.
    Cheers,
    Maxime.



  • @m_adam Thank you very much for all your help, Maxime!

    Yes, after you answered my question here, I used that workaround for the UserArea from this issue.

    It is working, but I'd rather the safer solution you're seeking.

    All the best.



  • Hi @blastframe just as a temporary workaround (since I confirm there is a bug in our side also).

    Here a solution that cancels the initials DrawMsg and replaces with a custom one (with the correct Size).
    This way everything is working as expected (but keep in mind this is a hack, but I see only this solution as a workaround).

    import c4d
    import os
    from c4d import gui
    
    class ExampleUserArea(c4d.gui.GeUserArea):
        def __init__(self, x, y):
            super(ExampleUserArea, self).__init__()
    
            # Init the bitmap
            fn = c4d.storage.GeGetC4DPath(c4d.C4D_PATH_DESKTOP)
            image = "random_img.jpg"
            path = os.path.join(fn,image)
            self.bmp = c4d.bitmaps.BaseBitmap()
            if self.bmp.InitWith(path)[0] != c4d.IMAGERESULT_OK:
                raise ValueError("Cannot load image " + path + ".")
    
            # During the init, set the width and height by the size of the Bitmap
            self.width, self.height = self.bmp.GetSize()
    
        def DrawMsg(self, x1, y1, x2, y2, msg):
            self.OffScreenOn()
            self.SetClippingRegion(x1, y1, x2, y2)
            self.DrawBitmap(self.bmp,
                            x1, y1, x2, y2,
                            0, 0, self.bmp.GetBw(), self.bmp.GetBh(),
                            c4d.BMP_NORMAL | c4d.BMP_ALLOWALPHA)
    
            self.DrawBorder(c4d.BORDER_IN, x1, y1, x2, y2)
    
        def GetMinSize(self):
            return self.width, self.height
        
        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 InputEvent(self, msg) :
            self.Redraw()
            return True
    
    class ExampleDialog(c4d.gui.GeDialog):
        ID_SLIDER = 10000
        ID_UA = 10001
        ID_SCROLLGRP = 10002
    
        def __init__(self):
            pass
    
        def Command(self, id, data):
            if id == self.ID_SLIDER:
                self.ua.height = int(self.GetFloat(self.ID_SLIDER))
                self.ua.LayoutChanged()
                self.LayoutChanged(self.ID_SCROLLGRP)
            return True
    
        def CreateLayout(self):
            self.SetTitle("Scrolling UserArea Example")
            self.AddSlider(self.ID_SLIDER, c4d.BFH_SCALEFIT | c4d.BFV_BOTTOM, 10)
            self.SetFloat(self.ID_SLIDER, 200, 1, 1000, 1)
    
            if self.ScrollGroupBegin(self.ID_SCROLLGRP, c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT, scrollflags=c4d.SCROLLGROUP_VERT):
                self.ua = ExampleUserArea(0, 0)
                self.AddUserArea(self.ID_UA, c4d.BFH_LEFT | c4d.BFV_TOP)
                self.AttachUserArea(self.ua, self.ID_UA)
    
            self.GroupEnd()
            return True
    
    def main():
        global dlg
        dlg = ExampleDialog()
        dlg.Open(c4d.DLG_TYPE_ASYNC, xpos=-2, ypos=-2, defaultw=400, defaulth=400)
    
    if __name__=='__main__':
        main()
    

    Cheers,
    Maxime.



  • @m_adam Thank you, Maxime, for going to this effort.


Log in to reply