Scroll area in UA

On 12/07/2018 at 07:46, xxxxxxxx wrote:

I have a User Area with multiple (vertical) images in it.
Now I want to add a vertical scroll bar to scroll through the images.
But I do not understand fully, the relation between the ua size and the visible area size?

What is the best place to define the visible area?
How do I use SetVisibleArea() when the user area is resized?

A little example would help me for certain!

Here the code.

import c4d
from c4d import bitmaps, gui, plugins, utils, documents
import collections, os, sys
  
PLUGIN_ID = 10000010 #TestID only!!!!!!!!!!!!
scrollGroup = 1001
  
class Area(gui.GeUserArea) :
  
    def __init__(self) :
        self.bmp = c4d.bitmaps.BaseBitmap()
  
    def Sized(self, w, h) :
        print "Sized: ", w,h
        return     
  
    def DrawMsg(self, x1, y1, x2, y2, msg) :
    
        self.DrawRectangle(x1, y1, x2, y2)                                #Draws the UA rectangle area
        
        path = os.path.join(os.path.dirname(__file__), "Library", "Airbus_A380.jpg")
        result, ismovie = self.bmp.InitWith(path)
        
        x1 = 10
        if result == c4d.IMAGERESULT_OK:
            y1 = 10
            self.DrawBitmap(self.bmp, x1, y1, 200, 200, 0, 0, 200, 200, c4d.BMP_NORMAL)    #first        
            y1 = 220
            self.DrawBitmap(self.bmp, x1, y1, 200, 200, 0, 0, 200, 200, c4d.BMP_NORMAL)    #second     
            y1 = 430
            self.DrawBitmap(self.bmp, x1, y1, 200, 200, 0, 0, 200, 200, c4d.BMP_NORMAL)    #third
    
class MyDialog(gui.GeDialog) :
  
    def __init__(self, area) :
        self.area = area
        
    def CreateLayout(self) :
        self.ScrollGroupBegin(scrollGroup, c4d.BFH_SCALEFIT|c4d.BFV_SCALEFIT, c4d.SCROLLGROUP_VERT)
        
        self.GroupBegin(1003, c4d.BFH_CENTER | c4d.BFV_CENTER, cols=1)
        self.GroupBorderNoTitle(c4d.BORDER_NONE)
        self.GroupBorderSpace(0, 0, 0, 20)
        self.AddUserArea(1, c4d.BFH_LEFT | c4d.BFV_TOP) 
        self.AttachUserArea(self.area, 1)
        self.GroupEnd() #end UA group
        
        self.GroupEnd() #end scrollgroup
  
        self.GroupBegin(0, flags=c4d.BFH_FIT, cols=1)
        self.AddButton(1027, flags=c4d.BFH_LEFT, initw=100, name="Test")  #Test button
        self.GroupEnd()
  
        return True
                
    def Command(self, id, msg) :
  
        if (id == 1027) :
            print self.GetVisibleArea(scrollGroup)   
            self.SetVisibleArea(scrollGroup, 0,0,250,250)
  
            self.area.Redraw()
            return True
            
        return True
        
class MBLibrary(plugins.CommandData) :
    
    area = Area()
    dialog = None
  
    def Execute(self, doc) :
        if self.dialog is None: self.dialog = MyDialog(self.area)
        return self.dialog.Open(dlgtype=c4d.DLG_TYPE_ASYNC, pluginid=PLUGIN_ID, defaultw=500, defaulth=120)
    def RestoreLayout(self, sec_ref) :
        if self.dialog is None: self.dialog = MyDialog(self.area)
        return self.dialog.Restore(pluginid=PLUGIN_ID, secret=sec_ref)
  
if __name__ == "__main__":
  
    pluginString = "MB Library V01"
   
    bmp = bitmaps.BaseBitmap()
    dir, f = os.path.split(__file__)
    fn = os.path.join(dir, "res", "icon.tif")
    bmp.InitWith(fn)
    okyn = plugins.RegisterCommandPlugin(id=PLUGIN_ID, str=pluginString, info=0, help=pluginString, dat=MBLibrary(), icon=bmp)
                                  
    if (okyn) : 
        print pluginString + " initialized."
    else: print "Error initializing " + pluginString
  

