hybridobject Posted March 25, 2021 Share Posted March 25, 2021 Hello. I'm having trouble figuring something out with a dialog that I am building. Hope someone here might be able to shed some light on what I am doing wrong Version Info: VWX 2021 SP2.1.1 R1 (Build 580007) (64Bit) on Win10 and MacOs Script: The script I am attaching here is a simplified version of my actual script that I am using this dialog in. The actual script's purpose is to present the user with a dialog that lets him choose a symbol folder from the document's resources (pulldown). This selection triggers the listbox to be populated with the names of all record formats that are connected with the symbols in the selected folder. The user can also check a box to include possible subfolders of the selected symbol folder. When the user changes his selection or checks/unchecks the subfolder option I first clear the listbox of old items before re-populating it with new items (record format names) based on the new user selection. So in total, the user selects a symbol folder (optionally incl. subfolders) and a record format which the script than uses to write a csv file that can be used to edit complex record entries for a multitude of symbols in Excel. This data edited in Excel can then be reimported to update the record formats of all symbol definitions in the selected symbol folder. To present my problem without any fat attached I have isolated the dialog and use dummy data from hardcoded lists for populating the dialog elements. Problems: 1. I have to call my clearListBox function 2 times to fully clear the listbox of previous items. I'm using vs.GetChoiceCount to get the current number of items in the listbox and then run a 'for' loop with vs.RemoveChoice to remove the items from the listbox. If I am running this only once (which I think should suffice) some old items remain in the listbox, which is the reason I am calling the function twice. (See event functions) 2. The second and bigger problem I have is that triggering the call of my clearListBox function causes Vectorworks to crash to Desktop on MacOs. On Win10 it works fine. (The crash on MacOs mostly happens on the second triggering of the event. So you can check the box to include subfolders but a following uncheck crashes vwx.) Attempts for solution so far: I suspected there might be a problem with the checkbox dialog component on MacOs that caused the crash to desktop. I therefore made another version of the dialog with radio buttons (yes / no) to see if the problem disappears. Sadly both attempts work only on Windows but crash Vectorworks on MacOs. I therefore think there must be something wrong with the way I am handling the clearing of the listbox but cant figure out what it is. Since my solution for clearing the listbox (see problem 1) by simply doing it twice is kinda hacky I also thought this might cause the problem but the amount of times I'm calling the function does not change anything about the crash on MacOs. Searched the interwebs and this forum for the problem without success. I would really appreciate you help. Checkbox version of the dialog: def control_IDs(): global SetupDialogC global groupBoxSF_ID global pullDownSF_ID global checkBox_ID global listBox_ID SetupDialogC = 12255 groupBoxSF_ID = 29 pullDownSF_ID = 30 checkBox_ID = 31 listBox_ID = 32 return def DialogHandler(item, data): if item == SetupDialogC: initializeDialog() elif item == pullDownSF_ID: pullDownEvent(item, data) elif item == checkBox_ID: checkBoxEvent(item, data) elif item == 1: # if 'OK' clicked pass elif item == 2: # if 'Cancel' clicked pass return def Dialog(): global dialog_ID dialog_ID = vs.CreateLayout("Dialog Title", False, "OK", "Cancel") vs.CreateGroupBox(dialog_ID, groupBoxSF_ID, "Select Symbol Folder", True) vs.CreatePullDownMenu(dialog_ID, pullDownSF_ID, 64) vs.CreateCheckBox(dialog_ID, checkBox_ID, "Include Subfolders ?") vs.CreateListBox(dialog_ID, listBox_ID, 64, 10) vs.SetFirstGroupItem(dialog_ID, groupBoxSF_ID, pullDownSF_ID) vs.SetBelowItem(dialog_ID, pullDownSF_ID, checkBox_ID, 0, 0) vs.SetBelowItem(dialog_ID, checkBox_ID, listBox_ID, 0, 0) vs.SetFirstLayoutItem(dialog_ID, groupBoxSF_ID) return vs.RunLayoutDialog(dialog_ID, DialogHandler) def initializeDialog(): symFNameList = ["Folder-Truss", "Folder-Lights", "Folder-Cable"] counter = 0 vs.AddChoice(dialog_ID, pullDownSF_ID, "-- Default --", counter) counter = counter + 1 for name in symFNameList: vs.AddChoice(dialog_ID, pullDownSF_ID, name, counter) counter = counter + 1 return def pullDownEvent(item, data): symDRecNameList = ["Recordname-1", "Recordname-2", "Recordname-3"] symDRecNameList2 = ["Recordname-4", "Recordname-5", "Recordname-6"] symDRecNameList3 = ["Recordname-7", "Recordname-8", "Recordname-9"] sfSelectedIndex, sfSelectedChoiceText = vs.GetSelectedChoiceInfo(dialog_ID, pullDownSF_ID, 0) for i in range(2): clearListBox() if sfSelectedChoiceText == "Folder-Truss": for i in range(len(symDRecNameList)): vs.AddChoice(dialog_ID, listBox_ID, symDRecNameList[i], i) elif sfSelectedChoiceText == "Folder-Lights": for i in range(len(symDRecNameList2)): vs.AddChoice(dialog_ID, listBox_ID, symDRecNameList2[i], i) elif sfSelectedChoiceText == "Folder-Cable": for i in range(len(symDRecNameList3)): vs.AddChoice(dialog_ID, listBox_ID, symDRecNameList3[i], i) return def checkBoxEvent(item, data): sfSelectedIndex, sfSelectedChoiceText = vs.GetSelectedChoiceInfo(dialog_ID, pullDownSF_ID, 0) lbSelectedIndex, lbSelectedChoiceText = vs.GetSelectedChoiceInfo(dialog_ID, listBox_ID, 0) for i in range(2): clearListBox() #vs.AlrtDialog(f"Index : {sfSelectedIndex}\nText : {sfSelectedChoiceText}") return def clearListBox(): listBoxChoiceCnt = vs.GetChoiceCount(dialog_ID, listBox_ID) for i in range(listBoxChoiceCnt): vs.RemoveChoice(dialog_ID, listBox_ID, i) return control_IDs() Dialog() Radiobutton version of the dialog: def control_IDs(): global SetupDialogC global groupBoxSF_ID global pullDownSF_ID global radioBtnGrpBx_ID global radioBtn1_ID global radioBtn2_ID global listBox_ID SetupDialogC = 12255 groupBoxSF_ID = 29 pullDownSF_ID = 30 radioBtnGrpBx_ID = 31 radioBtn1_ID = 32 radioBtn2_ID = 33 listBox_ID = 34 return def DialogHandler(item, data): if item == SetupDialogC: initializeDialog() elif item == pullDownSF_ID: pullDownEvent(item, data) #vs.AlrtDialog(f"Pulldown SF - Item : {item} Data : {data}") elif item == radioBtn1_ID: radioBtn1Event(item, data) #vs.AlrtDialog(f"RadioBtn1 - Item : {item} Data : {data}") elif item == radioBtn2_ID: radioBtn2Event(item, data) #vs.AlrtDialog(f"RadioBtn2 - Item : {item} Data : {data}") elif item == 1: # if 'OK' clicked pass elif item == 2: # if 'Cancel' clicked pass return def Dialog(): global dialog_ID dialog_ID = vs.CreateLayout("Dialog Title", False, "OK", "Cancel") vs.CreateGroupBox(dialog_ID, groupBoxSF_ID, "Select Symbol Folder", True) vs.CreatePullDownMenu(dialog_ID, pullDownSF_ID, 64) vs.CreateGroupBox(dialog_ID, radioBtnGrpBx_ID, "Include Subfolders ? ", True) vs.CreateRadioButton(dialog_ID, radioBtn1_ID, "Yes") vs.CreateRadioButton(dialog_ID, radioBtn2_ID, "No") vs.CreateListBox(dialog_ID, listBox_ID, 64, 10) vs.SetFirstGroupItem(dialog_ID, radioBtnGrpBx_ID, radioBtn1_ID) vs.SetRightItem(dialog_ID, radioBtn1_ID, radioBtn2_ID, 10, 0) vs.SetFirstGroupItem(dialog_ID, groupBoxSF_ID, pullDownSF_ID) vs.SetBelowItem(dialog_ID, pullDownSF_ID, radioBtnGrpBx_ID, 0, 0) vs.SetBelowItem(dialog_ID, radioBtnGrpBx_ID, listBox_ID, 0, 0) vs.SetFirstLayoutItem(dialog_ID, groupBoxSF_ID) return vs.RunLayoutDialog(dialog_ID, DialogHandler) def initializeDialog(): vs.SetBooleanItem(dialog_ID, radioBtn1_ID, False) vs.SetBooleanItem(dialog_ID, radioBtn2_ID, True) symFNameList = ["Folder-Truss", "Folder-Lights", "Folder-Cable"] counter = 0 vs.AddChoice(dialog_ID, pullDownSF_ID, "-- Default --", counter) counter = counter + 1 for name in symFNameList: vs.AddChoice(dialog_ID, pullDownSF_ID, name, counter) counter = counter + 1 return def pullDownEvent(item, data): symDRecNameList = ["Recordname-1", "Recordname-2", "Recordname-3"] symDRecNameList2 = ["Recordname-4", "Recordname-5", "Recordname-6"] symDRecNameList3 = ["Recordname-7", "Recordname-8", "Recordname-9"] sfSelectedIndex, sfSelectedChoiceText = vs.GetSelectedChoiceInfo(dialog_ID, pullDownSF_ID, 0) for i in range(2): clearListBox() if sfSelectedChoiceText == "Folder-Truss": for i in range(len(symDRecNameList)): vs.AddChoice(dialog_ID, listBox_ID, symDRecNameList[i], i) elif sfSelectedChoiceText == "Folder-Lights": for i in range(len(symDRecNameList2)): vs.AddChoice(dialog_ID, listBox_ID, symDRecNameList2[i], i) elif sfSelectedChoiceText == "Folder-Cable": for i in range(len(symDRecNameList3)): vs.AddChoice(dialog_ID, listBox_ID, symDRecNameList3[i], i) return def radioBtn1Event(item, data): sfSelectedIndex, sfSelectedChoiceText = vs.GetSelectedChoiceInfo(dialog_ID, pullDownSF_ID, 0) lbSelectedIndex, lbSelectedChoiceText = vs.GetSelectedChoiceInfo(dialog_ID, listBox_ID, 0) for i in range(2): clearListBox() #vs.AlrtDialog(f"Index : {sfSelectedIndex}\nText : {sfSelectedChoiceText}") return def radioBtn2Event(item, data): sfSelectedIndex, sfSelectedChoiceText = vs.GetSelectedChoiceInfo(dialog_ID, pullDownSF_ID, 0) lbSelectedIndex, lbSelectedChoiceText = vs.GetSelectedChoiceInfo(dialog_ID, listBox_ID, 0) for i in range(2): clearListBox() #vs.AlrtDialog(f"Index : {sfSelectedIndex}\nText : {sfSelectedChoiceText}") return def clearListBox(): listBoxChoiceCnt = vs.GetChoiceCount(dialog_ID, listBox_ID) for i in range(listBoxChoiceCnt): vs.RemoveChoice(dialog_ID, listBox_ID, i) return control_IDs() Dialog() Quote Link to comment
JBenghiat Posted March 25, 2021 Share Posted March 25, 2021 The problem is that when you remove choices, each removal changes the list. For example, if you have n choices, removing choice 0, you now have n-1 choices. Removing choice 1, you have n-2 choices, etc. About half way through the range, you'll be trying to remove choices that don't exist. Two options: You can remove items from last to first, which preserves the list as you remove options. You can remove choice 0 n times. That essentially pops the front choice off the list until the length of the list is zero. Separately, any actions in python, even if they are illegal, should produce an error message and not crash Vectorworks. I recommend also reporting this as a bug. 1 Quote Link to comment
hybridobject Posted March 25, 2021 Author Share Posted March 25, 2021 Thank you very much @JBenghiat . Now that you´ve laid it out for me it seems so obvious . I've applied your second suggestion, to remove choice 0 n times, and this has solved my first problem. I can now clear the listbox with just a single function call, which is great and I feel like less of a cheater.... Tomorrow I will check if this fix also resolves the issue of vwx crashing on macOS. I'll report back. Regarding your recommendation to report this as a bug, I guess https://www.vectorworks.net/support/bugsubmit would probably the right place to that? Once I've done the test on mac I'll make sure to write a bug report. Thank you so much!!! Quote Link to comment
hybridobject Posted March 26, 2021 Author Share Posted March 26, 2021 Happy to report that the fix @JBenghiat suggested also solves my second problem of the script crashing vwx to desktop on macOS. I reported the behaviour as a bug as recommended. For anybody wanting to just copy the working code: Checkbox version of the dialog: def control_IDs(): global SetupDialogC global groupBoxSF_ID global pullDownSF_ID global checkBox_ID global listBox_ID SetupDialogC = 12255 groupBoxSF_ID = 29 pullDownSF_ID = 30 checkBox_ID = 31 listBox_ID = 32 return def DialogHandler(item, data): if item == SetupDialogC: initializeDialog() elif item == pullDownSF_ID: pullDownEvent(item, data) elif item == checkBox_ID: checkBoxEvent(item, data) elif item == 1: # if 'OK' clicked pass elif item == 2: # if 'Cancel' clicked pass return def Dialog(): global dialog_ID dialog_ID = vs.CreateLayout("Dialog Title", False, "OK", "Cancel") vs.CreateGroupBox(dialog_ID, groupBoxSF_ID, "Select Symbol Folder", True) vs.CreatePullDownMenu(dialog_ID, pullDownSF_ID, 64) vs.CreateCheckBox(dialog_ID, checkBox_ID, "Include Subfolders ?") vs.CreateListBox(dialog_ID, listBox_ID, 64, 10) vs.SetFirstGroupItem(dialog_ID, groupBoxSF_ID, pullDownSF_ID) vs.SetBelowItem(dialog_ID, pullDownSF_ID, checkBox_ID, 0, 0) vs.SetBelowItem(dialog_ID, checkBox_ID, listBox_ID, 0, 0) vs.SetFirstLayoutItem(dialog_ID, groupBoxSF_ID) return vs.RunLayoutDialog(dialog_ID, DialogHandler) def initializeDialog(): symFNameList = ["Folder-Truss", "Folder-Lights", "Folder-Cable"] counter = 0 vs.AddChoice(dialog_ID, pullDownSF_ID, "-- Default --", counter) counter = counter + 1 for name in symFNameList: vs.AddChoice(dialog_ID, pullDownSF_ID, name, counter) counter = counter + 1 return def pullDownEvent(item, data): symDRecNameList = ["Recordname-1", "Recordname-2", "Recordname-3"] symDRecNameList2 = ["Recordname-4", "Recordname-5", "Recordname-6"] symDRecNameList3 = ["Recordname-7", "Recordname-8", "Recordname-9"] sfSelectedIndex, sfSelectedChoiceText = vs.GetSelectedChoiceInfo(dialog_ID, pullDownSF_ID, 0) clearListBox() if sfSelectedChoiceText == "Folder-Truss": for i in range(len(symDRecNameList)): vs.AddChoice(dialog_ID, listBox_ID, symDRecNameList[i], i) elif sfSelectedChoiceText == "Folder-Lights": for i in range(len(symDRecNameList2)): vs.AddChoice(dialog_ID, listBox_ID, symDRecNameList2[i], i) elif sfSelectedChoiceText == "Folder-Cable": for i in range(len(symDRecNameList3)): vs.AddChoice(dialog_ID, listBox_ID, symDRecNameList3[i], i) return def checkBoxEvent(item, data): sfSelectedIndex, sfSelectedChoiceText = vs.GetSelectedChoiceInfo(dialog_ID, pullDownSF_ID, 0) lbSelectedIndex, lbSelectedChoiceText = vs.GetSelectedChoiceInfo(dialog_ID, listBox_ID, 0) clearListBox() #vs.AlrtDialog(f"Index : {sfSelectedIndex}\nText : {sfSelectedChoiceText}") return def clearListBox(): listBoxChoiceCnt = vs.GetChoiceCount(dialog_ID, listBox_ID) if listBoxChoiceCnt > 0: for i in range(listBoxChoiceCnt): vs.RemoveChoice(dialog_ID, listBox_ID, 0) return control_IDs() Dialog() RadioButton version of the dialog: def control_IDs(): global SetupDialogC global groupBoxSF_ID global pullDownSF_ID global radioBtnGrpBx_ID global radioBtn1_ID global radioBtn2_ID global listBox_ID SetupDialogC = 12255 groupBoxSF_ID = 29 pullDownSF_ID = 30 radioBtnGrpBx_ID = 31 radioBtn1_ID = 32 radioBtn2_ID = 33 listBox_ID = 34 return def DialogHandler(item, data): if item == SetupDialogC: initializeDialog() elif item == pullDownSF_ID: pullDownEvent(item, data) #vs.AlrtDialog(f"Pulldown SF - Item : {item} Data : {data}") elif item == radioBtn1_ID: radioBtn1Event(item, data) #vs.AlrtDialog(f"RadioBtn1 - Item : {item} Data : {data}") elif item == radioBtn2_ID: radioBtn2Event(item, data) #vs.AlrtDialog(f"RadioBtn2 - Item : {item} Data : {data}") elif item == 1: # if 'OK' clicked pass elif item == 2: # if 'Cancel' clicked pass return def Dialog(): global dialog_ID dialog_ID = vs.CreateLayout("Dialog Title", False, "OK", "Cancel") vs.CreateGroupBox(dialog_ID, groupBoxSF_ID, "Select Symbol Folder", True) vs.CreatePullDownMenu(dialog_ID, pullDownSF_ID, 64) vs.CreateGroupBox(dialog_ID, radioBtnGrpBx_ID, "Include Subfolders ? ", True) vs.CreateRadioButton(dialog_ID, radioBtn1_ID, "Yes") vs.CreateRadioButton(dialog_ID, radioBtn2_ID, "No") vs.CreateListBox(dialog_ID, listBox_ID, 64, 10) vs.SetFirstGroupItem(dialog_ID, radioBtnGrpBx_ID, radioBtn1_ID) vs.SetRightItem(dialog_ID, radioBtn1_ID, radioBtn2_ID, 10, 0) vs.SetFirstGroupItem(dialog_ID, groupBoxSF_ID, pullDownSF_ID) vs.SetBelowItem(dialog_ID, pullDownSF_ID, radioBtnGrpBx_ID, 0, 0) vs.SetBelowItem(dialog_ID, radioBtnGrpBx_ID, listBox_ID, 0, 0) vs.SetFirstLayoutItem(dialog_ID, groupBoxSF_ID) return vs.RunLayoutDialog(dialog_ID, DialogHandler) def initializeDialog(): vs.SetBooleanItem(dialog_ID, radioBtn1_ID, False) vs.SetBooleanItem(dialog_ID, radioBtn2_ID, True) symFNameList = ["Folder-Truss", "Folder-Lights", "Folder-Cable"] counter = 0 vs.AddChoice(dialog_ID, pullDownSF_ID, "-- Default --", counter) counter = counter + 1 for name in symFNameList: vs.AddChoice(dialog_ID, pullDownSF_ID, name, counter) counter = counter + 1 return def pullDownEvent(item, data): symDRecNameList = ["Recordname-1", "Recordname-2", "Recordname-3"] symDRecNameList2 = ["Recordname-4", "Recordname-5", "Recordname-6"] symDRecNameList3 = ["Recordname-7", "Recordname-8", "Recordname-9"] sfSelectedIndex, sfSelectedChoiceText = vs.GetSelectedChoiceInfo(dialog_ID, pullDownSF_ID, 0) clearListBox() if sfSelectedChoiceText == "Folder-Truss": for i in range(len(symDRecNameList)): vs.AddChoice(dialog_ID, listBox_ID, symDRecNameList[i], i) elif sfSelectedChoiceText == "Folder-Lights": for i in range(len(symDRecNameList2)): vs.AddChoice(dialog_ID, listBox_ID, symDRecNameList2[i], i) elif sfSelectedChoiceText == "Folder-Cable": for i in range(len(symDRecNameList3)): vs.AddChoice(dialog_ID, listBox_ID, symDRecNameList3[i], i) return def radioBtn1Event(item, data): sfSelectedIndex, sfSelectedChoiceText = vs.GetSelectedChoiceInfo(dialog_ID, pullDownSF_ID, 0) lbSelectedIndex, lbSelectedChoiceText = vs.GetSelectedChoiceInfo(dialog_ID, listBox_ID, 0) clearListBox() #vs.AlrtDialog(f"Index : {sfSelectedIndex}\nText : {sfSelectedChoiceText}") return def radioBtn2Event(item, data): sfSelectedIndex, sfSelectedChoiceText = vs.GetSelectedChoiceInfo(dialog_ID, pullDownSF_ID, 0) lbSelectedIndex, lbSelectedChoiceText = vs.GetSelectedChoiceInfo(dialog_ID, listBox_ID, 0) clearListBox() #vs.AlrtDialog(f"Index : {sfSelectedIndex}\nText : {sfSelectedChoiceText}") return def clearListBox(): listBoxChoiceCnt = vs.GetChoiceCount(dialog_ID, listBox_ID) if listBoxChoiceCnt > 0: for i in range(listBoxChoiceCnt): vs.RemoveChoice(dialog_ID, listBox_ID, 0) return control_IDs() Dialog() Really appreciate the help ! Thx! Quote Link to comment
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.