-
Notifications
You must be signed in to change notification settings - Fork 253
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #119 from fredrikaverpil/feature/examples
Add examples
- Loading branch information
Showing
10 changed files
with
269 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import os | ||
import glob | ||
import shutil | ||
|
||
# Copy example files into current working directory | ||
for filepath in glob.glob('examples/*/*'): | ||
filename = os.path.basename(filepath) | ||
if filepath.endswith('.py'): | ||
shutil.copyfile(filepath, 'test_'+filename) # Prepend 'test' to *.py | ||
else: | ||
shutil.copyfile(filepath, filename) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
## Examples | ||
|
||
#### Purpose | ||
|
||
Sometimes a pull request or a feature request doesn't make it into Qt.py because it doesn't fit [the contribution guidelines](https://github.com/mottosso/Qt.py/blob/master/CONTRIBUTING.md). This is hopefully a good thing for the end product in the long term perspective, but we're always sad to see good code drift into oblivion and disappear when a pull request is turned down and closed. | ||
|
||
This part of the Qt.py project is a more loosely maintained (although tested) space, where we welcome example use of Qt.py to be shown off especially if it solves a problem Qt.py by itself doesn't solve out of the box. | ||
|
||
If you wish to contribute, make a pull request. Please put your example files in a sub-folder of `/examples`. If you also wish to have your example included in testing, make sure the function you wish to be executed during testing is named in such a way that it starts with `test`. For a working example of examples :wink:, see the `/examples/load_ui` folder. | ||
|
||
<br> | ||
|
||
#### List of examples | ||
|
||
* [`load_ui`](https://github.com/mottosso/Qt.py/blob/master/examples/load_ui/README.md) - add base instance argument |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
## `load_ui` examples | ||
|
||
#### Base instance as argument | ||
|
||
The `uic.loadUi` function of PyQt4 and PyQt5 as well as the `QtUiTools.QUiLoader().load` function of PySide/PySide2 are mapped to a convenience function in Qt.py called `load_ui`. This function only accepts the filename argument of the .ui file. | ||
|
||
A popular approach is to provide a base instance argument to PyQt's `uic.loadUi`, into which all widgets are loaded: | ||
|
||
```python | ||
# PyQt4, PyQt5 | ||
class MainWindow(QtWidgets.QWidget): | ||
def __init__(self, parent=None): | ||
QtWidgets.QWidget.__init__(self, parent) | ||
uic.loadUi('uifile.ui', self) # Loads all widgets of uifile.ui into self | ||
``` | ||
|
||
PySide does not support this out of the box, but it can be implemented in various ways. In the example in `baseinstance1.py`, a support function `setup_ui` is defined which wraps `load_ui` and provides this second base instance argument. In `baseinstance2.py`, another approach is used where `pysideuic` is required for PySide/PySide2 and `uic` is required for PyQt4/PyQt5. |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import sys | ||
import os | ||
|
||
# Set preferred binding | ||
os.environ['QT_PREFERRED_BINDING'] = os.pathsep.join(['PySide', 'PyQt4']) | ||
|
||
from Qt import QtWidgets, load_ui | ||
|
||
|
||
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 = load_ui(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 | ||
|
||
|
||
class MainWindow(QtWidgets.QWidget): | ||
"""Load .ui file example, using setattr/getattr approach""" | ||
def __init__(self, parent=None): | ||
QtWidgets.QWidget.__init__(self, parent) | ||
self.base_instance = setup_ui('qwidget.ui', self) | ||
|
||
|
||
def test(): | ||
"""Example: load_ui with setup_ui wrapper""" | ||
working_directory = os.path.dirname(__file__) | ||
os.chdir(working_directory) | ||
|
||
app = QtWidgets.QApplication(sys.argv) | ||
window = MainWindow() | ||
|
||
# Tests | ||
assert isinstance(window, QtWidgets.QWidget) | ||
assert isinstance(window.parent(), type(None)) | ||
assert isinstance(window.base_instance, QtWidgets.QWidget) | ||
assert isinstance(window.lineEdit, QtWidgets.QWidget) | ||
assert window.lineEdit.text() == '' | ||
window.lineEdit.setText('Hello') | ||
assert window.lineEdit.text() == 'Hello' | ||
|
||
app.exit() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
import sys | ||
import os | ||
|
||
# Set preferred binding, or Qt.py tests will fail which doesn't have pysideuic | ||
os.environ['QT_PREFERRED_BINDING'] = 'PyQt4' | ||
|
||
from Qt import QtWidgets | ||
from Qt import __binding__ | ||
|
||
|
||
def load_ui_type(uifile): | ||
"""Pyside equivalent for the loadUiType function in PyQt. | ||
From the PyQt4 documentation: | ||
Load a Qt Designer .ui file and return a tuple of the generated form | ||
class and the Qt base class. These can then be used to create any | ||
number of instances of the user interface without having to parse the | ||
.ui file more than once. | ||
Note: | ||
Pyside lacks the "loadUiType" command, so we have to convert the ui | ||
file to py code in-memory first and then execute it in a special frame | ||
to retrieve the form_class. | ||
Args: | ||
uifile (str): Absolute path to .ui file | ||
Returns: | ||
tuple: the generated form class, the Qt base class | ||
""" | ||
import pysideuic | ||
import xml.etree.ElementTree as ElementTree | ||
from cStringIO import StringIO | ||
|
||
parsed = ElementTree.parse(uifile) | ||
widget_class = parsed.find('widget').get('class') | ||
form_class = parsed.find('class').text | ||
|
||
with open(uifile, 'r') as f: | ||
o = StringIO() | ||
frame = {} | ||
|
||
pysideuic.compileUi(f, o, indent=0) | ||
pyc = compile(o.getvalue(), '<string>', 'exec') | ||
exec(pyc) in frame | ||
|
||
# Fetch the base_class and form class based on their type in | ||
# the xml from designer | ||
form_class = frame['Ui_%s' % form_class] | ||
base_class = eval('QtWidgets.%s' % widget_class) | ||
return form_class, base_class | ||
|
||
|
||
def pyside_load_ui(uifile, base_instance=None): | ||
"""Provide PyQt4.uic.loadUi functionality to PySide | ||
Args: | ||
uifile (str): Absolute path to .ui file | ||
base_instance (QWidget): The widget into which UI widgets are loaded | ||
Note: | ||
pysideuic is required for this to work with PySide. | ||
This seems to work correctly in Maya as well as outside of it as | ||
opposed to other implementations which involve overriding QUiLoader. | ||
Returns: | ||
QWidget: the base instance | ||
""" | ||
form_class, base_class = load_ui_type(uifile) | ||
if not base_instance: | ||
typeName = form_class.__name__ | ||
finalType = type(typeName, | ||
(form_class, base_class), | ||
{}) | ||
base_instance = finalType() | ||
else: | ||
if not isinstance(base_instance, base_class): | ||
raise RuntimeError( | ||
'The base_instance passed to loadUi does not inherit from' | ||
' needed base type (%s)' % type(base_class)) | ||
typeName = type(base_instance).__name__ | ||
base_instance.__class__ = type(typeName, | ||
(form_class, type(base_instance)), | ||
{}) | ||
base_instance.setupUi(base_instance) | ||
return base_instance | ||
|
||
|
||
def load_ui_wrapper(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: | ||
function: pyside_load_ui or uic.loadUi | ||
""" | ||
if 'PySide' in __binding__: | ||
return pyside_load_ui(uifile, base_instance) | ||
elif 'PyQt' in __binding__: | ||
from Qt import uic | ||
return uic.loadUi(uifile, base_instance) | ||
|
||
|
||
class MainWindow(QtWidgets.QWidget): | ||
"""Load .ui file example, utilizing pysideuic and/or PyQt4.uic.loadUi""" | ||
def __init__(self, parent=None): | ||
QtWidgets.QWidget.__init__(self, parent) | ||
self.base_instance = load_ui_wrapper('qwidget.ui', self) | ||
|
||
|
||
def test(): | ||
"""Example: load_ui with custom uic.loadUi-like wrapper""" | ||
working_directory = os.path.dirname(__file__) | ||
os.chdir(working_directory) | ||
|
||
app = QtWidgets.QApplication(sys.argv) | ||
window = MainWindow() | ||
|
||
# Tests | ||
assert isinstance(window, QtWidgets.QWidget) | ||
assert isinstance(window.parent(), type(None)) | ||
assert isinstance(window.base_instance, QtWidgets.QWidget) | ||
assert isinstance(window.lineEdit, QtWidgets.QWidget) | ||
assert window.lineEdit.text() == '' | ||
window.lineEdit.setText('Hello') | ||
assert window.lineEdit.text() == 'Hello' | ||
|
||
app.exit() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
<?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>235</width> | ||
<height>149</height> | ||
</rect> | ||
</property> | ||
<property name="windowTitle"> | ||
<string>Form</string> | ||
</property> | ||
<layout class="QGridLayout" name="gridLayout"> | ||
<item row="0" column="0"> | ||
<widget class="QLineEdit" name="lineEdit"/> | ||
</item> | ||
</layout> | ||
</widget> | ||
<resources/> | ||
<connections/> | ||
</ui> |