-
Notifications
You must be signed in to change notification settings - Fork 252
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
Ignore the default empty layout objects when loading a .ui file #217
Conversation
Ok, I'm getting a Travis build failure on the PyQt implementations. From what I can track down, calling |
Thanks @tbttfox. I've tracked down the original commit from which this clause got added, I think it'd be helpful to future readers if we included a comment on what it's doing there. The reason being to mimic PyQt throwing an exception. Which now that I think of it sounds a little backwards. It's PyQt who's supposed to mimic PySide2. Do you think it's possible to have PyQt support this feature, as opposed to PySide inheriting shortcomings of PyQt? It might not be, in which case this would be fine. |
From what I can see, PyQt does support this feature. Nosepipe is just confused by the qwarning (that doesn't actually break anything), and I'm not familiar enough with nosepipe to tell it that the warning is expected, because it's not a normal python error. And FYI: |
From my testing for both PyQt and PySide:
And if you call bool(widget.layout()) or if widget.layout():
This is consistent in both Qt4 and and Qt5 versions. This means that due to the artificial check, you can't use I've done some testing and PyQt4/5 do not error if you use Qt's c++ code for QLayout raises a qWarning 'QLayout: Attempting to add QLayout "" to QMainWindow "", which already has a layout' I'm assuming this goes to stderr, and nose detects it as a error. This is a c++ output, so I'm not sure if you can redirect its stderr output away from nose, but I think this is the original reason for raising the RuntimeError in Here is a simple example of the problem, in this case it was run with PyQt4 from a windows command prompt: >>> import sys
>>> from Qt import QtWidgets
>>> app = QtWidgets.QApplication(sys.argv)
>>> win = QtWidgets.QMainWindow()
>>> layout = QtWidgets.QVBoxLayout(win)
QLayout: Attempting to add QLayout "" to QMainWindow "", which already has a layout
>>> No error was raised, but "QLayout: Attempting ..." is written to the command prompt, not python. I haven't seen it in Maya, but I assume its there just not being shown. Is it possible to make nose ignore stderr from c++ for these specific tests? Or make Qt not log qWarnings? I don't think the check added in e4a736a is needed at all and it actually breaks things. |
I did a little more searching and I think we could control the qWarning output using this function already exposed in Qt.py http://doc.qt.io/qt-5/qtglobal.html#qInstallMessageHandler Some example code that looks promising: https://stackoverflow.com/a/25681472 |
Thanks for the excellent investigatory notes, @MHendricks.
Would you be able to take the lead on resolving that? I don't use |
It may be a few days before I can get to this (production support gets in the way). You don't need to test with loadUi, just attempt to create a layout and pass a parent widget(that already has a layout) into its constructor like I did in the code sample above. I'll try to find some time to give it a try, or I can help @tbttfox debug it if he has time. |
2e0face
to
d2bb8d4
Compare
tests.py
Outdated
|
||
|
||
if binding("PyQt5") or binding("PySide2"): | ||
def ignoreQtMessageHandlerFactory(msgs): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
redefinition of unused 'ignoreQtMessageHandlerFactory' from line 155
tests.py
Outdated
|
||
|
||
if binding("PyQt5") or binding("PySide2"): | ||
def ignoreQtMessageHandlerFactory(msgs): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
redefinition of unused 'ignoreQtMessageHandlerFactory' from line 155
tests.py
Outdated
if msg in msgs: | ||
return | ||
sys.stderr.write("{0}\n".format(msg)) | ||
return ictxtMgr |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
indentation contains tabs
tests.py
Outdated
msg = msg.decode() | ||
if msg in msgs: | ||
return | ||
sys.stderr.write("{0}\n".format(msg)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
indentation contains tabs
tests.py
Outdated
if binding("PySide2"): | ||
msg = msg.decode() | ||
if msg in msgs: | ||
return |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
indentation contains tabs
tests.py
Outdated
msg = args[-1] | ||
if binding("PySide2"): | ||
msg = msg.decode() | ||
if msg in msgs: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
indentation contains tabs
tests.py
Outdated
def ictxtMgr(*args): | ||
msg = args[-1] | ||
if binding("PySide2"): | ||
msg = msg.decode() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
indentation contains tabs
tests.py
Outdated
def ignoreQtMessageHandlerFactory(msgs): | ||
def ictxtMgr(*args): | ||
msg = args[-1] | ||
if binding("PySide2"): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
indentation contains tabs
tests.py
Outdated
@@ -122,6 +151,17 @@ def binding(binding): | |||
return os.getenv("QT_PREFERRED_BINDING") == binding | |||
|
|||
|
|||
def ignoreQtMessageHandlerFactory(msgs): | |||
def ictxtMgr(*args): | |||
msg = args[-1] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
indentation contains tabs
tests.py
Outdated
@@ -122,6 +151,17 @@ def binding(binding): | |||
return os.getenv("QT_PREFERRED_BINDING") == binding | |||
|
|||
|
|||
def ignoreQtMessageHandlerFactory(msgs): | |||
def ictxtMgr(*args): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
indentation contains tabs
indentation contains mixed spaces and tabs
Ok, so @MHendricks and I got together and got this working, but there's some extra stuff we found out In the Qt4 bindings, the message handler is called So, for now, we've added There is probably a bigger discussion to be had on how to handle the message handlers, but I think we can do that in a separate PR. |
tests.py
Outdated
def test_load_ui_mainwindow(): | ||
"""Tests to see if the baseinstance loading loads main windows properly""" | ||
import sys | ||
from Qt import QtWidgets, QtCompat, QtCore |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
'Qt.QtCore' imported but unused
tests.py
Outdated
msgs: list of message strings to ignore | ||
""" | ||
from Qt import QtCore | ||
def messageOutputHandler(*args): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
expected 1 blank line before a nested definition, found 0
I'm sorry for the silence here. I'm very busy at work and my wife is expecting in about a week... so I'm not sure exactly when I'll be able to review this. |
I pulled some commits from @MHendricks. Tests for running loadUi on |
There is some inconsistency between the windows and Linux builds of Qt4 when dealing with QDockWidgets. >>> from Qt.QtWidgets import QDockWidget
>>> dw = QDockWidget()
>>> dw.layout().count()
2
>>> dw.layout().isEmpty()
True For QDockWidgets on Windows, isEmpty() returns True, but on Linux it returns False. I'm not sure why the layout existence check in _loadUi was added in the first place so I removed it. QMainWindow has a default .layout() object and return False when .layout().isEmpty() is called on a new instance. This seems consistent for both Linux and Windows. QDialog and QWidget do not have a default layout. Also, thanks @tbttfox for cleaning up the unfinished mess I left on my test repo. |
Maybe we could add a
If you select a line here on GitHub and press the "Blame" button, it'll tell you the commit message for that line. In this case, I found:
Not sure what this means given the changes in this PR. @dgovil do you remember what issues you where having before that commit? Does this resolve those issues?
Not sure how this slipped in here, there hasn't been any mention of this so far? I'm happy to keep it though, it looks well thought out and documented. |
It's been mentioned a couple times, but just for some explicit background: When checking for the non-existent layouts (in the check we removed), Qt would call a Now that the check and the associated tests have been removed, it's not a strictly necessary part of this PR, but it's definitely worth having. |
@mottosso It seems PyQt4 would error if you tried to load a ui file on to a widget that already had a layout assigned, which was the reason for that warning. However I've recently discovered that it really depends on the PyQt version whether that is an issue or not. Some versions do not care while others do. I'm not sure what's the best way to proceed here? IIRC the version that the tests download will fail, but the version we have at work does not. Not sure what's entirely different and noone seems to have answered me. Maybe it's best to ignore them and if the test fails, acknowledge that it's expected behavior? |
So I added a test to see that error. And it wasn't an actual error, it was just nosepipe freaking out about a |
After some thought, we decided that And with that, I think this beast is finally ready, pending review. |
Well done everyone. I think it looks great. I've bumped the version and will merge and release once the tests do one final round. |
Merging this, sorry for the delay! |
Lets try this again, this time without the phantom user:
The default empty layout object bool(QMainWindow().layout()) evaluates to True in PySide and False in PyQt4. So I added a check to see if an existing layout is empty.