Controlling the window size of a gui

On 25/06/2013 at 14:00, xxxxxxxx wrote:

Hi everyone!
I'm currently developing a command plugin, that's using lots of dynamic groups in the gui. I use GeDialog.LayoutFlushGroup(id) and GeDialog.LayoutChanged(id) to create and remove these groups respectively, which is working as expected.
The problem I'm currently running into is, that with every new groups that becomes visible the dialog window gets expanded. When one of the groups is hidden on the other hand, the dialog window stays the same, so it basically gets stretched out over time. While functionally this isn't a problem, it just doesn't look very visually pleasing.
The plugin currently uses a modal dialog, which in my case is actually the best choice, but if there was a way to shrink my dialog dynamically to my content size in an asynchronous dialog, I'd be considering that option.

I hope someone has an idea how to work around this.

On 25/06/2013 at 14:08, xxxxxxxx wrote:

Have you considered using a scroll group to hold your dynamic groups?
That way your main dialog window will never change size.


On 25/06/2013 at 14:22, xxxxxxxx wrote:

Well, in my particular case that wouldn't make much sense. The plugin basically functions as a popup, that is only supposed to work with keyboard input.
My original version had all the possible groups visible at all time, but I was just curious if it can be done more elegantly.
I guess what I'd need is something that tells the dialog to redraw once the content has been updated. Apparently there is some mechanism like this for expanding it, only not he other way around.

On 25/06/2013 at 15:22, xxxxxxxx wrote:


I assume there is way with GeDialog.SendMessage(), promising ids could be BFM_CALCSIZE, 
BFM_SIZED or BFM_ADJUSTSIZE. Personally I would go for simply sending the new desired
min size to the hosting plugin and then reopen the dialog with that values. Much easier imho.

Happy rendering,

On 25/06/2013 at 15:52, xxxxxxxx wrote:

If I remember correctly.
We don't have the ability to control the window size in the SDK. Not even with C++.
That kind of thing has to be done by programming the OS.
So in order to make the window fit the GUI's. The window has to be closed and reopened again.
It's a rather annoying limitation.

If you're just using the dialog as a pop up. Maybe the ShowPopupDialog() option is something that will work for you as an alternative?


On 25/06/2013 at 16:22, xxxxxxxx wrote:

Well, controlling the size trough messages seemed to be a good idea for a moment. At the time beeing I've only managed to read the window dimensions with BFM_ADJUSTSIZE, but no luck on actually changing them.
I've actually tried the opening/closing method, but the plugin always got stuck reopening and I had to force quit Cinema. The ShowPopopDialog also won't work, because it will halt everything else and wait for an result (which won't do in my case).
Anyhow, thanks for your help, but I'm afraid if it can't even be done in C++ I think I'm all out of luck.

On 25/06/2013 at 17:11, xxxxxxxx wrote:

try something like that:

import c4d
from c4d import gui
class myDialog(c4d.gui.GeDialog) :
    def __init__(self, host, width) : = host
        self.width = width
    def CreateLayout(self) :
        self.AddEditNumber(1000, c4d.BFH_SCALEFIT)
        self.GroupBegin(1001, c4d.BFH_LEFT)
        self.AddButton(1002, c4d.BFH_SCALEFIT, name = 'Reopen')
        self.AddButton(1003, c4d.BFH_SCALEFIT, name = 'Close')
        return True
    def InitValues(self) :
        self.SetLong(1000, self.width, 0, step=1)
        return True
    def Command(self, id, msg) :
        if id == 1002:
        if id == 1003: self.Close()
        return True
class DummyHost(object) :
    def __init__(self) :
        self.dialog = myDialog(self, 200)
        self.dialog.Open(c4d.DLG_TYPE_ASYNC_POPUPEDIT, xpos = 150, ypos = 150, defaultw = 200)
    def ReOpen(self, width) :
        if self.dialog.Close() :
            self.dialog = myDialog(self, width)
            self.dialog.Open(c4d.DLG_TYPE_ASYNC_POPUPEDIT, xpos = 150, ypos = 150, defaultw = width)
def main() :
    foo = DummyHost()
if __name__=='__main__':

On 26/06/2013 at 03:15, xxxxxxxx wrote:

Very interesting approach on the reopening mechanic. I would have never thought of that. Your example works fine, but so far I haven't been able to implement this on my more complex plugin.
Still have the hanging effect.
In any case I'm reevaluating if this is the way to go for me. The downside is, that it makes my code really hard to read, because I have to outsource all my local variables to the dummy host and then reference them by (In bulk it's worse than it might look at first sight).

Would be great if there was a simpler way, but beggars can't be choosers, right? :wink:

On 26/06/2013 at 05:29, xxxxxxxx wrote:

The dummy host is just meant to be a placeholder for the wrapping class for your dialog. Something like your plugin class or another dialog. For the values - you have simply to store them in a container. That is not much work. You could however write a fully automated value initialization GeDialog class, that might become handy in the future, as this is a common problem (with fully automated i mean a class that you can derive from a do not have to overwrite Init for initing the basic dialog elements like strings, bools, reals and so on. you would need than a BaseContainer format that does store the DescLevel.dtype, min/max values and so on.

i have not tested the code, but i guess it shows the general idea and it just does cover the four very basic dialog element types.

def someClassMethod(self) :
    self.bc = c4d.BaseContainer()
    self.bc.SetLong(1000, someValue)
    self.bc.SetBool(1001, someValue)
    self.bc.SetString(1002, someValue)
    self.dlg = myDialog(self.bc)
    # return self.dlg.Open( ...
def anotherClassMethod(self) :
    self.bc = self.dlg.GetValues()
    return self.dlg.Close()
# ...
class myDialog(c4d.gui.GeDialog) :
    def __init__(self, container) :
        self.bc = container
    def InitValues(self) :
        if isinstance(self.bc , c4d.BaseContainer) :
            self.SetLong(1000, self.bc.GetLong(1000), 0, step=1)
            self.SetBool(1001, self.bc.GetBool(1001), 0, step=1)
            self.SetString(1002, self.bc.GetString(1002), 0, step=1)
            return True
	# implement some sort of fallback here
            return False
    def Command(self, cid, msg) :
        # ...
        if cid in [_cid for _cid, value in self.bc]:
            self.WriteValue(cid, self.bc)
        return True
    def GetValues(self) :
        Returns the dialog container.
        return self.bc
    def WriteValue(self, cid, bc) :
        Save a value to the dialog container.
        :param cid: the container id.
        :param bc: the container to write to.
        :return: bool
        if isinstance(bc, c4d.BaseContainer) :
            etype = bc.GetType(cid)
            if etype == c4d.DTYPE_BOOL:
                value = self.GetBool(cid)
                if value is not None:
                    bc.SetBool(cid, bool(value))
                    return True
            elif etype == c4d.DTYPE_STRING:
                value = self.GetString(cid)
                if value is not None:
                    bc.SetString(cid, str(value))
                    return True
            elif etype == c4d.DTYPE_LONG:
                value = self.GetLong(cid)
                if value is not None: 
                    bc.SetLong(cid, int(value))
                    return True
            elif etype == c4d.DTYPE_REAL: 
                value = self.GetReal(cid)
                if value is not None:
                    bc.SetReal(cid, float(value))
                    return True
        return False

On 26/06/2013 at 07:08, xxxxxxxx wrote:

That's basically what I did. I declared all my variables, in the wrapper class and was accessing them from within the dialog with the reference to the wrapper (e.g.
You're right, that using a BaseContainer for this is probably a more organized way of doing things, but from a readability standpoint it's probably as bad as prefixing everything with

Still not 100% sure, why I still have those hangups when restoring the dialog. It's kind of hard to debug, when Cinema freezes all the time.

On 26/06/2013 at 07:28, xxxxxxxx wrote:

The advantage of a BaseContainer is not only that it might be considered cleaner from structural viewpoint, but also that you can not only store values for the lifetime of you wrapping class but permanently as you can write BaseContainer data into the documents or a separate hyperfile. Also a BaseContainer  reduces the amount of code you need to update your data, as you can do it type based as shown in WriteValue(). The code for a dialog with 100 values is the same as for a dialog with three values. Handling 100 values manually on the other hand might turn out quite messy. I do not want to push anything, my point is just : The whole settings data handling is almost non existent in python. So it does not hurt to have custom GeDialog, ToolData, etc class that can do that.

About the restoring : Make sure you do not call the dialog from another thread than the c4d main thread. That will almost always lead to crashes. Also note that reopening non modal dialogs will also lead to crashes or 'funny' behaviour. You have to allocate a new instance of your modal dialog class before reopening it. Just like I did in my example above.

Happy rendering,

On 27/06/2013 at 09:38, xxxxxxxx wrote:

You're absolutely right about BaseContainers. For something more elaborate it would be the way to go. For now I'll be sticking to my good old standard variables. In any case ATM I'm focusing more on finishing a fully functional version of the plugin and have set the resizing issues aside, for it seems like nothing that can be fixed without completely restructuring the code.

Thanks again for your input. It's been very insightful.