Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware API
      • ZBrush Python 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

    Controlling the window size of a gui

    PYTHON Development
    0
    12
    1.3k
    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.
    • H
      Helper
      last edited by

      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.

      1 Reply Last reply Reply Quote 0
      • H
        Helper
        last edited by

        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.

        -ScottA

        1 Reply Last reply Reply Quote 0
        • H
          Helper
          last edited by

          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.

          1 Reply Last reply Reply Quote 0
          • H
            Helper
            last edited by

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

            Hi,

            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,
            Ferdinand

            1 Reply Last reply Reply Quote 0
            • H
              Helper
              last edited by

              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?

              -ScottA

              1 Reply Last reply Reply Quote 0
              • H
                Helper
                last edited by

                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.
                Confused

                1 Reply Last reply Reply Quote 0
                • H
                  Helper
                  last edited by

                  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) :
                          self.host = 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')
                          self.GroupEnd()
                          return True
                      
                      def InitValues(self) :
                          self.SetLong(1000, self.width, 0, step=1)
                          return True
                      
                      def Command(self, id, msg) :
                          if id == 1002: self.host.ReOpen(self.GetLong(1000))
                          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__':
                      main()
                  
                  1 Reply Last reply Reply Quote 0
                  • H
                    Helper
                    last edited by

                    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 self.host.variable (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? 😉

                    1 Reply Last reply Reply Quote 0
                    • H
                      Helper
                      last edited by

                      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
                              else:
                                  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
                      
                      1 Reply Last reply Reply Quote 0
                      • H
                        Helper
                        last edited by

                        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. self.host.variable).
                        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 self.host.

                        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.

                        1 Reply Last reply Reply Quote 0
                        • H
                          Helper
                          last edited by

                          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,
                          Ferdinand

                          1 Reply Last reply Reply Quote 0
                          • H
                            Helper
                            last edited by

                            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.

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