-
Notifications
You must be signed in to change notification settings - Fork 253
QDialog, loadUi and closeEvent #192
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Here's a full reproducible of import os
import sys
import tempfile
from Qt import QtWidgets, QtCompat
def setup_ui(uifile, base_instance=None):
"""Load a Qt Designer .ui file and returns an instance of the user interface
Args:
uifile (str): Absolute path to .ui file
base_instance (QWidget): The widget into which UI widgets are loaded
Returns:
QWidget: the base instance
"""
ui = QtCompat.loadUi(uifile) # Qt.py mapped function
if not base_instance:
return ui
else:
for member in dir(ui):
if not member.startswith('__') and \
member is not 'staticMetaObject':
setattr(base_instance, member, getattr(ui, member))
return ui
ui = """\
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Dialog</class>
<widget class="QDialog" name="Dialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>300</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>2</number>
</property>
<property name="margin">
<number>3</number>
</property>
</layout>
</widget>
<resources/>
<connections/>
</ui>
"""
class MainWindow(QtWidgets.QWidget):
def __init__(self, parent=None):
QtWidgets.QWidget.__init__(self, parent)
with tempfile.NamedTemporaryFile("w+b", delete=False) as f:
f.write(ui)
self.base_instance = setup_ui(f.name, self)
os.remove(f.name)
def closeEvent(self, event):
print("Called closeEvent()")
window = MainWindow()
window.show()
window.close() Removing the |
Surrounding it with print-statements reveals that the call to setup_ui erases the overridden function. ...
class MainWindow(QtWidgets.QWidget):
def __init__(self, parent=None):
QtWidgets.QWidget.__init__(self, parent)
with tempfile.NamedTemporaryFile("w+b", delete=False) as f:
f.write(ui)
print(self.closeEvent)
self.base_instance = setup_ui(f.name, self)
print(self.closeEvent)
os.remove(f.name)
def closeEvent(self, event):
print("Called closeEvent()") Output <bound method MainWindow.closeEvent of <__main__.MainWindow object at 0x000002C4C0AA9408>>
<built-in method closeEvent of PySide.QtGui.QDialog object at 0x000002C4C0B86C88> |
Handing over to @fredrikaverpil, seeing anything odd here? |
Thanks @mottosso for putting together the reproducible code! When looping over the members of the UI file in I can update the docs to make a note of this. def setup_ui(uifile, base_instance=None):
"""Load a Qt Designer .ui file and returns an instance of the user interface
Args:
uifile (str): Absolute path to .ui file
base_instance (QWidget): The widget into which UI widgets are loaded
Returns:
QWidget: the base instance
"""
ui = QtCompat.loadUi(uifile) # Qt.py mapped function
if not base_instance:
return ui
else:
for member in dir(ui):
if not member.startswith('__') and \
member is not 'staticMetaObject' and \
not member.endswith('Event'):
setattr(base_instance, member, getattr(ui, member))
return ui @nchaverou will this work for you? |
Maybe there is a way to only override non-overridden methods in the |
Yeah... you could do something like this to check whether the member was overridden in the class instance and if so skip it: import inspect
def has_method(obj, name):
"""Returns True if method is defined directly in instance"""
v = vars(obj.__class__)
return name in v and inspect.isroutine(v[name]) |
Let me whip up a working example. |
Actually, I just noticed I'm barking up the completely wrong tree as my suggestion didn't fix the original issue. I can only get |
...which in itself is kind of weird as I get this when I print the method before and after
|
This is what I have. But unfortunately, I don't know why we can't get the import inspect
import os
import sys
import tempfile
from Qt import QtWidgets, QtCompat
def has_method(obj, name):
"""Returns True if method is defined directly in instance"""
v = vars(obj.__class__)
return name in v and inspect.isroutine(v[name])
def setup_ui(uifile, base_instance=None, obj=None):
"""Load a Qt Designer .ui file and returns an instance of the user interface
Args:
uifile (str): Absolute path to .ui file
base_instance (QWidget): The widget into which UI widgets are loaded
Returns:
QWidget: the base instance
"""
ui = QtCompat.loadUi(uifile) # Qt.py mapped function
overridden_methods = []
if obj is not None:
for member in dir(ui):
if has_method(obj, member):
overridden_methods.append(member)
# print(overridden_methods)
if not base_instance:
return ui
else:
for member in dir(ui):
if not member.startswith('__') and \
member is not 'staticMetaObject' and \
member not in overridden_methods:
setattr(base_instance, member, getattr(ui, member))
# else:
# print('Skip ' + member)
return ui
ui = """\
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Dialog</class>
<widget class="QDialog" name="Dialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>300</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>2</number>
</property>
<property name="margin">
<number>3</number>
</property>
</layout>
</widget>
<resources/>
<connections/>
</ui>
"""
class MainWindow(QtWidgets.QWidget):
def __init__(self, parent=None):
QtWidgets.QWidget.__init__(self, parent)
with tempfile.NamedTemporaryFile("w+b", delete=False) as f:
f.write(ui)
print(self.closeEvent)
# self.my_ui = QtWidgets.QWidget()
# self.base_instance = setup_ui(f.name, base_instance=self.my_ui, obj=self)
self.base_instance = setup_ui(f.name, base_instance=self, obj=self)
print(self.closeEvent)
os.remove(f.name)
def closeEvent(self, event):
print("Called closeEvent()")
window = MainWindow()
window.show()
window.close()
|
Hey guys, so semi related, but at work I'm getting a lot of requests for I have an implementation we're trying based on this gist I found here. It's not the prettiest but it seems to be working. I'm getting permission to contribute the changes back but essentially:
Anyway hopefully I can contribute the changes back, but it's the minimal most change I could make to |
@dgovil yeah, I created Qt.py support for Just wanted to mention it because there was substantial amount of work (as you can see from that PR) as well as discussing back and forth. In the end, it still seems right to me that we ultimately didn't support this in Qt.py as this would be difficult to maintain. This is why the two examples exists which makes it easy to customize In this thread, we're discussing why the My recommendation is to not attempt to further extend the |
Fair enough, its definitely good to want to keep the separation of custom functionality separate. I'll maintain the extensions to loadUi in our fork then rather than try and mainline it. |
I forgot to mention I very much appreciate the fact that you want to contribute back and I know @mottosso does too. ❤️ Let's hear what @mottosso has to say as well. Perhaps we should consider including the base instance examples into the Python wheel (and then perhaps just |
Oh I don't really have a stake in the functionality of loadUi myself so I wouldn't want to try and make the argument either way. Personally I don't ever use ui files (handcoded UI's all the way!) . I only implemented it internally because we're starting our long port to Maya 2017 and a lot of TDs here use loadUi. I agree with you guys that introducing custom functionality sort of puts the project on the hook for dealing with issues that may arise from the custom implementations and being the basis for a lot of production tools, that can start ballooning. On the flip side, I see it being a common request from a lot of studio TDs who use loadUi with the baseinstance parameter. And I'm glad you guys are open to discussion and contributions :) |
Thanks @dgovil, like Fredrik mentioned it is something we've been through in the past. Having said that, a few things have changed since then.
And by now I had expected plentiful of additional requests for wrapping various functionality but it almost looks like So if Fredrik is ok with it, I'd be open to reconsider it, given we first:
If you put a PR together @dgovil we could continue the discussion there. |
👍 |
@nchaverou back on topic; I think you're best off using the |
@fredrikaverpil : yep that's what i'm doing right now.
|
A third option is to not use the base instance argument and just use Then you'll have to do something like this: my_ui = QtCompat.loadUi(my_ui_file)
my_ui.my_widget # this is how you would then access a widget loaded from the ui file This is a compromise which I don't want. But when working with multiple bindings this is an okay compromise, I think, compared to the headaches of dealing with base instance (like you are experiencing now). |
Out of curiosity, is there anything you can't achieve with the approach @fredrikaverpil just suggested? For example, here's the initial example using this approach, the difference being that main window parameters are defined in Python as opposed to Qt Designer, such as Window Size. import os
import tempfile
from Qt import QtWidgets, QtCompat
ui = """\
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QPushButton" name="pushButton">
<property name="text">
<string>My Button</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
"""
class MainWindow(QtWidgets.QWidget):
def __init__(self, parent=None):
QtWidgets.QWidget.__init__(self, parent)
with tempfile.NamedTemporaryFile("w+b", delete=False) as f:
f.write(ui)
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(QtCompat.loadUi(f.name))
os.remove(f.name)
self.resize(300, 300)
def closeEvent(self, event):
print("Called closeEvent()")
window = MainWindow()
window.show() |
I'm guessing that the reason people want to use the base_instance is that they want their ui-loaded widgets to appear exactly next to their "hand-coded" widgets and they want all widgets on Meaning; the following would have all widgets defined on class MainWindow(QtWidgets.QWidget):
def __init__(self, parent=None):
QtWidgets.QWidget.__init__(self, parent)
with tempfile.NamedTemporaryFile("w+b", delete=False) as f:
f.write(ui)
self.layout = QtWidgets.QVBoxLayout() # Creates widget "layout" on self
QtCompat.loadUi(f.name, base_instance=self) # Load "pushButton" widget onto self (if base_instance was an argument)
os.remove(f.name)
self.resize(300, 300)
print(self.layout) # this would work
print(self.pushButton) # this would work
def closeEvent(self, event):
print("Called closeEvent()") I can see an issue if you're porting old code, but not really if you're designing your own stuff and from scratch and deliberately don't need your widgets stored on |
I've asked for peoples opinions at work, and here's what I get:
It's definitely not a problem for new code. But quite a few studios it seem are using |
Thanks guys that's really useful info. If you put a PR together @dgovil I'd be happy to start talking implementation details. |
Howdy all, I have a multi-window application that I'd like to close with a custom closeEvent, but have not had any luck implementing the above methods. What is the current preferred method of overriding closeEvent for a MainWindow(QtWidgets.QWidget) class implementing QtCompat.loadUi? |
Uh oh!
There was an error while loading. Please reload this page.
Hey there,
Wanted to move some legacy tools to maya 2017 using Qt.py
The only tool left I couldn't get to work properly used Nathan Ortiz loadUIType
Thus, I took a look to this page: https://github.com/mottosso/Qt.py/tree/master/examples/loadUi
Attached is a minimal module with its ui file showing the issue
Am I missing something or is there a limitation to the loadUi function ?
skinWrangler.zip
The text was updated successfully, but these errors were encountered: