Changelogs » Qt.py

PyUp Safety actively tracks 232,000 Python packages for vulnerabilities and notifies you when to upgrade.

Qt.py

5.11

- See https://github.com/mottosso/Qt.py/pull/334 for details.
  
  Thanks maxnbk for this fix.

1.2.6

Improved error reporting (see 342) in the ongoing effort of making even more errors more apparent and easy to debug (see 343). Thanks justinfx for this fix!

1.2.5

New environment variable `QT_PREFERRED_BINDING_JSON` added by MHendricks to dynamically pick binding based on parent package.
  
  **Usage Author**
  
  1. As a library author, self-host Qt by storing it along with your package, e.g. `mypackage.vendor.Qt`
  2. Inform the user that most but not all bindings are supported such as PySide and PyQt4 only (e.g. in your README)
  3. Pray they see it
  
  **Usage User**
  
  1. As a user, read a library's README
  2. If you see mention of which bindings are supported
  3. Add those bindings to your `QT_PREFERRED_BINDING_JSON` environment variable.
  
  **Python**
  
  py
  import os
  import json
  
  os.environ["QT_PREFERRED_BINDING_JSON"] = json.dumps({
  "pyblish.vendor.Qt": ["PyQt5", "PySide"],
  "studiotool.vendor.Qt": ["PyQt4", "PyQt5"]
  })
  
  
  **Cmd**
  
  bash
  set QT_PREFERRED_BINDING_JSON={"pyblish.vendor.Qt": ["PyQt5", "PySide"], "studiotool.vendor.Qt": ["PyQt4", "PyQt5"]}
  
  
  **Powershell**
  
  powershell
  $env:QT_PREFERRED_BINDING_JSON='{"pyblish.vendor.Qt": ["PyQt5", "PySide"], "studiotool.vendor.Qt": ["PyQt4", "PyQt5"]}'
  
  
  **Bash**
  
  bash
  export QT_PREFERRED_BINDING_JSON={"pyblish.vendor.Qt": ["PyQt5", "PySide"], "studiotool.vendor.Qt": ["PyQt4", "PyQt5"]}
  
  
  Now whenever Pyblish `from .vendor import Qt`, it'll try and use `PyQt5` whereas Studio Tool would try `PyQt4`. This can help narrow the supported bindings of your library to some but not all of the 4 supported bindings, if your library doesn't support e.g. PySide2. Ideally of course, as a library author, you shouldn't need this and should always strive for support by all bindings where possible.
  
  See https://github.com/mottosso/Qt.py/pull/335 for details.

1.2.4


        

1.2.3

New mechanism for warning about members that are missing and supposed to be missing, currently only `QtGui.QMatrix`.
  
  If you discover or know about a member that isn't in Qt.py and shouldn't be, you can add a it to [`_missing_members`](https://github.com/mottosso/Qt.py/blob/62dc86b3a610133b959f5af037f31c3728992faa/Qt.pyL692).
  
  py
  _missing_members = {
  "QtGui": {
  "QMatrix": "Deprecated in PyQt5",
  "YOUR MEMBER": "Message to display whenever anyone tries to access it"
  },
  }

1.2.2

- Added `QtCompat.QStyleOptionViewItemV4`
  
  python
  >>> from Qt import QtCompat
  >>> QtCompat.QStyleOptionViewItemV4
  <class 'PyQt5.QtWidgets.QStyleOptionViewItem'>
  
  
  See https://github.com/mottosso/Qt.py/pull/329 for details.

1.2.1

Cosmetic change only.
  
  Support for Rez's version resolution algorithm, which puts pre-releases ahead of stable releases.
  
  - See nerdvegas/rez653

1.2.0

Added:
  
  - `QtCompat.dataChanged`
  - `QtCompat.isValid`
  
  Merged:
  
  - 306
  - 285

1.2.0.b3

Workaround to [pyside-77](https://bugreports.qt.io/browse/PYSIDE-77) bug.
  
  See https://github.com/mottosso/Qt.py/pull/306 for details. Thanks to aoblet for this fix!

1.2.0.b2

Internal changes to facilitate the [QtPyConvert project](https://github.com/digitaldomain/QtPyConvert), should not affect the end-user.
  
  - [Details](https://github.com/mottosso/Qt.py/pull/285)

1.1.0

Improved detection of common members
  
  The ability to automatically _generate_ the common members, used by Qt.py, makes it much easier to spot new additions to e.g. PySide2. Run `build_membership.sh` to generate a JSON file per binding, which are then compared. The result is written to a `common_members.json` and can be copy-pasted into Qt.py. This comparison leverages the tests suite and uses the binding versions used in the latest test suite.
  
  Once the binding versions used by the tests suite gets updated (usually annually during a VFX Platform update), we will check for new members, using this new approach.
  
  See [DOCKER.md](DOCKER.md) for more info.
  
  <br>
  
  New members
  
  
  QtCore.QItemSelectionRange   thanks to Ahuge
  QtCore.QT_TRANSLATE_NOOP
  QtCore.QT_TR_NOOP
  QtCore.QT_TR_NOOP_UTF8
  
  QtGui.DesktopServices
  QtGui.QTouchEvent
  QtGui.qRgba
  
  QtMultimedia.QAbstractVideoBuffer
  QtMultimedia.QAbstractVideoSurface
  QtMultimedia.QAudio
  QtMultimedia.QAudioDeviceInfo
  QtMultimedia.QAudioFormat
  QtMultimedia.QAudioInput
  QtMultimedia.QAudioOutput
  QtMultimedia.QVideoFrame
  QtMultimedia.QVideoSurfaceFormat
  
  QtPrintSupport.QAbstractPrintDialog
  QtPrintSupport.QPageSetupDialog
  QtPrintSupport.QPrintDialog
  QtPrintSupport.QPrintEngine
  QtPrintSupport.QPrintPreviewDialog
  QtPrintSupport.QPrintPreviewWidget
  QtPrintSupport.QPrinter
  QtPrintSupport.QPrinterInfo
  
  QtSql.QSql
  QtSql.QSqlDatabase
  QtSql.QSqlDriver
  QtSql.QSqlDriverCreatorBase
  QtSql.QSqlError
  QtSql.QSqlField
  QtSql.QSqlIndex
  QtSql.QSqlQuery
  QtSql.QSqlQueryModel
  QtSql.QSqlRecord
  QtSql.QSqlRelation
  QtSql.QSqlRelationalDelegate
  QtSql.QSqlRelationalTableModel
  QtSql.QSqlResult
  QtSql.QSqlTableModelQtSql
  
  QtSvg.QGraphicsSvgItem
  QtSvg.QSvgGenerator
  QtSvg.QSvgRenderer
  QtSvg.QSvgWidget
  
  QTest.QTest
  
  QtX11Extras.QX11Info   thanks to assumptionsoup
  
  QtXmlPatterns.QAbstractMessageHandler
  QtXmlPatterns.QAbstractUriResolver
  QtXmlPatterns.QAbstractXmlNodeModel
  QtXmlPatterns.QAbstractXmlReceiver
  QtXmlPatterns.QSourceLocation
  QtXmlPatterns.QXmlFormatter
  QtXmlPatterns.QXmlItem
  QtXmlPatterns.QXmlName
  QtXmlPatterns.QXmlNamePool
  QtXmlPatterns.QXmlNodeModelIndex
  QtXmlPatterns.QXmlQuery
  QtXmlPatterns.QXmlResultItems
  QtXmlPatterns.QXmlSchema
  QtXmlPatterns.QXmlSchemaValidator
  QtXmlPatterns.QXmlSerializer"
  
  
  <br>
  
  Removed QtCore.QMetaType member
  
  The `QtCore.QMetaType` member was removed as it was wrongly added to Qt.py.
  
  <br>
  
  New tests suite
  
  A new tests suite was added. Qt and its bindings are compiled from source using the CY2018 VFX Platform standards. This adds better support for testing with Python 3.x and removes the dependency of the Ubuntu PPAs previously used to install the Qt bindings.
  
  bash
  cd Qt.py
  
  Run nosetests (Linux/OSX)
  docker run --rm -v $(pwd):/Qt.py -e PYTHON=2.7 fredrikaverpil/qt.py:2018
  docker run --rm -v $(pwd):/Qt.py -e PYTHON=3.4 fredrikaverpil/qt.py:2018
  docker run --rm -v $(pwd):/Qt.py -e PYTHON=3.5 fredrikaverpil/qt.py:2018
  docker run --rm -v $(pwd):/Qt.py -e PYTHON=3.6 fredrikaverpil/qt.py:2018
  
  Run nosetests (Windows)
  docker run --rm -v %CD%:/Qt.py -e PYTHON=2.7 fredrikaverpil/qt.py:2018
  docker run --rm -v %CD%:/Qt.py -e PYTHON=3.4 fredrikaverpil/qt.py:2018
  docker run --rm -v %CD%:/Qt.py -e PYTHON=3.5 fredrikaverpil/qt.py:2018
  docker run --rm -v %CD%:/Qt.py -e PYTHON=3.6 fredrikaverpil/qt.py:2018
  
  
  The test suite will be updated anually, around the time of SIGGRAPH and when the VFX Platform usually gets updated.
  
  For more information on running the tests, see [README.md](README.md) and [DOCKER.md](DOCKER.md).
  
  <br>
  
  Support for Python 2.6
  
  Qt.py now works with Python 2.6, thanks to contributions made by Ahuge (see 245). However, keep in mind that the automatic tests don't apply to Python 2.6.
  
  <br>
  
  Upgraded QtCompat
  
  An additional layer was added to `QtCompat` to facilitate more members.
  
  **Before**
  
  python
  from Qt import QtCompat
  QtCompat.setSectionResizeMode
  
  
  **After**
  
  python
  from Qt import QtCompat
  QtCompat.QHeaderView.setSectionResizeMode
  
  
  **Backwards compatibility is maintained**, at least till the next major version release.
  
  <br>
  
  
  
  Pre-release installation
  
  **Install**
  
  To install a pre-release, prepend `--pre` to your `pip install`.
  
  bash
  $ pip install --pre -U Qt.py
  
  
  To upgrade an existing install, prepend `-U` as well.
  
  
  bash
  $ pip install --pre Qt.py
  
  
  
  <br>
  
  
  `QtSiteConfig`
  
  Version 1.1.0.b3 added support for overriding available members of Qt.py on import, to either add custom members and modules, or remove them.
  
  - [Documentation](https://github.com/mottosso/Qt.pyqtsiteconfigpy)
  
  <br>
  
  
  Added `wrapInstance` and `getCppPointer`
  
  The `shiboken` of PySide and `sip` of PyQt have found a home in `QtCompat`.
  
  python
  from Qt import QtCompat
  QtCompat.wrapInstance(...)
  
  
  They work similar as you would expect. See [the release notes for 1.1.0.b1](https://github.com/mottosso/Qt.py/releases/tag/1.1.0.b1) for details and examples.

1.1.0.b10

Adds support for `from Qt.QtCompat import X`.
  
  See https://github.com/mottosso/Qt.py/pull/265 for details.

1.1.0.b9

Improvements to `QtCompat.loadUi()`.
  
  - See https://github.com/mottosso/Qt.py/pull/217 for details.

1.1.0.b8

Here are the changes coming from 234:
  
  A new tests suite was added. Qt and its bindings are compiled from source using the CY2018 VFX Platform standards. This adds better support for testing with Python 3.x and removes the dependency of the Ubuntu PPAs previously used to install the Qt bindings.
  
  * The common members dictionary is now based on a proper comparison. As a result, [new members](https://github.com/mottosso/Qt.py/pull/234/filesdiff-cd1d7c0a982bce680146d5b248151256) were found to be common. Run `build_membership.py` to generate JSON files which gets compared and in finally generates `common_bindings.json` which serves as a base for the `common_members` dictionary in Qt.py.
  * `DOCKER.md` `README.md` were updated with instructions and details on testing.
  * [`QtCompat.wrapInstance` caveats test](https://github.com/mottosso/Qt.py/pull/234/filesdiff-915339f1306346f3f70f708409018718) is skipped with PySide2 due to this having been addressed by QtC.

1.1.0.b7

Added support (where available) for QtX11Extras.
  
  - See [PR](https://github.com/mottosso/Qt.py/pull/255)
  
  Thanks to assumptionsoup for this feature!

1.1.0.b6

Re-added `QItemSelectionRange` to `QtCore`.
  
  - See https://github.com/mottosso/Qt.py/pull/253
  
  Thanks Ahuge!

1.1.0.b4

Support for Python 2.6.
  
  - See 245
  
  Thanks Ahuge!

1.1.0.b3

Upgraded QtCompat
  
  An additional layer was added to `QtCompat` to facilitate more members.
  
  **Before**
  
  python
  from Qt import QtCompat
  QtCompat.setSectionResizeMode
  
  
  **After**
  
  python
  from Qt import QtCompat
  QtCompat.QHeaderView.setSectionResizeMode
  
  
  **Backwards compatibility is maintained** till the next major version release.
  
  <br>
  
  `QtSiteConfig` example
  
  > From [/examples](https://github.com/mottosso/Qt.py/tree/master/examples/QtSiteConfig)
  
  This example illustrates how to make a QtSiteConfig module and how it affects Qt.py at run-time.
  
  **Usage**
  
  bash
  $ cd to/this/directory
  $ python main.py
  Qt.QtCore was successfully removed by QSiteConfig.py
  
  
  Because `QtSiteConfig.py` is in the current working directory, it is available to import by Python. If running from a different directory, then you can append this directory to your `PYTHONPATH`
  
  bash
  $ set PYTHONPATH=path/to/QtSiteConfig/
  $ python main.py
  Qt.QtCore was successfully removed by QSiteConfig.py
  
  
  > Linux and MacOS users: Replace `set` with `export`
  
  <br>
  
  **Advanced examples**
  
  If you need to you can also add modules that are not in the standard Qt.py. All of these functions are optional in QtSiteConfig, so only implement the functions you need.
  
  QtSiteConfig.py: Adding non-standard modules
  
  By default Qt.py only exposes the "lowest common denominator" of all bindings. This example shows how to add the Qsci module that is not included by default with Qt.py.
  
  python
  def update_members(members):
  """An example of adding Qsci to Qt.py.
  
  Arguments:
  members (dict): The default list of members in Qt.py.
  Update this dict with any modifications needed.
  """
  
  Include Qsci module for scintilla lexer support.
  members["Qsci"] = [
  "QsciAPIs",
  "QsciAbstractAPIs",
  "QsciCommand",
  "QsciCommandSet",
  "QsciDocument",
  "QsciLexer",
  "QsciLexerAVS",
  "QsciLexerBash",
  "QsciLexerBatch",
  "QsciLexerCMake",
  "QsciLexerCPP",
  "QsciLexerCSS",
  "QsciLexerCSharp",
  "QsciLexerCoffeeScript",
  "QsciLexerCustom",
  "QsciLexerD",
  "QsciLexerDiff",
  "QsciLexerFortran",
  "QsciLexerFortran77",
  "QsciLexerHTML",
  "QsciLexerIDL",
  "QsciLexerJSON",
  "QsciLexerJava",
  "QsciLexerJavaScript",
  "QsciLexerLua",
  "QsciLexerMakefile",
  "QsciLexerMarkdown",
  "QsciLexerMatlab",
  "QsciLexerOctave",
  "QsciLexerPO",
  "QsciLexerPOV",
  "QsciLexerPascal",
  "QsciLexerPerl",
  "QsciLexerPostScript",
  "QsciLexerProperties",
  "QsciLexerPython",
  "QsciLexerRuby",
  "QsciLexerSQL",
  "QsciLexerSpice",
  "QsciLexerTCL",
  "QsciLexerTeX",
  "QsciLexerVHDL",
  "QsciLexerVerilog",
  "QsciLexerXML",
  "QsciLexerYAML",
  "QsciMacro",
  "QsciPrinter",
  "QsciScintilla",
  "QsciScintillaBase",
  "QsciStyle",
  "QsciStyledText",
  ]
  
  
  
  QtSiteConfig.py: Standardizing the location of Qt classes
  
  Some classes have been moved to new locations between bindings. Qt.py uses the namespace dictated by PySide2 and most members are already in place.
  This example reproduces functionality already in Qt.py but it provides a good example of how use this function.
  
  python
  def update_misplaced_members(members):
  """This optional function is called by Qt.py to standardize the location
  and naming of exposed classes.
  
  Arguments:
  members (dict): The members considered by Qt.py
  """
  Standardize the the Property name
  members["PySide2"]["QtCore.Property"] = "QtCore.Property"
  members["PyQt5"]["QtCore.pyqtProperty"] = "QtCore.Property"
  members["PySide"]["QtCore.Property"] = "QtCore.Property"
  members["PyQt4"]["QtCore.pyqtProperty"] = "QtCore.Property"
  
  
  QtSiteConfig.py: Standardizing PyQt4's QFileDialog functionality
  
  This example reproduces functionality already in Qt.py but it provides a good example of what is necessary to create your QtCompat namespaces with custom method decorators to change how the source method runs.
  
  python
  def update_compatibility_members(members):
  """This function is called by Qt.py to modify the modules it exposes.
  
  Arguments:
  members (dict): The members considered by Qt.py
  """
  members['PyQt4']["QFileDialog"] = {
  "getOpenFileName": "QtWidgets.QFileDialog.getOpenFileName",
  "getOpenFileNames": "QtWidgets.QFileDialog.getOpenFileNames",
  "getSaveFileName": "QtWidgets.QFileDialog.getSaveFileName",
  }
  
  def update_compatibility_decorators(binding, decorators):
  """ This function is called by Qt.py to modify the decorators applied to
  QtCompat namespace objects. Defining this method is optional.
  
  Arguments:
  binding (str): The Qt binding being wrapped by Qt.py
  decorators (dict): Maps specific decorator methods to
  QtCompat namespace methods. See Qt._build_compatibility_members
  for more info.
  """
  if binding == 'PyQt4':
  QFileDialog QtCompat decorator
  def _standardizeQFileDialog(some_function):
  """ decorator that makes PyQt4 return conform to other bindings
  """
  def wrapper(*args, **kwargs):
  ret = some_function(*args, **kwargs)
  PyQt4 only returns the selected filename, force it to a
  standard return of the selected filename, and a empty string
  for the selected filter
  return (ret, '')
  preserve docstring and name of original method
  wrapper.__doc__ = some_function.__doc__
  wrapper.__name__ = some_function.__name__
  return wrapper
  
  decorators.setdefault("QFileDialog",{})["getOpenFileName"] = \
  _standardizeQFileDialog
  decorators.setdefault("QFileDialog",{})["getOpenFileNames"] = \
  _standardizeQFileDialog
  decorators.setdefault("QFileDialog",{})["getSaveFileName"] = \
  _standardizeQFileDialog

1.1.0.b2

Maintenance release.
  
  - Fix 213 and 222
  
  **Install**
  
  To install a pre-release, prepend `--pre` to your `pip install`.
  
  bash
  $ pip install --pre -U Qt.py
  
  
  To upgrade an existing install, prepend `-U` as well.
  
  
  bash
  $ pip install --pre Qt.py

1.1.0.b1

This adds a wrapper for `wrapInstance` and `getCppPointer` from `shiboken2` and automatically unifies the differences with `shiboken` and `sip` for both Python 2 and 3.
  
  | Attribute               | Returns
  |:------------------------|:-----------
  | `QtCompat.wrapInstance(addr=long, type=QObject)` | `QObject` |
  `QtCompat.getCppPointer(object=QObject)`    | `long`
  
  - See 53 for details.
  - To **test**, copy/paste [this file](https://raw.githubusercontent.com/abstractfactory/Qt.py/implement59/Qt.py)
  
  **Usage**
  
  python
  import sys
  from Qt import QtCompat, QtWidgets
  app = QtWidgets.QApplication(sys.argv)
  button = QtWidgets.QPushButton("Hello world")
  pointer = QtCompat.getCppPointer(button)
  widget = QtCompat.wrapInstance(long(pointer), QtWidgets.QWidget)
  assert widget == button
  app.exit()
  
  
  **Maya Example**
  
  This works for both 2016 and 2017.
  
  python
  import sys
  from maya import OpenMayaUI
  from Qt import QtCompat, QtWidgets
  pointer = OpenMayaUI.MQtUtil.mainWindow()
  widget = QtCompat.wrapInstance(long(pointer), QtWidgets.QWidget)
  assert isinstance(widget, QtWidgets.QWidget)
  
  
  Important
  
  This addition requires `sip`, `shiboken` or `shiboken2` to be available on your system. If not found, Qt.py will still import successfully, but these members will not be available.
  
  In such cases, here is a Qt-only version and guaranteed cross-compatible version of the above.
  
  python
  from Qt import QtWidgets
  app = QtWidgets.QApplication.instance()
  widget = {o.objectName(): o for o in app.topLevelWidgets()}["MayaWindow"]
  
  
  The same pattern may be applied to any and all uses of `sip`, `shiboken` and `shiboken2`, as discussed in-depth at 53.
  
  Enjoy!
  
  More Examples
  
  For consideration into the main README.
  
  `wrapInstance` have found particular use in Autodesk Maya, below are a few scenarios in which it is commonly used along with cross-binding alternatives.
  
  <br>
  
  Finding Widget Through MEL
  
  **shiboken**
  
  python
  from maya import mel, OpenMayaUI
  from Qt import QtWidgets
  import shiboken2
  
  status_line = mel.eval('$temp=$gStatusLineForm')
  ptr = OpenMayaUI.MQtUtil.findControl(status_line)
  status_line = shiboken2.wrapInstance(long(ptr), QtWidgets.QWidget)
  status_line = status_line.children()[1].children()[1]
  status_line.setStyleSheet("QWidget {background: red}")
  
  
  **Qt**
  
  python
  from maya import mel
  from Qt import QtWidgets
  app = QtWidgets.QApplication.instance()
  window = {o.objectName(): o for o in app.topLevelWidgets()}["MayaWindow"]
  status_line = mel.eval('$temp=$gStatusLineForm')
  status_line = window.findChild(QtGui.QWidget, gStatusLine)
  status_lne.setStyleSheet("QWidget {background: red}")
  
  
  <br>
  
  Finding Widget through Object Name
  
  **shiboken**
  
  python
  import shiboken
  import maya.OpenMayaUI as apiUI
  from Qt import QtWidgets
  channel_box = apiUI.MQtUtil.findControl("mainChannelBox")
  channel_box = shiboken.wrapInstance(long(channel_box), QtWidgets.QTableView)
  channel_box.setStyleSheet("QWidget {background: red}")
  
  
  **Qt**
  
  python
  from Qt import QtWidgets
  app = QtWidgets.QApplication.instance()
  window = {o.objectName(): o for o in app.topLevelWidgets()}["MayaWindow"]
  channel_box = window.findChild(QtWidgets.QTableView, "mainChannelBox")
  channel_box.setStyleSheet("QWidget {background: green}")
  
  
  <br>
  
  Custom Attribute Editor Template
  
  For testing purposes, we'll create a custom node and associate an attribute editor template with it. The modification of the resulting template via Qt is what differs between `shiboken` and `Qt`.
  
  **Boilerplate**
  
  These two files are identical and cross-compatible.
  
  c
  // AEMyNodeTemplate.mel
  
  global proc AEMyNodeTemplate(string $nodeName)
  {
  editorTemplate -beginScrollLayout;
  editorTemplate -beginLayout "My Attributes" -collapse 0;
  editorTemplate -callCustom "MyNode_build_ui" "MyNode_update_ui" $nodeName;
  editorTemplate -addControl "x";
  editorTemplate -addControl "y";
  editorTemplate -addControl "z";
  editorTemplate -endLayout;
  editorTemplate -addExtraControls;
  editorTemplate -endScrollLayout;
  }
  
  global proc MyNode_build_ui( string $nodeName )
  {
  string $parent = `setParent -q`;
  python("import myNodeUi");
  python("myNodeUi.build_ui('" + $parent + "', '" + $nodeName + "')");
  }
  
  global proc MyNode_update_ui( string $nodeName )
  {
  string $parent = `setParent -q`;
  python("myNodeUi.update_ui('" + $parent + "', '" + $nodeName + "')");
  }
  
  
  python
  myNode.py
  from maya import OpenMaya, OpenMayaMPx
  
  kPluginNodeName = "MyNode"
  MyNodeId = OpenMaya.MTypeId(524286)
  
  
  class MyNode(OpenMayaMPx.MPxNode):
  _x = OpenMaya.MObject()
  _y = OpenMaya.MObject()
  _z = OpenMaya.MObject()
  
  def __init__(self):
  OpenMayaMPx.MPxNode.__init__(self)
  
  def compute(self, plug, data_block):
  print("Computing..")
  
  
  def MyNodeCreator():
  return OpenMayaMPx.asMPxPtr(MyNode())
  
  
  def MyNodeInit():
  attr = OpenMaya.MFnNumericAttribute()
  MyNode._x = attr.create("x", "x", OpenMaya.MFnNumericData.kFloat, 0.0)
  attr.setKeyable(True)
  MyNode._y = attr.create("y", "y", OpenMaya.MFnNumericData.kFloat, 0.0)
  attr.setKeyable(True)
  MyNode._z = attr.create("z", "z", OpenMaya.MFnNumericData.kFloat, 0.0)
  attr.setKeyable(True)
  MyNode.addAttribute(MyNode._x)
  MyNode.addAttribute(MyNode._y)
  MyNode.addAttribute(MyNode._z)
  
  
  def initializePlugin(mobject):
  mplugin = OpenMayaMPx.MFnPlugin(mobject)
  mplugin.registerNode(
  kPluginNodeName,
  MyNodeId,
  MyNodeCreator,
  MyNodeInit,
  OpenMayaMPx.MPxNode.kDependNode
  )
  
  
  def uninitializePlugin(mobject):
  mplugin = OpenMayaMPx.MFnPlugin(mobject)
  mplugin.deregisterNode(MyNodeId)
  
  
  **shiboken**
  
  Notice the OpenMayaUI and shiboken dependency.
  
  python
  myNodeUi.py
  from maya import cmds, OpenMayaUI
  
  from Qt import QtWidgets
  
  if cmds.about(api=True) >= 201700:
  from shiboken2 import wrapInstance
  else:
  from shiboken import wrapInstance
  
  def build_ui(layout, node):
  layout_ptr = OpenMayaUI.MQtUtil.findLayout(layout)
  layout_obj = wrapInstance(long(layout_ptr), QtWidgets.QWidget)
  layout_wid = layout_obj.findChild(QtWidgets.QBoxLayout)   Cast to QBoxLayout
  
  widget = QtWidgets.QLabel("Hello World")
  layout_wid.insertWidget(0, widget)
  
  def update_ui(layout, node):
  pass
  
  
  **Qt**
  
  python
  myNodeUi.py
  from Qt import QtWidgets
  
  def build_ui(layout, node):
  app = QtWidgets.QApplication.instance()
  window = {o.objectName(): o for o in app.topLevelWidgets()}["MayaWindow"]
  
  parent = window
  for child in layout.split("|")[1:]:
  parent = parent.findChild(QtWidgets.QWidget, child)
  
  widget = QtWidgets.QLabel("Hello World")
  layout = parent.findChild(QtWidgets.QBoxLayout)   Cast to QBoxLayout
  layout.insertWidget(0, widget)
  
  def update_ui(layout, node):
  pass
  
  
  Get top-level window in any binding and any application.
  
  sip and shiboken is sometimes used to fetch the main window of an application in order to make it a parent of a custom window. Below is an example of how to find said window efficiently and in any situation.
  
  python
  from Qt import QtWidgets
  
  current = QtWidgets.QApplication.activeWindow()
  while current:
  parent = current
  current = parent.parent()
  
  print(parent)
  
  
  **Limitations**
  
  - If run from within an already custom window that *did not* have it's parent set to the main window or a descendant of it, then this will return the custom window and may exit when it exists.

1.0

This **BACKWARDS INCOMPATIBLE** version boasts quite a few additions and changes.
  
  - Enforce PySide2 API | Read more [here](https://github.com/mottosso/Qt.py/pull/173) and [here](https://github.com/mottosso/Qt.py/issues/152)
  - Optional Submodules, now you can use Qt.py on distribution missing common members, such as in Houdini, along with QML, OpenGL and third-party additions like Qscintilla | [Read more](https://github.com/mottosso/Qt.py/pull/186)
  - Improved consistency, `load_ui()` is now called `loadUi()`, just like its original | [Read more](https://github.com/mottosso/Qt.py/releases/tag/1.0.0.b3)
  - Support for `baseinstance` in `loadUi()` | [Read more](https://github.com/mottosso/Qt.py/releases/tag/1.0.0.b4)
  - Binding constants reap the benefits of static checking from your IDE | [Read more](https://github.com/mottosso/Qt.py/releases/tag/1.0.0.b5)

1.0.0


        

1.0.0.b6

QtSiteConfig.py
  
  Individual members of Qt.py may now be customised via an additional module called `QtSiteConfig.py`. For example, one could remove `QtCore` if for whatever reason it shouldn't be used at all, or add site-specific modules like [`Qsci`](http://pyqt.sourceforge.net/Docs/QScintilla2/).
  
  - [Documentation](https://github.com/mottosso/Qt.pyqtsiteconfigpy)
  - [Original pull-request](https://github.com/mottosso/Qt.py/pull/207)
  
  Thanks to MHendricks for this feature!

1.0.0.b5

Added support for binding constants.
  
  python
  import Qt
  
  Before
  if Qt.__binding__ == "PyQt5":
  Do PyQt5 things
  
  After
  if Qt.IsPyQt5:
  Do PyQt5 things
  
  
  Which in addition to cutting down on typed characters also enables your IDE to detect potential misspellings. The previous method still exists and continues to work, there are no plans to deprecate it.
  
  Thanks to dgovil for this feature!

1.0.0.b4

Added support for `baseinstance` to `QtCompat.loadUi()`.
  
  python
  QtCompat.loadUi(uifile="my.ui", baseinstance=QtWidgets.QWidget)
  
  
  This feature mimics the functionality (warts and all) of `PyQt5.uic.loadUi` found [here](http://pyqt.sourceforge.net/Docs/PyQt5/designer.htmlPyQt5.uic.loadUi).
  
  - [Documentation](https://github.com/mottosso/Qt.pyloading-qt-designer-files)
  - See 196 for more details.
  
  Thanks to dgovil for this feature!

1.0.0.b3

**Beta** release of 179.
  
  python
  Before
  from Qt import QtCompat
  QtCompat.load_ui(...)
  
  After
  from Qt import QtCompat
  QtCompat.loadUi(...)
  
  
  This is backwards compatible, `QtCompat.load_ui` will continue to be available but is to be considered deprecated.
  
  See 177

1.0.0.b2

**Beta** release of 185.
  
  - See 186 for details.

1.0.0b1

**Beta** release of 152, an enforced PySide2 API.
  
  - See 173 for more details.

0.6.9

Maintenance release, it fixes the internal `QT_TESTING` environment variable such that members are properly tested during testing on Travis CI.
  
  Thanks to sol-ansano-kim for the fix!

0.6.8

This release fixes 162 and makes the Qt.py wheel universal. This means no more wheel filename fragmentation.
  
  Previous wheel filename fragmentation
  
  Previously, `pip wheel -w . Qt.py` could generate one of the following:
  
  
  Qt.py-0.6.7-py2-none-any.whl
  Qt.py-0.6.7-py3-none-any.whl
  
  
  or...
  
  
  Qt.py-0.6.7-cp27-none-any.whl
  Qt.py-0.6.7-cp35-none-any.whl
  ...
  
  
  New wheel file naming
  
  The wheels is now universal and `pip wheel -w . Qt.py` results in the following:
  
  
  Qt.py-0.6.8-py2.py3-none-any.whl

0.6.8.dev2

This is a test release to attempt to produce a universal wheel.

0.6.8.dev1

This is a test release to attempt to produce a universal wheel.