On 16/07/2018 at 07:48, xxxxxxxx wrote:

Hi Pim,

The ua size is the total size of the GeUserArea, without any scrolling, so it's the full GeUserArea size, which can be bigger than the screen of course.
The visible area is the part which is currently visible in this GeUserArea.

Let' take an example, I get a ua of a 2000 pixels height, but my dialog is only 500 pixels height. So the Visible area will only be 500 pixels, but from the 2000 pixels, displayed pixels can be from 0 to 500 or either 1000 to 1500.

But looking at your code there is few adjustments to make. For example, you initialize the parent group to BFH/BFV_CENTER, so by default a GeUserArea it will be a square of 4,4 pixels since there is nothing inside, please use BFH/BFV_SCALEFIT instead, it will then automatically scale according to the size you define in GetMinSize function.

On a side note since you are going to draw more than it's allowed (by that I mean you will draw from pixel 0 to 2000 and then crop it using a scrollGroup in order to allow custom display size) so make sure to call SetClippingRegion in the DrawMsg function.

Here is your example fixed

import c4d
from c4d import bitmaps, gui, plugins, utils, documents
import collections, os, sys
  
PLUGIN_ID = 10000010 #TestID only!!!!!!!!!!!!
scrollGroup = 1001
  
class Area(gui.GeUserArea) :
  
    def __init__(self) :
        self.bmp = c4d.bitmaps.BaseBitmap()
  
    def Sized(self, w, h) :
        print "Sized: ", w,h
        return
  
    def GetMinSize(self) :
      #do a calculation here
      return 400, 430 + 200
  
    def DrawMsg(self, x1, y1, x2, y2, msg) :
        self.DrawSetPen(c4d.COLOR_BG_HIGHLIGHT)
        self.SetClippingRegion(x1, y1, x2, y2)
        self.DrawRectangle(x1, y1, x2, y2)                                #Draws the UA rectangle area
        
        path = os.path.join(os.path.dirname(__file__), "Library", "Airbus_A380.jpg")
        result, ismovie = self.bmp.InitWith(path)
        
        x1 = 10
        if result == c4d.IMAGERESULT_OK:
            y1 = 10
            self.DrawBitmap(self.bmp, x1, y1, 200, 200, 0, 0, 200, 200, c4d.BMP_NORMAL)    #first        
            y1 = 220
            self.DrawBitmap(self.bmp, x1, y1, 200, 200, 0, 0, 200, 200, c4d.BMP_NORMAL)    #second     
            y1 = 430
            self.DrawBitmap(self.bmp, x1, y1, 200, 200, 0, 0, 200, 200, c4d.BMP_NORMAL)    #third
    
class MyDialog(gui.GeDialog) :
  
    def __init__(self, area) :
        self.area = area
        
    def CreateLayout(self) :
        self.ScrollGroupBegin(scrollGroup, c4d.BFH_SCALEFIT|c4d.BFV_SCALEFIT, c4d.SCROLLGROUP_VERT)
        
        self.GroupBegin(1003, c4d.BFH_SCALEFIT|c4d.BFH_SCALEFIT, cols=1)
        self.GroupBorderSpace(0, 0, 0, 20)
        self.AddUserArea(1005, c4d.BFH_SCALEFIT|c4d.BFH_SCALEFIT)
        self.AttachUserArea(self.area, 1005)
        self.GroupEnd() #end UA group
        
        self.GroupEnd() #end scrollgroup
  
        self.GroupBegin(0, flags=c4d.BFH_FIT, cols=1)
        self.AddButton(1027, flags=c4d.BFH_LEFT, initw=100, name="Test")  #Test button
        self.GroupEnd()
  
        return True
                
    def Command(self, id, msg) :
  
        if (id == 1027) :
            sizes = self.GetVisibleArea(scrollGroup)
            ySize = sizes["y2"] - sizes["y1"] # Get the current size displayed
            #Jump to the 2nd picture
            print self.area.GetHeight()
            self.SetVisibleArea(scrollGroup, 0,220, 0, 220 + ySize)
  
            #self.area.Redraw()
            return True
            
        return True
  
        
