Jump to content

Dialogs Modern - Crash on MacOs when clearing listbox and strange behaviour.


Recommended Posts

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()

 

Link to comment

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.

  • Like 1
Link to comment

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!!!

Link to comment

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!

Link to comment

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.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...