Remove pywidget 3dsmax

python
qt
max
#1

I modified an example of 3dsmax to replicate my problem.
I can’t figure out how remove a widget, actually, the instance is removed but the window display the widget
simply running the code and clicking on the button will display the problem

'''
	Demonstrates how to create a QWidget with PySide2 and attach it to the 3dsmax main window.
'''

from PySide2 import QtCore
from PySide2 import QtWidgets
import MaxPlus
import pymxs

def make_cylinder():
	cyl = pymxs.runtime.Cylinder(radius=10, height=30)
	pymxs.runtime.redrawViews()
	return	

class PyMaxDialog(QtWidgets.QDialog):
	def __init__(self, parent=MaxPlus.GetQMaxMainWindow()):
		super(PyMaxDialog, self).__init__(parent)
		self.setWindowTitle('Pyside Qt Window')
		self.initUI()
		
	def initUI(self):
		self.main_layout = QtWidgets.QVBoxLayout()
		self.label = QtWidgets.QLabel("Click button to create a cylinder in the scene")
		self.main_layout.addWidget(self.label)

		cylinder_btn = QtWidgets.QPushButton("Cylinder")
		cylinder_btn.clicked.connect(self.remove_text)
		self.main_layout.addWidget(cylinder_btn)

		self.setLayout(self.main_layout)
		self.resize(250, 100)

	def remove_text(self):
		self.main_layout.removeWidget(self.label)
		self.main_layout.update()
		time = MaxPlus.Core.GetCurrentTime()
		MaxPlus.ViewportManager.RedrawViews(time)

		

def main():
	#MaxPlus.FileManager.Reset(True)

	w = PyMaxDialog()
	w.show()

if __name__ == '__main__':
	main()

Do you know why?
It is basic stuff, but today is not my day
Thanks

#2

Hello again :slightly_smiling_face:
As your are importing pyside2 it is > 2017 so you can use pymxs.
So to redraw you only need to use:

pymxs.runtime.RedrawViews()

And to attach to the max window:
MaxPlus.AttachQWidgetToMax(self) instead of setting the parent widget in a traditional at way ( in the super._ init _ )
Adding:
@QtCore.Slot()
above remove_text is an optimisation.
And lastly you shouldn’t need to use:
main_ui.update()

These are all nit-picky and non critical though. So unfortunately, other than that I don’t understand your problem. Nothing in your code that I can see should be an issue.
Depending on your version of max you might have an obscure bug. What error message prints in the listener ( press f11 )

#3

I cleaned up a little bit the code, it was messy.
This sample is intended to display the problem

import pymxs
from PySide2 import QtCore
from PySide2 import QtWidgets
import MaxPlus

class PyMaxDialog(QtWidgets.QDialog):
	def __init__(self, parent=MaxPlus.GetQMaxMainWindow()):
		super(PyMaxDialog, self).__init__(parent)
		self.setWindowTitle('Pyside Qt Window')
		self.initUI()
		
	def initUI(self):
		self.main_layout = QtWidgets.QVBoxLayout()
		self.label = QtWidgets.QLabel("Click button delete this text")
		self.main_layout.addWidget(self.label)

		delete_btn = QtWidgets.QPushButton("Delete")
		delete_btn.clicked.connect(self.remove_text)
		self.main_layout.addWidget(delete_btn)

		self.setLayout(self.main_layout)
		self.resize(250, 100)
	
	@QtCore.Slot()
	def remove_text(self):
		self.main_layout.removeWidget(self.label)
		pymxs.runtime.RedrawViews()		

def main():
	w = PyMaxDialog()
	w.show()

if __name__ == '__main__':
	main()

This is what I have before and after clicking the button.
Capture0
Capture1

Is this a bug?
I tested this only on max 2019
Thanks for your support

#4

Yeah that definitely looks to be a bug. I don’t have access to 2019 as I am not at work but you could try the following as a work around:
self.label.setMaximumHeight(0)
Or:
self.label.hide()

That way you can hide the label and keep it in your layout to be reused later.

1 Like
#5

I will test on another max version asap to give some information to Autodesk about the bug
This combo actually removed the widget, thanks for the suggestion
I leave it here for someone else:

import pymxs
from PySide2 import QtCore
from PySide2 import QtWidgets
import MaxPlus

class PyMaxDialog(QtWidgets.QDialog):
	def __init__(self, parent=MaxPlus.GetQMaxMainWindow()):
		super(PyMaxDialog, self).__init__(parent)
		self.setWindowTitle('Pyside Qt Window')
		self.initUI()
		
	def initUI(self):
		self.main_layout = QtWidgets.QVBoxLayout()
		self.label = QtWidgets.QLabel("Click button delete this text")
		self.main_layout.addWidget(self.label)

		delete_btn = QtWidgets.QPushButton("Delete")
		delete_btn.clicked.connect(self.remove_text)
		self.main_layout.addWidget(delete_btn)

		self.setLayout(self.main_layout)
		self.resize(250, 100)
	
	@QtCore.Slot()
	def remove_text(self):
		delete_widget(self.label,self.main_layout)
		#check if widget is actually removed
		for i in range(self.main_layout.count()):
			print self.main_layout.itemAt(i)

def main():
	w = PyMaxDialog()
	w.show()
	
def delete_widget(w,parent):
	w.hide()
	parent.removeWidget(w)
	del(w)
	

if __name__ == '__main__':
	main()