diff --git a/Qt.py b/Qt.py
index 4aadc9ac..c3c7292e 100644
--- a/Qt.py
+++ b/Qt.py
@@ -3,8 +3,8 @@
DOCUMENTATION
Qt.py was born in the film and visual effects industry to address
the growing need for the development of software capable of running
- with more than one flavour of the Qt bindings for Python.
-
+ with more than one flavour of the Qt bindings for Python.
+
Supported Binding: PySide, PySide2, PySide6, PyQt4, PyQt5
1. Build for one, run with all
@@ -47,7 +47,7 @@
import json
-__version__ = "1.3.10"
+__version__ = "1.4.0"
# Enable support for `from Qt import *`
__all__ = []
@@ -112,7 +112,6 @@
"QGenericArgument",
"QGenericReturnArgument",
"QItemSelection",
- "QItemSelectionModel",
"QItemSelectionRange",
"QIODevice",
"QLibraryInfo",
@@ -152,7 +151,6 @@
"QSize",
"QSizeF",
"QSocketNotifier",
- "QStringListModel",
"QSysInfo",
"QSystemSemaphore",
"QT_TRANSLATE_NOOP",
@@ -197,8 +195,6 @@
"qUnregisterResourceData",
"qVersion",
"qWarning",
- "Signal",
- "Slot"
],
"QtGui": [
"QAbstractTextDocumentLayout",
@@ -271,7 +267,6 @@
"QQuaternion",
"QRadialGradient",
"QRegion",
- "QRegExpValidator",
"QResizeEvent",
"QSessionManager",
"QShortcutEvent",
@@ -930,6 +925,9 @@ def createWidget(self, class_name, parent=None, name=""):
These members from the original submodule are misplaced relative PySide2
+NOTE: For bindings where a member is not replaced, they still
+ need to be added such that they are added to Qt.py
+
"""
_misplaced_members = {
"PySide6": {
@@ -943,6 +941,9 @@ def createWidget(self, class_name, parent=None, name=""):
"QtCore.QItemSelection": "QtCore.QItemSelection",
"QtCore.QItemSelectionModel": "QtCore.QItemSelectionModel",
"QtCore.QItemSelectionRange": "QtCore.QItemSelectionRange",
+ "QtCore.QRegularExpression": "QtCore.QRegExp",
+ "QtStateMachine.QStateMachine": "QtCore.QStateMachine",
+ "QtStateMachine.QState": "QtCore.QState",
"QtGui.QRegularExpressionValidator": "QtGui.QRegExpValidator",
"QtGui.QShortcut": "QtWidgets.QShortcut",
"QtGui.QAction": "QtWidgets.QAction",
@@ -961,14 +962,16 @@ def createWidget(self, class_name, parent=None, name=""):
"QtCompat.qInstallMessageHandler", _qInstallMessageHandler
],
"QtWidgets.QStyleOptionViewItem": "QtCompat.QStyleOptionViewItemV4",
- "QtMultimedia.QSound": "QtMultimedia.QSound",
+ "QtWidgets.QActionGroup": "QtGui.QActionGroup",
},
"PySide2": {
- "QtGui.QStringListModel": "QtCore.QStringListModel",
"QtGui.QStringListModel": "QtCore.QStringListModel",
"QtCore.Property": "QtCore.Property",
"QtCore.Signal": "QtCore.Signal",
"QtCore.Slot": "QtCore.Slot",
+ "QtCore.QRegExp": "QtCore.QRegExp",
+ "QtWidgets.QShortcut": "QtWidgets.QShortcut",
+ "QtGui.QRegExpValidator": "QtGui.QRegExpValidator",
"QtCore.QAbstractProxyModel": "QtCore.QAbstractProxyModel",
"QtCore.QSortFilterProxyModel": "QtCore.QSortFilterProxyModel",
"QtCore.QItemSelection": "QtCore.QItemSelection",
@@ -989,7 +992,6 @@ def createWidget(self, class_name, parent=None, name=""):
"QtCompat.qInstallMessageHandler", _qInstallMessageHandler
],
"QtWidgets.QStyleOptionViewItem": "QtCompat.QStyleOptionViewItemV4",
- "QtMultimedia.QSound": "QtMultimedia.QSound",
},
"PyQt5": {
"QtCore.pyqtProperty": "QtCore.Property",
@@ -1000,6 +1002,8 @@ def createWidget(self, class_name, parent=None, name=""):
"sip.unwrapinstance": ["QtCompat.getCppPointer", _getcpppointer],
"sip.isdeleted": ["QtCompat.isValid", _isvalid],
"QtWidgets.qApp": "QtWidgets.QApplication.instance()",
+ "QtGui.QRegExpValidator": "QtGui.QRegExpValidator",
+ "QtCore.QRegExp": "QtCore.QRegExp",
"QtCore.QCoreApplication.translate": [
"QtCompat.translate", _translate
],
@@ -1009,10 +1013,13 @@ def createWidget(self, class_name, parent=None, name=""):
"QtCore.qInstallMessageHandler": [
"QtCompat.qInstallMessageHandler", _qInstallMessageHandler
],
+ "QtWidgets.QShortcut": "QtWidgets.QShortcut",
"QtWidgets.QStyleOptionViewItem": "QtCompat.QStyleOptionViewItemV4",
- "QtMultimedia.QSound": "QtMultimedia.QSound",
},
"PySide": {
+ "QtCore.Property": "QtCore.Property",
+ "QtCore.Signal": "QtCore.Signal",
+ "QtCore.Slot": "QtCore.Slot",
"QtGui.QAbstractProxyModel": "QtCore.QAbstractProxyModel",
"QtGui.QSortFilterProxyModel": "QtCore.QSortFilterProxyModel",
"QtGui.QStringListModel": "QtCore.QStringListModel",
@@ -1020,18 +1027,21 @@ def createWidget(self, class_name, parent=None, name=""):
"QtGui.QItemSelectionModel": "QtCore.QItemSelectionModel",
"QtGui.QItemSelectionRange": "QtCore.QItemSelectionRange",
"QtGui.QAbstractPrintDialog": "QtPrintSupport.QAbstractPrintDialog",
+ "QtGui.QRegExpValidator": "QtGui.QRegExpValidator",
"QtGui.QPageSetupDialog": "QtPrintSupport.QPageSetupDialog",
"QtGui.QPrintDialog": "QtPrintSupport.QPrintDialog",
"QtGui.QPrintEngine": "QtPrintSupport.QPrintEngine",
"QtGui.QPrintPreviewDialog": "QtPrintSupport.QPrintPreviewDialog",
"QtGui.QPrintPreviewWidget": "QtPrintSupport.QPrintPreviewWidget",
"QtGui.QPrinter": "QtPrintSupport.QPrinter",
+ "QtWidgets.QShortcut": "QtWidgets.QShortcut",
"QtGui.QPrinterInfo": "QtPrintSupport.QPrinterInfo",
"QtUiTools.QUiLoader": ["QtCompat.loadUi", _loadUi],
"shiboken.wrapInstance": ["QtCompat.wrapInstance", _wrapinstance],
"shiboken.unwrapInstance": ["QtCompat.getCppPointer", _getcpppointer],
"shiboken.isValid": ["QtCompat.isValid", _isvalid],
"QtGui.qApp": "QtWidgets.QApplication.instance()",
+ "QtCore.QRegExp": "QtCore.QRegExp",
"QtCore.QCoreApplication.translate": [
"QtCompat.translate", _translate
],
@@ -1042,7 +1052,6 @@ def createWidget(self, class_name, parent=None, name=""):
"QtCompat.qInstallMessageHandler", _qInstallMessageHandler
],
"QtGui.QStyleOptionViewItemV4": "QtCompat.QStyleOptionViewItemV4",
- "QtGui.QSound": "QtMultimedia.QSound",
},
"PyQt4": {
"QtGui.QAbstractProxyModel": "QtCore.QAbstractProxyModel",
@@ -1055,9 +1064,11 @@ def createWidget(self, class_name, parent=None, name=""):
"QtCore.pyqtSlot": "QtCore.Slot",
"QtGui.QItemSelectionRange": "QtCore.QItemSelectionRange",
"QtGui.QAbstractPrintDialog": "QtPrintSupport.QAbstractPrintDialog",
+ "QtGui.QRegExpValidator": "QtGui.QRegExpValidator",
"QtGui.QPageSetupDialog": "QtPrintSupport.QPageSetupDialog",
"QtGui.QPrintDialog": "QtPrintSupport.QPrintDialog",
"QtGui.QPrintEngine": "QtPrintSupport.QPrintEngine",
+ "QtWidgets.QShortcut": "QtWidgets.QShortcut",
"QtGui.QPrintPreviewDialog": "QtPrintSupport.QPrintPreviewDialog",
"QtGui.QPrintPreviewWidget": "QtPrintSupport.QPrintPreviewWidget",
"QtGui.QPrinter": "QtPrintSupport.QPrinter",
@@ -1068,6 +1079,7 @@ def createWidget(self, class_name, parent=None, name=""):
"sip.isdeleted": ["QtCompat.isValid", _isvalid],
"QtCore.QString": "str",
"QtGui.qApp": "QtWidgets.QApplication.instance()",
+ "QtCore.QRegExp": "QtCore.QRegExp",
"QtCore.QCoreApplication.translate": [
"QtCompat.translate", _translate
],
@@ -1078,7 +1090,6 @@ def createWidget(self, class_name, parent=None, name=""):
"QtCompat.qInstallMessageHandler", _qInstallMessageHandler
],
"QtGui.QStyleOptionViewItemV4": "QtCompat.QStyleOptionViewItemV4",
- "QtGui.QSound": "QtMultimedia.QSound",
}
}
@@ -1141,6 +1152,12 @@ def createWidget(self, class_name, parent=None, name=""):
"getOpenFileNames": "QtWidgets.QFileDialog.getOpenFileNames",
"getSaveFileName": "QtWidgets.QFileDialog.getSaveFileName",
},
+ "QFont":{
+ "setWeight": "QtGui.QFont.setWeight",
+ },
+ "Qt": {
+ "MidButton": "QtCore.Qt.MiddleButton",
+ },
},
"PyQt5": {
"QWidget": {
@@ -1161,6 +1178,12 @@ def createWidget(self, class_name, parent=None, name=""):
"getOpenFileNames": "QtWidgets.QFileDialog.getOpenFileNames",
"getSaveFileName": "QtWidgets.QFileDialog.getSaveFileName",
},
+ "QFont":{
+ "setWeight": "QtGui.QFont.setWeight",
+ },
+ "Qt": {
+ "MidButton": "QtCore.Qt.MiddleButton",
+ },
},
"PySide": {
"QWidget": {
@@ -1179,6 +1202,12 @@ def createWidget(self, class_name, parent=None, name=""):
"getOpenFileNames": "QtWidgets.QFileDialog.getOpenFileNames",
"getSaveFileName": "QtWidgets.QFileDialog.getSaveFileName",
},
+ "QFont":{
+ "setWeight": "QtGui.QFont.setWeight",
+ },
+ "Qt": {
+ "MidButton": "QtCore.Qt.MiddleButton",
+ },
},
"PyQt4": {
"QWidget": {
@@ -1197,6 +1226,12 @@ def createWidget(self, class_name, parent=None, name=""):
"getOpenFileNames": "QtWidgets.QFileDialog.getOpenFileNames",
"getSaveFileName": "QtWidgets.QFileDialog.getSaveFileName",
},
+ "QFont":{
+ "setWeight": "QtGui.QFont.setWeight",
+ },
+ "Qt": {
+ "MidButton": "QtCore.Qt.MiddleButton",
+ },
},
}
@@ -1275,6 +1310,7 @@ def _reassign_misplaced_members(binding):
"""
+
for src, dst in _misplaced_members[binding].items():
dst_value = None
@@ -1293,6 +1329,7 @@ def _reassign_misplaced_members(binding):
if len(dst_parts) > 1:
dst_member = dst_parts[1]
+
# Get the member we want to store in the namesapce.
if not dst_value:
try:
@@ -1439,7 +1476,7 @@ def _pyside6():
if hasattr(Qt, "_QtWidgets"):
Qt.QtCompat.setSectionResizeMode = \
Qt._QtWidgets.QHeaderView.setSectionResizeMode
-
+
def setWeight(func):
def wrapper(self, weight):
weight = {
@@ -1460,7 +1497,7 @@ def wrapper(self, weight):
wrapper.__name__ = func.__name__
return wrapper
-
+
decorators = {
"QFont": {
@@ -1469,7 +1506,6 @@ def wrapper(self, weight):
}
_reassign_misplaced_members("PySide6")
- _build_compatibility_members("PySide6")
_build_compatibility_members("PySide6", decorators)
diff --git a/README.md b/README.md
index 1460b0b6..608d23cf 100644
--- a/README.md
+++ b/README.md
@@ -15,6 +15,7 @@ Qt.py enables you to write software that runs on any of the 4 supported bindings
| Date | Version | Event
|:---------|:----------|:----------
+| May 2024 | [1.4.0][] | Added support for Qt 6
| Jan 2024 | [1.3.9][] | Run CI on Github Actions, instead of Travis CI.
| Sep 2020 | [1.3.0][] | Stability improvements and greater ability for `QtCompat.wrapInstance` to do its job
| Jun 2019 | [1.2.1][] | Bugfixes and [additional members](https://github.com/mottosso/Qt.py/releases/tag/1.2.0)
@@ -34,9 +35,11 @@ Qt.py enables you to write software that runs on any of the 4 supported bindings
[1.2.1]: https://github.com/mottosso/Qt.py/releases/tag/1.2.1
[1.3.0]: https://github.com/mottosso/Qt.py/releases/tag/1.3.0
[1.3.9]: https://github.com/mottosso/Qt.py/releases/tag/1.3.9
+[1.4.0]: https://github.com/mottosso/Qt.py/releases/tag/1.4.0
##### Guides
+- [Qt 6 Transition Guide](#qt-6-transition-guide)
- [Developing with Qt.py](https://fredrikaverpil.github.io/blog/2016/07/25/developing-with-qtpy/)
- [Dealing with Maya 2017 and PySide2](https://fredrikaverpil.github.io/blog/2016/07/25/dealing-with-maya-2017-and-pyside2/)
- [Vendoring Qt.py](https://fredrikaverpil.github.io/blog/2017/05/04/vendoring-qtpy/)
@@ -64,6 +67,7 @@ Qt.py enables you to write software that runs on any of the 4 supported bindings
- [Projects using Qt.py](#projects-using-qtpy)
- [Projects similar to Qt.py](#projects-similar-to-qtpy)
- [Developer guide](#developer-guide)
+- [Qt 6 transition guide](#qt-6-transition-guide)
@@ -589,3 +593,174 @@ cd Qt.py
python .\setup.py sdist bdist_wheel
python -m twine upload .\dist\*
```
+
+
+
+
+
+### Qt 6 Transition Guide
+
+| Replace | With | Notes
+|:--------|:-----|:----------------------------
+| `QFont().setWeight(...)` | `QtCompat.QFont.setWeight(font, ...)`
+| `QFont().setWeight(QFont().Bold)` | `QFont().setWeight(QFont.Bold)` | Instance of class doesn't have the enums, apparently
+| `QEvent().Resize` | `QEvent.Resize` | Instance of class doesn't have the enums, seems to apply overall
+| `QtCore.Qt.MidButton` | `QtCompat.QtCore.Qt.MidButton`
+| `QLabel.setPixmap(str)` | `QLabel.setPixmap(QPixmap())` | Can't take a string anymore (tested in Maya 2025.0)
+| `QModelIndex.child` | `QModel.index` | This one is apparently from Qt 4 and should not have been in Qt.py to begin with
+| | Submit your known issues here! |
+
+##### Removed Members
+
+Many members were removed from Qt.py due to no longer existing in PySide 6.
+
+> If you find where they went, or think some were removed in error, please submit a pull-request!
+
+```json
+"QtCore": [
+ "QAbstractState",
+ "QAbstractTransition",
+ "QEventTransition",
+ "QFinalState",
+ "QSignalTransition",
+ "QTextCodec",
+ "QTextDecoder",
+ "QTextEncoder",
+ "QtCriticalMsg",
+ "QtDebugMsg",
+ "QtFatalMsg",
+ "QtSystemMsg",
+ "QtWarningMsg",
+ "qChecksum",
+ "QPictureIO",
+],
+"QtMultimedia": [
+ "QAbstractVideoBuffer",
+ "QAbstractVideoSurface",
+ "QAudio",
+ "QAudioDeviceInfo",
+ "QAudioFormat",
+ "QAudioInput",
+ "QAudioOutput",
+ "QVideoFrame",
+ "QVideoSurfaceFormat"
+],
+"QtNetwork": [
+ "QNetworkConfiguration",
+ "QNetworkConfigurationManager",
+ "QNetworkSession",
+],
+"QtOpenGL": [
+ "QGL",
+ "QGLContext",
+ "QGLFormat",
+ "QGLWidget"
+],
+"QtSql": [
+ "QSql",
+ "QSqlDatabase",
+ "QSqlDriver",
+ "QSqlDriverCreatorBase",
+ "QSqlError",
+ "QSqlField",
+ "QSqlIndex",
+ "QSqlQuery",
+ "QSqlQueryModel",
+ "QSqlRecord",
+ "QSqlRelation",
+ "QSqlRelationalDelegate",
+ "QSqlRelationalTableModel",
+ "QSqlResult",
+ "QSqlTableModel"
+],
+"QtSvg": [
+ "QSvgGenerator",
+ "QSvgRenderer",
+],
+"QtWidgets": [
+ "QActionGroup",
+ "QDesktopWidget",
+ "QDirModel",
+ "QKeyEventTransition",
+ "QMouseEventTransition",
+ "QUndoCommand",
+ "QUndoGroup",
+ "QUndoStack",
+],
+"QtX11Extras": [
+ "QX11Info"
+],
+"QtXml": [
+ "QXmlAttributes",
+ "QXmlContentHandler",
+ "QXmlDTDHandler",
+ "QXmlDeclHandler",
+ "QXmlDefaultHandler",
+ "QXmlEntityResolver",
+ "QXmlErrorHandler",
+ "QXmlInputSource",
+ "QXmlLexicalHandler",
+ "QXmlLocator",
+ "QXmlNamespaceSupport",
+ "QXmlParseException",
+ "QXmlReader",
+ "QXmlSimpleReader"
+],
+"QtXmlPatterns": [
+ "QAbstractMessageHandler",
+ "QAbstractUriResolver",
+ "QAbstractXmlNodeModel",
+ "QAbstractXmlReceiver",
+ "QSourceLocation",
+ "QXmlFormatter",
+ "QXmlItem",
+ "QXmlName",
+ "QXmlNamePool",
+ "QXmlNodeModelIndex",
+ "QXmlQuery",
+ "QXmlResultItems",
+ "QXmlSchema",
+ "QXmlSchemaValidator",
+ "QXmlSerializer"
+]
+```
+
+##### Static Members Missing from Instances
+
+An overall change is that instances of classes, like `QFont()` no longer provides access to static members, such as `QFont.Bold`. So things like:
+
+```py
+font = QFont()
+font.setWeight(font.Bold)
+```
+
+Must be replaced with:
+
+```py
+font = QFont()
+font.setWeight(QFont.Bold)
+```
+
+Or:
+
+```py
+font.setWeight(type(font).Bold)
+```
+
+Tedious and seemingly unnecessary.. But there you have it!
+
+##### Notes
+
+Qt.py 1.4.0, released in May 2024, added support for Qt 6 whilst preserving compatibility with Qt 4 and 5. That means that in most cases, code you've already written for Qt 4 or 5 will now continue to work with Qt 6, such as Maya 2025.
+
+However, some changes between 5 and 6 require up-front work by you the developer to make your codebase run on Qt 6 whilst continuing to run on Qt 4 and 5.
+
+The above is what we know, please do submit issues and pull-request with what else you find!
+
+- https://github.com/mottosso/Qt.py/issues/new
+
+**See also**
+
+The official PySide2 to PySide6 transition guide, which is especially helpful since Qt.py is modeled after PySide2.
+
+- https://doc.qt.io/qtforpython-6/gettingstarted/porting_from2.html
diff --git a/tests.py b/tests.py
index fef4ea51..6439b656 100644
--- a/tests.py
+++ b/tests.py
@@ -21,7 +21,7 @@
except ImportError:
# Fallback: Define assert_raises using unittest if the import fails
import unittest
-
+
def assert_raises(expected_exception, callable_obj=None, *args, **kwargs):
"""
Custom implementation of assert_raises using unittest.
@@ -36,7 +36,7 @@ def assert_raises(expected_exception, callable_obj=None, *args, **kwargs):
function_that_raises_some_exception()
"""
context = unittest.TestCase().assertRaises(expected_exception)
-
+
# If callable_obj is provided, directly call the function with the context manager
if callable_obj:
with context:
@@ -409,7 +409,7 @@ def test_load_ui_returntype():
import sys
from Qt import QtWidgets, QtCore, QtCompat
-
+
if not QtWidgets.QApplication.instance():
app = QtWidgets.QApplication(sys.argv)
else:
@@ -423,7 +423,7 @@ def test_load_ui_baseinstance():
"""Tests to see if the baseinstance loading loads a QWidget on properly"""
import sys
from Qt import QtWidgets, QtCompat
-
+
if not QtWidgets.QApplication.instance():
app = QtWidgets.QApplication(sys.argv)
else:
@@ -438,7 +438,7 @@ def test_load_ui_signals():
"""Tests to see if the baseinstance connects signals properly"""
import sys
from Qt import QtWidgets, QtCompat
-
+
if not QtWidgets.QApplication.instance():
app = QtWidgets.QApplication(sys.argv)
else:
@@ -457,7 +457,7 @@ def test_load_ui_mainwindow():
import sys
from Qt import QtWidgets, QtCompat
-
+
if not QtWidgets.QApplication.instance():
app = QtWidgets.QApplication(sys.argv)
else:
@@ -477,7 +477,7 @@ def test_load_ui_dialog():
import sys
from Qt import QtWidgets, QtCompat
-
+
if not QtWidgets.QApplication.instance():
app = QtWidgets.QApplication(sys.argv)
else:
@@ -497,7 +497,7 @@ def test_load_ui_dockwidget():
import sys
from Qt import QtWidgets, QtCompat
-
+
if not QtWidgets.QApplication.instance():
app = QtWidgets.QApplication(sys.argv)
else:
@@ -517,7 +517,7 @@ def test_load_ui_customwidget():
import sys
from Qt import QtWidgets, QtCompat
-
+
if not QtWidgets.QApplication.instance():
app = QtWidgets.QApplication(sys.argv)
else:
@@ -560,12 +560,12 @@ def test_load_ui_pycustomwidget():
# append the path to ensure the future import can be loaded 'relative' to the tempdir
sys.path.append(self.tempdir)
-
+
if not QtWidgets.QApplication.instance():
app = QtWidgets.QApplication(sys.argv)
else:
app = QtWidgets.QApplication.instance()
-
+
win = QtWidgets.QMainWindow()
QtCompat.loadUi(self.ui_qpycustomwidget, win)
@@ -584,7 +584,7 @@ def test_load_ui_invalidpath():
"""Tests to see if loadUi successfully fails on invalid paths"""
import sys
from Qt import QtWidgets, QtCompat
-
+
if not QtWidgets.QApplication.instance():
app = QtWidgets.QApplication(sys.argv)
else:
@@ -606,7 +606,7 @@ def test_load_ui_invalidxml():
from xml.etree import ElementTree
from Qt import QtWidgets, QtCompat
-
+
if not QtWidgets.QApplication.instance():
app = QtWidgets.QApplication(sys.argv)
else:
@@ -624,7 +624,7 @@ def test_load_ui_existingLayoutOnDialog():
'"Dialog", which already has a layout'
with ignoreQtMessageHandler([msgs]):
-
+
if not QtWidgets.QApplication.instance():
app = QtWidgets.QApplication(sys.argv)
else:
@@ -645,7 +645,7 @@ def test_load_ui_existingLayoutOnMainWindow():
'"", which already has a layout'
with ignoreQtMessageHandler([msgs]):
-
+
if not QtWidgets.QApplication.instance():
app = QtWidgets.QApplication(sys.argv)
else:
@@ -666,7 +666,7 @@ def test_load_ui_existingLayoutOnDockWidget():
'"", which already has a layout'
with ignoreQtMessageHandler([msgs]):
-
+
if not QtWidgets.QApplication.instance():
app = QtWidgets.QApplication(sys.argv)
else:
@@ -687,7 +687,7 @@ def test_load_ui_existingLayoutOnWidget():
'"Form", which already has a layout'
with ignoreQtMessageHandler([msgs]):
-
+
if not QtWidgets.QApplication.instance():
app = QtWidgets.QApplication(sys.argv)
else:
@@ -998,7 +998,7 @@ def test_qtcompat_base_class():
import Qt
from Qt import QtWidgets
from Qt import QtCompat
-
+
if not QtWidgets.QApplication.instance():
app = QtWidgets.QApplication(sys.argv)
else: