Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware API
      • ZBrush GoZ API
      • Code Examples on Github
    • Forum
    • Downloads
    • Support
      • Support Procedures
      • Registered Developer Program
      • Plugin IDs
      • Contact Us
    • Unread
    • Recent
    • Tags
    • Users
    • Login

    Quicktab SDK Example

    Cinema 4D SDK
    r20 2023 python sdk
    2
    9
    1.6k
    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.
    • M
      mogh
      last edited by mogh

      Dear Developpers,

      I am trying to implement a Quicktab Gui into my Plugin. While i get some User interface that is somehow usable, I see some content getting mixed up in the gui after multi selecting some tabs.

      It might be a simple thing as an ID not matching, but I am a little bit lost hence its basically just the SDK example i use.

      quicktabgui.png

      as you can see FFFF is deselected but shown
      DDDD and CCCC are switched up ....
      and no its not just a row shuffle .. deselecting DDD hides CCC
      deselecting more result in more gui disintegration ...

      Can you confirm that applying more content results in some weird stuff? Or what is your hunch .,... where to look ?

      Thank you for your time.
      cheers mogh

      only little was added .... rest is basically the SDK example ....

      class CustomGroup(c4d.gui.SubDialog):
          
          """A SubDialog to display the passed string, its used as example for the actual content of a Tab"""
          def __init__(self, material, parts):
              self._material = material
              self._parts = '\n'.join(parts)
      
          def CreateLayout(self):
              
              
              self.AddStaticText(CUSTOM_GROUP_ID_TEXT_BASE + 1 , flags=c4d.BFH_LEFT | c4d.BFV_TOP | c4d.BFH_FIT | c4d.BFV_FIT, name=self._material)
              self.AddStaticText(CUSTOM_GROUP_ID_TEXT_BASE + 2 , flags=c4d.BFH_LEFT | c4d.BFV_TOP | c4d.BFH_FIT | c4d.BFV_FIT, name=CUSTOM_GROUP_ID_TEXT_BASE)
              self.AddMultiLineEditText(CUSTOM_GROUP_ID_TEXT_BASE + 3, flags=c4d.BFH_LEFT | c4d.BFV_TOP | c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT, initw=0, inith=0, style=c4d.DR_MULTILINE_WORDWRAP)
              self.SetString(CUSTOM_GROUP_ID_TEXT_BASE + 3, value=self._parts)
      
              #for i, parts in enumerate(self._parts):
              #    self.AddStaticText(CUSTOM_GROUP_ID_TEXT_BASE + i, c4d.BFH_SCALEFIT, name=parts)
              
              return True
      
      
          def Populate(self):
              doc = c4d.documents.GetActiveDocument()
              directory, _ = os.path.split(__file__)
              poart_json_file = os.path.join(directory, "default_asignment.json")
              material_ids, materials_dict = read_parts_json(poart_json_file)
      
              self.FlushAllTabs() # clear the GUI
      
              print( str( material_ids))
              print("-"*80)
      
              for material_id in materials_dict:
                  part_list = materials_dict[material_id]
      
                  #print( str(material_id), str(part_list) )
      
                  ### Tabs
                  ##################################################
                  
                  tab_Content = CustomGroup(str(material_id), part_list)
                  self.AppendTab(str(material_id), tab_Content, False)
      1 Reply Last reply Reply Quote 0
      • M
        m_adam
        last edited by

        Hi there is nothing preventing for more "tabs", since your code is far from being complete it will be very hard for us to help you, so please share your code.

        As a hint I would check for Ids, as it is most likely the cause.
        Cheers,
        Maxime.

        MAXON SDK Specialist

        Development Blog, MAXON Registered Developer

        1 Reply Last reply Reply Quote 0
        • M
          mogh
          last edited by

          I am sorry, I do not want to come across as lacy but it is more or less the sdk example thats why I asked so sparse.

          I numerated the IDS from top to bottom that solved the FFFFF unchecking but there is still GUI disintegration and the switch between CCCC and DDDD

          The occasional supper and so is the prework for the link boxes I want to integrate (copied from ferdinands recent GUI example here in the forum)

          kind regards mogh

          """
          Copyright: MAXON Computer GmbH
          Author: Maxime Adam
          
          Description:
              - Creates a Modal Dialog displaying a different SubDialog according to the selected entry of the QuickTab.
              - Demonstrates how to add, flushes, remove tab interactively.
          
          Class/method highlighted:
              - c4d.gui.QuickTabCustomGui
              - QuickTabCustomGui.ClearStrings()
              - QuickTabCustomGui.AppendString()
              - c4d.gui.GeDialog
              - GeDialog.CreateLayout()
              - GeDialog.InitValues()
              - GeDialog.Command()
              - GeDialog.HideElement()
              - GeDialog.RemoveElement()
              - c4d.gui.SubDialog
          
          """
          import c4d # pyright: ignore[reportMissingImports]
          import json
          import os
          
          DEBUG = True
          
          # Ids used in our Dialog
          ID_MAINGROUP = 1000  # ID used for the Group that holds all the other group representing the tab content
          ID_QUICKTAB_BAR = 1001  # ID for the quicktab customGui
          ID_QUICKTAB_BASE_GROUP = 1002  # Base ID for each SubDialog
          ID_LOADDEFAULT_MAT_ASIGN = 1003
          ID_CREATE_MATERIALS = 1004
          BUTTON_PRINT_TEXT = 1005  # ID used for the Print text Button
          BUTTON_PRINT_SELECTED = 1006  # ID used for the Print Selected Button
          BUTTON_FLUSH_ALL = 1007  # ID used for the Flush All Button
          BUTTON_ADD = 1008  # ID used for the Add Button
          BUTTON_REMOVE = 1009  # ID used for the Remove Button
          
          # Id used in our SubDialog
          CUSTOM_GROUP_ID_TEXT_BASE = 4000  # Defines the ID for the string to be displayed
          
          LAYOUT_DEBUG = True
          DEFAULT_BORDER_STYLE =c4d.BORDER_NONE
          if LAYOUT_DEBUG is True:
              DEFAULT_BORDER_STYLE = c4d.BORDER_BLACK
          
          GENERATOR_START_ID = 2000
          def plusone_id():
              n = GENERATOR_START_ID
              while n < GENERATOR_START_ID+999:
                  yield n
                  n += 1
          
          somne_json = '{"AAAAAAA 85": ["a part_number_1","a part_number_2","a part_number_3"],"BBBBB 40": ["b part_number_4","b part_number_5"],"CCCCCCC": ["c part_number_6","c part_number_7","c part_number_8","c part_number_9"],"DDDDDD": ["d part_number_6","d part_number_7","d part_number_8","d part_number_9"],"FFFFF": ["f Plane"],"GGGGGGG": ["g part_number_6","g part_number_7","g part_number_8","g part_number_9"],"HHHHHHHH": ["h part_number_6","h part_number_7","h part_number_8","h part_number_9"],"IIIIII": ["i part_number_6","i part_number_7","i part_number_8","i part_number_9"],"JJJJJJJJJ": ["j part_number_6","j part_number_7","j part_number_8","j part_number_9"],"KKKKKKKK": ["k part_number_6","k part_number_7","k part_number_8","k part_number_9"],"LLLLLLLLL": ["l part_number_6","l part_number_7","l part_number_8","l part_number_9"]}'
          
          ### Json
          #####################################################################################
          
          def read_parts_json(json_file_path):
              #with open(json_file_path) as f: data = json.load(f)
              
              data = json.loads(somne_json)
          
              materials = {}
              for material_id, part_numbers in data.items():
                  materials[material_id] = part_numbers
              
              material_ids = list(data.keys())
          
              return material_ids, materials
          
          class CustomGroup(c4d.gui.SubDialog):
          
              DEFAULT_FLAGS = c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT
              DEFAULT_SPACE = (5, 5, 5, 5)
              
              """A SubDialog to display the passed string, its used as example for the actual content of a Tab"""
              def __init__(self, material, parts):
          
                  self._material = material
                  self._parts = '\n'.join(parts)
          
              def CreateLayout(self):
                  
                  #self.GroupBorderNoTitle(borderstyle=DEFAULT_BORDER_STYLE)
                  self.AddStaticText(CUSTOM_GROUP_ID_TEXT_BASE + 1 , flags=c4d.BFH_LEFT | c4d.BFV_TOP | c4d.BFH_FIT | c4d.BFV_FIT, name=self._material)
                  self.AddStaticText(CUSTOM_GROUP_ID_TEXT_BASE + 2 , flags=c4d.BFH_LEFT | c4d.BFV_TOP | c4d.BFH_FIT | c4d.BFV_FIT, name=CUSTOM_GROUP_ID_TEXT_BASE)
                  self.AddMultiLineEditText(CUSTOM_GROUP_ID_TEXT_BASE + 3, flags=c4d.BFH_LEFT | c4d.BFV_TOP | c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT, initw=0, inith=0, style=0)
                  self.SetString(CUSTOM_GROUP_ID_TEXT_BASE + 3, value=self._parts)
          
                  #for i, parts in enumerate(self._parts):
                  #    self.AddStaticText(CUSTOM_GROUP_ID_TEXT_BASE + i, c4d.BFH_SCALEFIT, name=parts)
                  
                  return True
          
          
          class MyDialog(c4d.gui.GeDialog):
          
              DEFAULT_FLAGS = c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT
              DEFAULT_SPACE = (5, 5, 5, 5)
          
              def __init__(self, items=[]):
          
                  # this is from the linkbox example from ferdinand
                  super(MyDialog, self).__init__() 
          
                  # will be link box
                  self._items = [] 
                  self._doc = None 
                  self._hasCreateLayout = False
                  self.Items = items
          
                  self._quickTab = None  # Stores the quicktab custom GUI
                  self._tabList = {}  # Stores the TabName and the SubDialog that represents each tab of the QuickTab
          
              def _DrawQuickTabGroup(self):
                  """Creates and draws all the SubDialog for each tab, take care it does not hide these according to a selection state.
          
                  Returns: 
                      True if success otherwise False.
                  """
          
                  # Checks if the quicktab is defined
                  if self._quickTab is None:
                      return False
          
                  # Flush the content of the group that holds all ours SubDialogs
                  self.LayoutFlushGroup(ID_MAINGROUP)
                  self.GroupBorderSpace(left=5, top=5, right=5, bottom=5)
          
                  # Iterates over the number of tab to create and attach the correct SubDialog
                  for tabId, (tabName, tabGui) in enumerate(self._tabList.items()):
                      self.AddSubDialog(ID_QUICKTAB_BASE_GROUP + tabId, c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT, 0, 0)
                      self.AttachSubDialog(tabGui, ID_QUICKTAB_BASE_GROUP + tabId)
          
                  # Notifies the content of the MainGroup has changed
                  self.LayoutChanged(ID_MAINGROUP)
          
                  return True
          
              def GetActiveTabs(self):
                  """Retrieves two list of currently selected tabs from the self._quickTab.
          
                  Returns:
                      list(int), list(name): The first list, contains tabs Id (from self._quickTab the dict) and the second list contains all names of the selected tabs.
                  """
                  # Checks if the quicktab is defined
                  if self._quickTab is None:
                      return False, False
          
                  returnIds = []
                  returnNames = []
          
                  for tabId, (tabName, tabGui) in enumerate(self._tabList.items()):
                      if self._quickTab.IsSelected(tabId):
                          returnIds.append(tabId)
                          returnNames.append(tabName)
          
                  return returnIds, returnNames
          
              def DisplayCorrectGroup(self):
                  """Hides all unused groups and display the correct one.
          
                  Returns: 
                      True if success otherwise False.
                  """
                  # Retrieves the selected tab
                  activeIds, activeNames = self.GetActiveTabs()
          
                  # Iterates each CustomGui and defines if they are hidden or not
                  for tabId in range(len(self._tabList)):
                      toDisplay = tabId in activeIds
                      self.HideElement(ID_QUICKTAB_BASE_GROUP + tabId, not toDisplay)
          
                  # Notifies the content of the MainGroup has changed
                  self.LayoutChanged(ID_MAINGROUP)
                  return True
          
              def AppendTab(self, tabName, content, active=True):
                  """Appends a tab to the current quicktab with the associated content to be displayed.
          
                  Args:
                      tabName (str): The name the tab should have.
                      content (c4d.gui.SubDialog): The SubDialog to be drawn/linked when the tab is selected.
                      active (bool, optional): If True, the inserted tab will be selected. Defaults to True.
          
                  Returns:
                      True if success otherwise False.
                  """
                  # Checks if the quicktab is defined
                  if self._quickTab is None:
                      return False
          
                  # Adds the tab entry n the quicktab
                  self._quickTab.AppendString(len(self._tabList), tabName, False)
          
                  # Updates our current tabList with tabName and the Subdialog to be linked
                  self._tabList.update({tabName: content})
          
                  # Retrieves the current selected tab
                  previousActiveId, previousActiveName = self.GetActiveTabs()
          
                  # Draws the quicktab SubDialog (in order to have the new one drawn)
                  self._DrawQuickTabGroup()
          
                  # Defines the just added tab according state
                  self._quickTab.Select(len(self._tabList) - 1, active)
          
                  # Defines previous active tab
                  for tabId in previousActiveId:
                      self._quickTab.Select(tabId, True)
          
                  # Display only the selected tab and hides all others
                  self.DisplayCorrectGroup()
          
                  return True
          
              def FlushAllTabs(self):
                  """Removes all tabs and their content from the GUI.
          
                  Returns: 
                      True if success otherwise False.
                  """
                  # Checks if the quicktab is defined
                  if self._quickTab is None:
                      return False
          
                  # Removes all the tabs
                  self._quickTab.ClearStrings()
          
                  # Removes all the customGui
                  for tabId in range(len(self._tabList)):
                      self.RemoveElement(ID_QUICKTAB_BASE_GROUP + tabId)
          
                  # Reinitializes the stored tablist to an empty dict
                  self._tabList = {}
          
                  # Notifies the content of the MainGroup has changed
                  self.LayoutChanged(ID_MAINGROUP)
          
                  return True
          
              def RemoveTab(self, tabNameToRemove):
                  """Removes a tab by its name
          
                  Args:
                      tabNameToRemove (str): The tab to remove.
          
                  Returns:
                      True if success otherwise False.
                  """
                  # Checks if the quicktab is defined
                  if self._quickTab is None:
                      return False
          
                  # Copies the tabList
                  newDict = dict(self._tabList)
          
                  # Checks if the entry exist
                  if tabNameToRemove not in newDict:
                      return True
          
                  # Removes the entry we want to delete
                  del newDict[tabNameToRemove]
          
                  # Removes all groups
                  self.FlushAllTabs()
          
                  # Re-adds all the one from our copy
                  for tabName, tabGui in newDict.items():
                      self.AppendTab(tabName, tabGui)
          
                  return True
          
              def CreateLayout(self):
                  """This Method is called automatically when Cinema 4D Create the Layout (display) of the Dialog."""
          
                  # Creates a QuickTab Custom Gui
                  bc = c4d.BaseContainer()
                  #bc.SetInt32(c4d.QUICKTAB_BAR, 0) # (0=off, 1=on, 2=non-bold, 3=special separator look)
                  bc.SetBool(c4d.QUICKTAB_BAR, False)
                  #bc.SetBool(c4d.QUICKTAB_SPRINGINGFOLDERS, True) # if we can get link fields usefull
                  bc.SetBool(c4d.QUICKTAB_SHOWSINGLE, True)
                  bc.SetBool(c4d.QUICKTAB_NOMULTISELECT, False)
          
                  self.GroupBegin(next(plusone_id()), c4d.BFH_SCALEFIT | c4d.BFV_TOP | c4d.BFV_FIT, 0, 0, '', 0)
                  self._quickTab = self.AddCustomGui(ID_QUICKTAB_BAR, c4d.CUSTOMGUI_QUICKTAB, '', c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT, 0, 0, bc)
                  self.GroupBorderNoTitle(borderstyle=DEFAULT_BORDER_STYLE)
                  self.GroupBorderSpace(left=5, top=5, right=5, bottom=5)
                  self.GroupEnd()
          
                  # Creates a group that will contain all the group representing each tab
                  self.GroupBegin(ID_MAINGROUP, flags=c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT, cols=10, rows=10, title='')
                  self.GroupBorderNoTitle(borderstyle=DEFAULT_BORDER_STYLE)
                  self.GroupBorderSpace(left=5, top=5, right=5, bottom=5)
                  self.GroupEnd()
                  
                  # dummy group to spread vertically
                  if self.GroupBegin(id=next(plusone_id()),  flags=c4d.BFH_FIT | c4d.BFV_FIT): #cols=1, rows=1,
                      self.GroupBorderSpace(left=0, top=0, right=0, bottom=0)
                      self.GroupBorderNoTitle(borderstyle=DEFAULT_BORDER_STYLE)
                      self.AddStaticText(id=next(plusone_id()), flags=c4d.BFV_CENTER | c4d.BFV_SCALE | c4d.BFH_CENTER | c4d.BFH_SCALE, name="")
                      self.GroupEnd()
          
                  # Creates a group with button in order to do some operation with the QuickTab CustomGUI
                  if self.GroupBegin(next(plusone_id()), c4d.BFH_SCALEFIT | c4d.BFV_BOTTOM, 6, 1, '', 0):
                      self.AddButton(ID_LOADDEFAULT_MAT_ASIGN, c4d.BFH_SCALEFIT, name="Populate")
                      self.AddButton(BUTTON_PRINT_TEXT, c4d.BFH_SCALEFIT, name="Print text")
                      self.AddButton(BUTTON_PRINT_SELECTED, c4d.BFH_SCALEFIT, name="Print Selected")
                      self.AddButton(BUTTON_FLUSH_ALL, c4d.BFH_SCALEFIT, name="Flush All")
                      self.AddButton(BUTTON_ADD, c4d.BFH_SCALEFIT, name="Add")
                      self.AddButton(BUTTON_REMOVE, c4d.BFH_SCALEFIT, name="Remove")
                      self.GroupEnd()
                  
                  return True
          
              def InitValues(self):
                  """This Method is called automatically after the GUI is initialized."""
                  # Creates the first Tab
                  #cg1 = CustomGroup(["This is the first Tab", "Just dummy text here"])
                  #self.AppendTab("First Tab", cg1, True)
          
                  # Creates the second Tab
                  #cg2 = CustomGroup(["This is the second Tab", "Just another dummy text here"])
                  #self.AppendTab("Second Tab", cg2, False)
                  #return True
              
                  return super(MyDialog, self).InitValues()
          
              def Command(self, id, msg):
                  """This Method is called automatically when the user clicks on a gadget and/or changes its value this function will be called.
                   It is also called when a string menu item is selected.
          
                  Args:
                      id: The ID of the gadget that triggered the event.
                      msg: The original message container
          
                  Returns:
                      False if there was an error, otherwise True.
                  """
          
                  # If the user interacts with the quicktab, we make sure to display the CustomGUI linked to the active one
                  if id == ID_QUICKTAB_BAR and self._quickTab:
                      self.DisplayCorrectGroup()
                      return True
          
                  # Displays all the Tab name
                  if id == BUTTON_PRINT_TEXT:
                      print([key for key in self._tabList])
                      return True
          
                  # Displays the ID and name of the selected tab
                  if id == BUTTON_PRINT_SELECTED:
                      print(self.GetActiveTabs())
          
                  # Removes all tabs
                  if id == BUTTON_FLUSH_ALL:
                      self.FlushAllTabs()
          
                  # Adds a new Tab to the quicktab
                  if id == BUTTON_ADD:
                      cg3 = CustomGroup(["This is the third Tab"])
                      self.AppendTab("Third Tab", cg3, True)
          
                  # Removes the first tab of the quicktab
                  if id == BUTTON_REMOVE:
                      self.RemoveTab("First Tab")
          
                  if id == ID_LOADDEFAULT_MAT_ASIGN:
                      self.Populate()
          
                  return True
              
          
              def Populate(self):
                  doc = c4d.documents.GetActiveDocument()
                  directory, _ = os.path.split(__file__)
                  poart_json_file = os.path.join(directory, "default_asignment.json")
                  material_ids, materials_dict = read_parts_json(poart_json_file)
          
                  self.FlushAllTabs() # clear the GUI
          
                  print( str( material_ids))
                  print("-"*80)
          
                  for material_id in materials_dict:
                      part_list = materials_dict[material_id]
          
                      #print( str(material_id), str(part_list) )
          
                      ### Tabs
                      ##################################################
                      
                      tab_Content = CustomGroup(str(material_id), part_list)
                      self.AppendTab(str(material_id), tab_Content, False)
                  
                  print("-"*80)
                  c4d.EventAdd()    
                
          
          # Main function
          def main():
              
              if DEBUG:
                  c4d.CallCommand(13957)  # clear console
              # Initializes a QuickTabDialogExample Dialog
              diag = MyDialog()
          
              # Opens the Dialog in modal mode
              diag.Open(dlgtype=c4d.DLG_TYPE_MODAL_RESIZEABLE, defaultw=400, defaulth=400)
          
          # Execute main()
          if __name__ == '__main__':
              main()
          
          1 Reply Last reply Reply Quote 0
          • M
            m_adam
            last edited by m_adam

            Hi can you illustrate what "GUI disintegration" means with a picture. as with the latest 2023.1 I can't spot any issue.

            May I ask which version of Cinema 4D are you using and most important which Python version? You taged this topic as R20 and 2023 and R20 would mean Python 2.7, but you need to be careful since dictionary in python are guarantee to be ordered only since Python 3.6.

            Which I think is what cause your issue since with Cinema 4D 2023.1 I wasn't able to reproduce the "switch between CCCC and DDDD". So if you want to support version as old as R20 you will need a different data structure like tuple. For more information see Default ordered dictionaries in Python 2.7.x.

            Cheers,
            Maxime.

            MAXON SDK Specialist

            Development Blog, MAXON Registered Developer

            1 Reply Last reply Reply Quote 0
            • M
              mogh
              last edited by mogh

              Hi Maxime,

              GUI disintegration mostly happening when selecting first and last Tab ...

              tabs_gui_dissintegration_2.png

              tabs_gui_dissintegration.png

              I am troubleshooting this thing for 5 hours now and the outcome seems to stay the same, I slim-lined the code to draw less gui refreshs, printed the ids / tab names/ and content in several places, but the code and underlying list do not indicate CCCC on ID 10 - So it seems only the TABs are wrong not the data structure ...

              # json list
              ['KKKKKKKK', 'BBBBB 40', 'LLLLLLLLL', 'HHHHHHHH', 'GGGGGGG', 'IIIIII', 'JJJJJJJJJ', 'CCCCCCC', 'AAAAAAA 85', 'FFFFF', 'DDDDDD']
              --------------------------------------------------------------------------------
              #AppendTab
              _tabList:
              {'KKKKKKKK': <__main__.CustomGroup object at 0x000001FBD74848B8>, 'BBBBB 40': <__main__.CustomGroup object at 0x000001FBD7484948>, 'LLLLLLLLL': <__main__.CustomGroup object at 0x000001FBD7484A68>, 'HHHHHHHH': <__main__.CustomGroup object at 0x000001FBD74849D8>, 'GGGGGGG': <__main__.CustomGroup object at 0x000001FBD7484990>, 'IIIIII': <__main__.CustomGroup object at 0x000001FBD7484A20>, 'JJJJJJJJJ': <__main__.CustomGroup object at 0x000001FBD7C23C60>, 'CCCCCCC': <__main__.CustomGroup object at 0x000001FBDF91FCA8>, 'AAAAAAA 85': <__main__.CustomGroup object at 0x000001FBD7C23CF0>, 'FFFFF': <__main__.CustomGroup object at 0x000001FBDF91FC60>, 'DDDDDD': <__main__.CustomGroup object at 0x000001FBD7C23D80>}
              --------------------------------------------------------------------------------
              #_DrawQuickTabGroup
              (0, 'KKKKKKKK', ' layout inserted subdialog id-> ', 4000)
              (1, 'BBBBB 40', ' layout inserted subdialog id-> ', 4001)
              (2, 'LLLLLLLLL', ' layout inserted subdialog id-> ', 4002)
              (3, 'HHHHHHHH', ' layout inserted subdialog id-> ', 4003)
              (4, 'GGGGGGG', ' layout inserted subdialog id-> ', 4004)
              (5, 'IIIIII', ' layout inserted subdialog id-> ', 4005)
              (6, 'JJJJJJJJJ', ' layout inserted subdialog id-> ', 4006)
              (7, 'CCCCCCC', ' layout inserted subdialog id-> ', 4007)
              (8, 'AAAAAAA 85', ' layout inserted subdialog id-> ', 4008)
              (9, 'FFFFF', ' layout inserted subdialog id-> ', 4009)
              (10, 'DDDDDD', ' layout inserted subdialog id-> ', 4010)
              
              # GetActiveTabs for loop
              (0, 'KKKKKKKK')
              (1, 'BBBBB 40')
              (2, 'LLLLLLLLL')
              (3, 'HHHHHHHH')
              (4, 'GGGGGGG')
              (5, 'IIIIII')
              (6, 'JJJJJJJJJ')
              (7, 'CCCCCCC')
              (8, 'AAAAAAA 85')
              (9, 'FFFFF')
              (10, 'DDDDDD')
              
              ('GetActiveTabs id: ', [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
              ('GetActiveTabs names: ', ['KKKKKKKK', 'BBBBB 40', 'LLLLLLLLL', 'HHHHHHHH', 'GGGGGGG', 'IIIIII', 'JJJJJJJJJ', 'CCCCCCC', 'AAAAAAA 85', 'FFFFF', 'DDDDDD'])
              

              a few moments ago I tested briefly OrderedDict() and tuple with no change in outcome ...

              EDIT: ok so i missed the flushing, changing every {} to self._tabList = OrderedDict() seems top work - The GUI Problems (screenshot) remain ...

              I had the idea to link to # instead of names but that is not what the SDK example does it takes the names ?

              I am at 2023.1 at work and in my free time on R20.059
              I checked 2023.1 before I pester you here because I know you will limit support otherwise 🤷

              thank you for your time.
              mogh

              1 Reply Last reply Reply Quote 0
              • M
                mogh
                last edited by mogh

                Here is my latest Example Code, I will check for R2023.1 on Monday.
                selecting FFFF and DDDD results in GUI weirdness ... on R20

                Update: Also on 2023.1 I get this UI glitch ... selecting KKKKK / LLLLLL (last ones)

                Update 2: fiddled a little bit with the IDs (the class has an offset now so the ids are unique) and Layoutchange() no updated on the glitch when selecting the KKKK + X elements.

                2023-03-27-Window_000269.png

                import c4d # pyright: ignore[reportMissingImports]
                import json
                import os
                from collections import OrderedDict
                
                DEBUG = True
                
                # Ids used in our Dialog
                ID_MAINGROUP = 1000  # ID used for the Group that holds all the other group representing the tab content
                ID_QUICKTAB_BAR = 1001  # ID for the quicktab customGui
                
                ID_LOADDEFAULT_MAT_ASIGN = 1003
                ID_CREATE_MATERIALS = 1004
                BUTTON_PRINT_TEXT = 1005  # ID used for the Print text Button
                BUTTON_PRINT_SELECTED = 1006  # ID used for the Print Selected Button
                BUTTON_FLUSH_ALL = 1007  # ID used for the Flush All Button
                BUTTON_ADD = 1008  # ID used for the Add Button
                BUTTON_REMOVE = 1009  # ID used for the Remove Button
                
                # Id used in our SubDialog
                ID_QUICKTAB_BASE_GROUP = 5000
                
                # Defines the ID for the string to be displayed
                CUSTOM_GROUP_ID_TEXT_BASE = 4000  
                
                DEFAULT_FLAGS = c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT
                DEFAULT_SPACE = (5, 5, 5, 5)
                DEFAULT_BORDER_STYLE =c4d.BORDER_NONE
                
                LAYOUT_DEBUG = True
                if LAYOUT_DEBUG is True:
                    DEFAULT_BORDER_STYLE = c4d.BORDER_BLACK
                
                GENERATOR_START_ID = 2000
                def plusone_id():
                    n = GENERATOR_START_ID
                    while n < GENERATOR_START_ID+999:
                        yield n
                        n += 1
                
                some_json = '{"AAAAAAA 85": ["a part_num_1","a part_num_2","a part_num_3"],"BBBBB 40": ["b part_num_4","b part_num_5"],"CCCCCCC": ["c part_num_6","c part_num_7","c part_num_8","c part_num_9"],"DDDDDD": ["d part_num_6","d part_num_7","d part_num_8","d part_num_9"],"FFFFF": ["f Plane"],"GGGGGGG": ["g part_num_6","g part_num_7","g part_num_8","g part_num_9"],"HHHHHHHH": ["h part_num_6","h part_num_7","h part_num_8","h part_num_9"],"IIIIII": ["i part_num_6","i part_num_7","i part_num_8","i part_num_9"],"JJJJJJJJJ": ["j part_num_6","j part_num_7","j part_num_8","j part_num_9"],"KKKKKKKK": ["k part_num_6","k part_num_7","k part_num_8","k part_num_9"],"LLLLLLLLL": ["l part_num_6","l part_num_7","l part_num_8","l part_num_9"],"MMMMMM": ["m part_num_6","m part_num_7","m part_num_8","m part_num_9"]}'
                
                ### Json
                #####################################################################################
                
                def read_parts_json(json_file_path):
                    #with open(json_file_path) as f: data = json.load(f)
                    data = json.loads(some_json)
                
                    materials = {}
                    for material_id, part_numbers in data.items():
                        materials[material_id] = part_numbers
                    
                    material_ids = list(data.keys())
                    return material_ids, materials
                
                class CustomGroup(c4d.gui.SubDialog):
                    """A SubDialog to display the passed string, its used as example for the actual content of a Tab"""
                    
                    def __init__(self, material, parts, offset):
                
                        self._material = material
                        self._parts = '\n'.join(parts)
                        self.gui_id = CUSTOM_GROUP_ID_TEXT_BASE + offset
                
                    def CreateLayout(self):
                        
                        self.GroupBegin(self.gui_id + 4, flags=c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT, cols=1, rows=2, title=self.gui_id)
                        self.GroupBorder(DEFAULT_BORDER_STYLE)
                        self.GroupBorderSpace(left=5, top=5, right=5, bottom=5)
                        self.AddStaticText(self.gui_id + 1 , flags=c4d.BFH_LEFT | c4d.BFV_TOP | c4d.BFH_FIT | c4d.BFV_FIT, name=self._material)
                        #self.AddStaticText(self.gui_id + 2 , flags=c4d.BFH_LEFT | c4d.BFV_TOP | c4d.BFH_FIT | c4d.BFV_FIT, name=self.gui_id)
                        self.AddMultiLineEditText(self.gui_id + 3, flags=c4d.BFH_LEFT | c4d.BFV_TOP | c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT, initw=0, inith=0, style=0)
                        self.SetString(self.gui_id + 3, value=self._parts)
                        self.GroupEnd()
                        #for i, parts in enumerate(self._parts):
                        #    self.AddStaticText(self.gui_id + i, c4d.BFH_SCALEFIT, name=parts)
                        return True
                
                
                class MyDialog(c4d.gui.GeDialog):
                
                    def __init__(self, items=[]):
                
                        # this is from the linkbox example from ferdinand
                        super(MyDialog, self).__init__() 
                
                        # will be link box
                        self._items = [] 
                        self._doc = None 
                        self._hasCreateLayout = False
                        self.Items = items
                
                        self._quickTab = None  # Stores the quicktab custom GUI
                        self._tabList = OrderedDict()  # Stores the TabName and the SubDialog that represents each tab of the QuickTab
                
                    def _DrawQuickTabGroup(self):
                        """ Creates and draws all the SubDialog for each tab, 
                            take care it does not hide these according to a selection state.
                
                        Returns: 
                            True if success otherwise False.
                        """
                
                        # Checks if the quicktab is defined
                        if self._quickTab is None:
                            return False
                
                        # Flush the content of the group that holds all ours SubDialogs
                        self.LayoutFlushGroup(ID_MAINGROUP)
                        #self.GroupBorderSpace(left=5, top=5, right=5, bottom=5)
                
                        # Iterates over the number of tab to create and attach the correct SubDialog
                        for tabId, (tabName, tabGui) in enumerate(self._tabList.items()):
                            self.AddSubDialog(ID_QUICKTAB_BASE_GROUP + tabId, c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT, 0, 0)
                            self.AttachSubDialog(tabGui, ID_QUICKTAB_BASE_GROUP + tabId)
                
                        # Notifies the content of the MainGroup has changed
                        self.LayoutChanged(ID_MAINGROUP)
                
                        return True
                
                
                    def GetActiveTabs(self):
                        """Retrieves two list of currently selected tabs from the self._quickTab.
                
                        Returns:
                            list(int), list(name): The first list, contains tabs Id (from self._quickTab the dict) and the second list contains all names of the selected tabs.
                        """
                        # Checks if the quicktab is defined
                        if self._quickTab is None:
                            return False, False
                
                        returnIds = []
                        returnNames = []
                
                        for tabId, (tabName, tabGui) in enumerate(self._tabList.items()):
                            if self._quickTab.IsSelected(tabId):
                                returnIds.append(tabId)
                                returnNames.append(tabName)
                
                        return returnIds, returnNames
                
                
                    def DisplayCorrectGroup(self):
                        """Hides all unused groups and display the correct one.
                
                        Returns: 
                            True if success otherwise False.
                        """
                        # Retrieves the selected tab
                        activeIds, activeNames = self.GetActiveTabs()
                        
                        print("Display Correct Group")
                        print(activeIds)
                
                        # Iterates each CustomGui and defines if they are hidden or not
                        for tabId in range(len(self._tabList)):
                            toDisplay = tabId in activeIds
                            #print("activeIds: ", activeIds, "current: ", ID_QUICKTAB_BASE_GROUP, tabId, " -> toDisplay: ", toDisplay)
                            self.HideElement(ID_QUICKTAB_BASE_GROUP + tabId, not toDisplay)
                
                
                        # Notifies the content of the MainGroup has changed
                        self.LayoutChanged(ID_MAINGROUP)
                        
                        return True
                
                    def AppendTab(self, tabName, content, active=True):
                        """Appends a tab to the current quicktab with the associated content to be displayed.
                
                        Args:
                            tabName (str): The name the tab should have.
                            content (c4d.gui.SubDialog): The SubDialog to be drawn/linked when the tab is selected.
                            active (bool, optional): If True, the inserted tab will be selected. Defaults to True.
                
                        Returns:
                            True if success otherwise False.
                        """
                        # Checks if the quicktab is defined
                        if self._quickTab is None:
                            return False
                
                        # Adds the tab entry n the quicktab
                        self._quickTab.AppendString(len(self._tabList), tabName, active)
                
                        # Updates our current tabList with tabName and the Subdialog to be linked
                        self._tabList.update({tabName: content})
                
                        """ I uncommented below because it resulted in a lot of gui calls 
                            It seems to work fine just calling self.DisplayCorrectGroup() at the right places
                            not inside here hence this functionis called for each tab
                        """
                
                        # Retrieves the current selected tab
                        #previousActiveId, previousActiveName = self.GetActiveTabs()
                
                        # Draws the quicktab SubDialog (in order to have the new one drawn)
                        #self._DrawQuickTabGroup()
                
                        # Defines the just added tab according state
                        #self._quickTab.Select(len(self._tabList) - 1, active)
                
                        # Defines previous active tab
                        #for tabId in previousActiveId: self._quickTab.Select(tabId, True)
                
                        # Display only the selected tab and hides all others
                        #self.DisplayCorrectGroup()
                
                        return True
                
                    def FlushAllTabs(self):
                        """Removes all tabs and their content from the GUI.
                
                        Returns: 
                            True if success otherwise False.
                        """
                        # Checks if the quicktab is defined
                        if self._quickTab is None:
                            return False
                
                        # Removes all the tabs
                        self._quickTab.ClearStrings()
                
                        # Removes all the customGui
                        for tabId in range(len(self._tabList)):
                            self.RemoveElement(ID_QUICKTAB_BASE_GROUP + tabId)
                
                        # Reinitializes the stored tablist to an empty dict
                        self._tabList = OrderedDict()
                
                        # Flush the content of the group that holds all ours SubDialogs
                        self.LayoutFlushGroup(ID_MAINGROUP)
                        # Notifies the content of the MainGroup has changed
                        self.LayoutChanged(ID_MAINGROUP)
                
                        return True
                
                    def RemoveTab(self, tabNameToRemove):
                        """Removes a tab by its name
                
                        Args:
                            tabNameToRemove (str): The tab to remove.
                
                        Returns:
                            True if success otherwise False.
                        """
                        # Checks if the quicktab is defined
                        if self._quickTab is None:
                            return False
                
                        # Copies the tabList
                        newDict = OrderedDict(self._tabList)
                
                        # Checks if the entry exist
                        if tabNameToRemove not in newDict:
                            return True
                
                        # Removes the entry we want to delete
                        del newDict[tabNameToRemove]
                
                        # Removes all groups
                        self.FlushAllTabs()
                
                        # Re-adds all the one from our copy
                        for tabName, tabGui in newDict.items():
                            self.AppendTab(tabName, tabGui)
                
                        return True
                
                    def CreateLayout(self):
                        """This Method is called automatically when Cinema 4D Create the Layout (display) of the Dialog."""
                
                        # Creates a QuickTab Custom Gui
                        bc = c4d.BaseContainer()
                        bc.SetInt32(c4d.QUICKTAB_BAR, 0) # (0=off, 1=on, 2=non-bold, 3=special separator look)
                        bc.SetString(c4d.QUICKTAB_BARTITLE, "Title")
                        bc.SetBool(c4d.QUICKTAB_SPRINGINGFOLDERS, True) # if we can get link fields usefull
                        bc.SetBool(c4d.QUICKTAB_SHOWSINGLE, False)
                        bc.SetBool(c4d.QUICKTAB_NOMULTISELECT, False)
                        bc.SetBool(c4d.QUICKTAB_SEPARATOR, True)
                
                        self.GroupBegin(next(plusone_id()), c4d.BFH_SCALEFIT | c4d.BFV_TOP | c4d.BFV_FIT, 0, 0, 'ID_QUICKTAB_BAR', 0)
                        #self.GroupBorderNoTitle(borderstyle=DEFAULT_BORDER_STYLE)
                        self.GroupBorder(DEFAULT_BORDER_STYLE)
                        self.GroupBorderSpace(left=5, top=5, right=5, bottom=5)
                        self._quickTab = self.AddCustomGui(ID_QUICKTAB_BAR, c4d.CUSTOMGUI_QUICKTAB, 'the tabs', c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT, 0, 0, bc)
                        #self.AddStaticText(id=next(plusone_id()), flags=c4d.BFV_CENTER | c4d.BFV_SCALE | c4d.BFH_CENTER | c4d.BFH_SCALE, name="tabs")
                        #print("layout cusrom gui: ", self._quickTab)
                        self.GroupEnd()
                
                        # Creates a group that will contain all the group representing each tab
                        self.GroupBegin(ID_MAINGROUP, flags=c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT, cols=10, rows=10, title='ID_MAINGROUP')
                        #self.GroupBorderNoTitle(borderstyle=DEFAULT_BORDER_STYLE)
                        self.GroupBorder(DEFAULT_BORDER_STYLE)
                        self.GroupBorderSpace(left=5, top=5, right=5, bottom=5)
                        self.AddStaticText(id=next(plusone_id()), flags=c4d.BFV_CENTER | c4d.BFV_SCALE | c4d.BFH_CENTER | c4d.BFH_SCALE, name="main")
                        self.GroupEnd()
                        
                        # dummy group to spread vertically
                        if self.GroupBegin(id=next(plusone_id()),  flags=c4d.BFH_FIT | c4d.BFV_FIT): #cols=1, rows=1,
                            self.GroupBorderSpace(left=0, top=0, right=0, bottom=0)
                            self.GroupBorderNoTitle(borderstyle=DEFAULT_BORDER_STYLE)
                            self.AddStaticText(id=next(plusone_id()), flags=c4d.BFV_CENTER | c4d.BFV_SCALE | c4d.BFH_CENTER | c4d.BFH_SCALE, name="")
                            self.GroupEnd()
                
                        # Creates a group with button in order to do some operation with the QuickTab CustomGUI
                        if self.GroupBegin(next(plusone_id()), c4d.BFH_SCALEFIT | c4d.BFV_BOTTOM, 4, 1, '', 0):
                            self.AddButton(ID_LOADDEFAULT_MAT_ASIGN, c4d.BFH_SCALEFIT, name="Populate")
                            self.AddButton(BUTTON_PRINT_TEXT, c4d.BFH_SCALEFIT, name="Print text")
                            self.AddButton(BUTTON_PRINT_SELECTED, c4d.BFH_SCALEFIT, name="Print Selected")
                            self.AddButton(BUTTON_FLUSH_ALL, c4d.BFH_SCALEFIT, name="Flush All")
                            #self.AddButton(BUTTON_ADD, c4d.BFH_SCALEFIT, name="Add")
                            #self.AddButton(BUTTON_REMOVE, c4d.BFH_SCALEFIT, name="Remove")
                            self.GroupEnd()
                        
                        return True
                
                    def InitValues(self):
                        """This Method is called automatically after the GUI is initialized."""
                        # Creates the first Tab
                        #cg1 = CustomGroup(["This is the first Tab", "Just dummy text here"])
                        #self.AppendTab("First Tab", cg1, True)
                
                        # Creates the second Tab
                        #cg2 = CustomGroup(["This is the second Tab", "Just another dummy text here"])
                        #self.AppendTab("Second Tab", cg2, False)
                        #return True
                    
                        return super(MyDialog, self).InitValues()
                
                    def Command(self, id, msg):
                        """This Method is called automatically when the user clicks on a gadget and/or changes its value this function will be called.
                         It is also called when a string menu item is selected.
                
                        Args:
                            id: The ID of the gadget that triggered the event.
                            msg: The original message container
                
                        Returns:
                            False if there was an error, otherwise True.
                        """
                
                        # If the user interacts with the quicktab, we make sure to display the CustomGUI linked to the active one
                        if id == ID_QUICKTAB_BAR and self._quickTab:
                            print("user interacted with tab")
                            self.DisplayCorrectGroup()
                            c4d.EventAdd()
                            return True
                
                        # Displays all the Tab name
                        if id == BUTTON_PRINT_TEXT:
                            print([key for key in self._tabList])
                            return True
                
                        # Displays the ID and name of the selected tab
                        if id == BUTTON_PRINT_SELECTED:
                            print(self.GetActiveTabs())
                
                        # Removes all tabs
                        if id == BUTTON_FLUSH_ALL:
                            self.FlushAllTabs()
                
                        """        
                        # Adds a new Tab to the quicktab
                        if id == BUTTON_ADD:
                            cg3 = CustomGroup(["This is the third Tab"])
                            self.AppendTab("Third Tab", cg3, True)
                
                        # Removes the first tab of the quicktab
                        if id == BUTTON_REMOVE:
                            self.RemoveTab("First Tab")
                        """
                
                        if id == ID_LOADDEFAULT_MAT_ASIGN:
                            self.Populate()
                
                        return True
                    
                
                    def Populate(self):
                        doc = c4d.documents.GetActiveDocument()
                        directory, _ = os.path.split(__file__)
                        poart_json_file = os.path.join(directory, "default_asignment.json")
                        material_ids, materials_dict = read_parts_json(poart_json_file)
                
                        self.FlushAllTabs() # clear the GUI
                
                        print( str( material_ids))
                        print("-"*80)
                        
                        offset=0
                        
                        for material_id in materials_dict:
                            part_list = materials_dict[material_id]
                
                            #print( str(material_id), str(part_list) )
                
                            ### Tabs
                            ##############################################
                            
                            tab_Content = CustomGroup(str(material_id), part_list, offset)
                            self.AppendTab(str(material_id), tab_Content, True)
                            offset += 10
                        
                        print("-"*80)
                        self._DrawQuickTabGroup()
                        c4d.EventAdd()    
                      
                
                # Main function
                def main():
                    
                    if DEBUG:
                        c4d.CallCommand(13957)  # clear console
                    
                    # Initializes a QuickTabDialogExample Dialog
                    diag = MyDialog()
                
                    # Opens the Dialog in modal mode
                    diag.Open(dlgtype=c4d.DLG_TYPE_MODAL_RESIZEABLE, defaultw=960, defaulth=600)
                
                
                # Execute main()
                if __name__ == '__main__':
                    main()
                
                1 Reply Last reply Reply Quote 1
                • M
                  m_adam
                  last edited by

                  Hi @mogh sorry for the delay, I can indeed confirm the bug and reported it so far I don't have any other workaround than flushing the whole group and re-add everything, which can be very slow.

                  Cheers,
                  Maxime.

                  MAXON SDK Specialist

                  Development Blog, MAXON Registered Developer

                  1 Reply Last reply Reply Quote 0
                  • M
                    mogh
                    last edited by mogh

                    Ok Maxime, thanks for the heads up,

                    I might just display all all the time ....
                    or build a Classic Tab version ... (is there an example ?)

                    Barebone classic Tab:

                    import c4d
                    
                    DEBUG = True
                    
                    DEFAULT_FLAGS = c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT
                    DEFAULT_SPACE = (5, 5, 5, 5)
                    DEFAULT_BORDER_STYLE = c4d.BORDER_NONE
                    
                    DEFAULT_TEXT_FLAGS = c4d.BFV_CENTER | c4d.BFV_FIT | c4d.BFH_CENTER | c4d.BFH_FIT
                    
                    class MyDialog(c4d.gui.GeDialog):
                    
                        def CreateLayout(self):
                            
                            if self.GroupBegin(id=950,  flags=c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT):
                                self.GroupBorderSpace(left=5, top=5, right=5, bottom=5)
                                self.GroupBorderNoTitle(borderstyle=DEFAULT_BORDER_STYLE)
                            
                                if self.TabGroupBegin(id=1000, flags=c4d.BFH_LEFT | c4d.BFH_SCALEFIT | c4d.BFV_CENTER | c4d.BFV_SCALEFIT, tabtype=c4d.TAB_TABS):
                                    self.GroupBorderSpace(left=5, top=5, right=5, bottom=5)
                        
                                    if self.GroupBegin(id=1001, cols=1, rows=1, flags=c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT, title="Tab 1"):
                                        self.GroupBorderSpace(left=5, top=5, right=5, bottom=5)
                                        self.GroupBorderNoTitle(borderstyle=DEFAULT_BORDER_STYLE)
                                        self.AddStaticText(id=2001, flags=DEFAULT_TEXT_FLAGS, name="some content 1")
                                        self.GroupEnd()
                                    
                                    if self.GroupBegin(id=1002, cols=1, rows=1, flags=c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT, title="Tab 2"):
                                        self.GroupBorderSpace(left=5, top=5, right=5, bottom=5)
                                        self.GroupBorderNoTitle(borderstyle=DEFAULT_BORDER_STYLE)
                                        self.AddStaticText(id=2002, flags=DEFAULT_TEXT_FLAGS, name="some content 2")
                                        self.GroupEnd()        
                                    
                                    if self.GroupBegin(id=1003, cols=1, rows=1, flags=c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT, title="Tab 3 long name for test"):
                                        self.GroupBorderSpace(left=5, top=5, right=5, bottom=5)
                                        self.GroupBorderNoTitle(borderstyle=DEFAULT_BORDER_STYLE)
                                        self.AddStaticText(id=2003, flags=DEFAULT_TEXT_FLAGS, name="some content 3")
                                        self.GroupEnd()                    
                    
                                    self.GroupEnd()    # tab group
                    
                                self.GroupEnd() # main window
                    
                            return True
                    
                    
                    def main():
                        
                        if DEBUG:
                            c4d.CallCommand(13957)  # clear console
                        
                        diag = MyDialog()
                    
                        diag.Open(dlgtype=c4d.DLG_TYPE_MODAL_RESIZEABLE, defaultw=960, defaulth=600)
                    
                    
                    if __name__ == '__main__':
                        main()
                    

                    kind regards ...
                    mogh

                    1 Reply Last reply Reply Quote 0
                    • M
                      m_adam
                      last edited by m_adam

                      While rethinking about it last night, it may not be that slow if on user interaction you just flush the main group and re-attach already existing dialog.

                      So you need to modify your `def _DrawQuickTabGroup(self)`` method like so to only display visible dialog:

                          def _DrawQuickTabGroup(self, onlyVisible=True):
                              """ Creates and draws all the SubDialog for each tab, 
                                  take care it does not hide these according to a selection state.
                      
                              Returns: 
                                  True if success otherwise False.
                              """
                      
                              # Checks if the quicktab is defined
                              if self._quickTab is None:
                                  return False
                      
                              activeIds, activeNames = self.GetActiveTabs()
                      
                              # Flush the content of the group that holds all ours SubDialogs
                              self.LayoutFlushGroup(ID_MAINGROUP)
                              #self.GroupBorderSpace(left=5, top=5, right=5, bottom=5)
                      
                              # Iterates over the number of tab to create and attach the correct SubDialog
                              for tabId, (tabName, tabGui) in enumerate(self._tabList.items()):
                                  toDisplay = tabId in activeIds
                                  if not toDisplay:
                                      continue
                                  self.AddSubDialog(ID_QUICKTAB_BASE_GROUP + tabId, c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT, 0, 0)
                                  self.AttachSubDialog(tabGui, ID_QUICKTAB_BASE_GROUP + tabId)
                      
                              self.LayoutChanged(ID_MAINGROUP)
                      
                              return True
                      

                      Then in the Command message instead of calling DisplayCorrectGroup you should call _DrawQuickTabGroup like so:

                          def Command(self, id, msg):
                              if id == ID_QUICKTAB_BAR and self._quickTab:
                                  self._DrawQuickTabGroup()
                                  return True
                      

                      Regarding your question and classic Tab, find an example in Remove a GeDialog Tab Group but as you can see this is also not possible to remove a tab without redrawing everything .

                      Cheers,
                      Maxime.

                      MAXON SDK Specialist

                      Development Blog, MAXON Registered Developer

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