class MBLibrary(plugins.CommandData) :
    
    area = Area()
    dialog = None
  
    def Execute(self, doc) :
        if self.dialog is None: self.dialog = MyDialog(self.area)
        return self.dialog.Open(dlgtype=c4d.DLG_TYPE_ASYNC, pluginid=PLUGIN_ID, defaultw=500, defaulth=120)
    def RestoreLayout(self, sec_ref) :
        if self.dialog is None: self.dialog = MyDialog(self.area)
        return self.dialog.Restore(pluginid=PLUGIN_ID, secret=sec_ref)
  
if __name__ == "__main__":
  
    pluginString = "MB Library V01"
   
    bmp = bitmaps.BaseBitmap()
    dir, f = os.path.split(__file__)
    fn = os.path.join(dir, "res", "icon.tif")
    bmp.InitWith(fn)
    okyn = plugins.RegisterCommandPlugin(id=PLUGIN_ID, str=pluginString, info=0, help=pluginString, dat=MBLibrary(), icon=bmp)
                                  
    if (okyn) :
        print pluginString + " initialized."
    else: print "Error initializing " + pluginString

If you have any questions please let me know,
Cheers,
Maxime

On 17/07/2018 at 03:30, xxxxxxxx wrote:

Great explanation!
Thanks, Pim

On 18/07/2018 at 08:38, xxxxxxxx wrote:

Sorry, more questions on scrolling UA.

Next step is to size the images / thumbnails to be displayed.

For example, when I click the test button, the size of the thumbnail is decreased from 200 to 20.
Displaying the sized thumbnails is no problem, but how to size the vertical scroll bar, thus that it indicates the new size?

Do I use GetMinSize() or SetVisibleArea().
If so, where and when do I call GetMinSize()?

        if (id == 1027) :
            newSize = 20
            self.area.GetMinSize()
            print "SetVisibleArea: ", self.SetVisibleArea(scrollGroup, 0, 0, 400, 3*(newSize+10))
            self.area.Redraw()

On 18/07/2018 at 09:39, xxxxxxxx wrote:

Hi Scott,

You don't have to call GetMinSize, it's called automatically when the parent(owner of the GeUserArea) is drawn/sized, in your case, group 1003 which is owned by your ScrollGroup.
So in order to support what's, you want I've made few adjustments.

class Area(gui.GeUserArea) :
    size = 300
    pictureCnt = 3
  
    def GetMinSize(self) :
        return 400, self.pictureCnt * self.size
  
    def DrawMsg(self, x1, y1, x2, y2, msg) :
        self.DrawSetPen(c4d.COLOR_BG_HIGHLIGHT)
        self.SetClippingRegion(x1, y1, x2, y2)
        self.DrawRectangle(x1, y1, x2, y2)  # Draws the UA rectangle area
  
        path = os.path.join(os.path.dirname(__file__), "Library", "Airbus_A380.jpg")
        result, ismovie = self.bmp.InitWith(path)
  
        x1 = 10
        if result == c4d.IMAGERESULT_OK:
            for i in xrange(0, self.pictureCnt) :
                y1 = self.size * i
                self.DrawBitmap(self.bmp, x1, y1, self.size, self.size, 0, 0, self.size, self.size, c4d.BMP_NORMAL)

and in the GeDialog

if (id == 1027) :
	self.area.size = 400
	self.LayoutChanged(scrollGroup)
	sizes = self.GetVisibleArea(scrollGroup)
	ySize = sizes["y2"] - sizes["y1"]  # Get the current size displayed
	pictureId = 1 # ID start from 0
	self.SetVisibleArea(scrollGroup, 0, self.area.size * pictureId, 0, self.area.size * pictureId + ySize)
	return True

So we define the new size of the picture then we call self.LayoutChanged(scrollGroup) which will call DrawMsg and also GetMinSize in order to know the size of the UserArea then finally our scrollGroup is drawn according to these data. Then after is up to us to scroll where we want.

Maybe off topic, but I did a library with thumbnail on my personal time some time ago, it's available in here you may found some interesting stuff in it.

Again if you have any question, let me know! 🙂
Cheers,
Maxime