Runtime UI class loading - why?

The "qtui" module lets you read a UI file at runtime, and create an instance of the widget it describes (which is usually a QMainWindow or QDialog).

This is useful, because it avoids having to run pyuic at compile time, and allows your programs to choose and load UI files dynamically. However, in Python it's more useful to be able to create the class of the widget, rather than an instance of that class, at runtime. There are two big advantages:

Neither of the above make any sense in a C++ context, which is why qtui doesn't implement this.

Here is a module which allows such runtime loading. It works by spawning pyuic and then using the import internals.

Example Usage

import PyUIC
FormClass = PyUIC.load('form.ui')

Bugs, Limitations

PyUIC.py

"""Loads Qt UI files as python classes at runtime.

EXAMPLE USAGE

    import qt
    import PyUIC
    
    # Load screen class from UI file
    FormClass = PyUIC.load('exampleform.ui')

REQUIRES

    - Qt
    - PyQt
    - pyuic

COPYRIGHT

    Copyright (C) 2004, David Chan and Clockwork Software Systems.

    This program is free software; you can distribute it and/or modify
    it under the terms of the GNU Lesser General Public License as
    published by the Free Software Foundation; either version 2.1 of the
    License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful, but
    WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
    USA

"""


import os
import re
import imp
import popen2

class Error(Exception):
    pass

def load(filename):
    """Reads a UI file; returns a python class"""
    python_code = _compile_ui(filename)
    module = _load_module(python_code, filename)
    klass = _load_class(module)
    return klass

def _compile_ui(filename):
    """Reads a UI file; returns a string of python code"""

    # Create new process, get its stdin/stdout
    uic_out, uic_in = popen2.popen2('pyuic /dev/stdin')

    # Write UI file to pyuic
    ui_file = open(filename)
    uic_in.write(ui_file.read())
    uic_in.close()

    # Read code from pyuic -> python_code
    python_code = uic_out.read()
    uic_out.close()
    return python_code

# -----------------------------------------------------------------

def _load_module(python_code, filename):
    """loads the code as a python module"""

    # Read class name from code
    m = re.search('^class\s+(\w+)', python_code, re.MULTILINE)
    if not m:
        raise Error, "Can't find class name:((%s))" % python_code
    class_name = m.group(1) 

    # Write code to a temporary file
    tmp_file = os.tmpfile()
    tmp_file.write(python_code)
    tmp_file.seek(0)

    # Load code with the module loader
    if not imp.lock_held(): imp.acquire_lock()
    try:
        module = imp.load_source(class_name, filename, tmp_file)
    finally:
        tmp_file.close()
        imp.release_lock()

    # Delete compiled .XXc file XXX can we stop it being made?
    try:
        os.unlink(filename + 'c')
    except OSError, ex:
        pass

    return module

# -----------------------------------------------------------------

def _load_class(module):
    """Returns the class whose name the module shares"""
    try:
        klass = getattr(module, module.__name__)
    except AttributeError:
        raise Error, "Can't get class %r" % class_name

    return klass

# ---------------------------------------------------------------------
# 
# Test code
# 

if __name__ == '__main__':
    import qt
    import sys
    if len(sys.argv) != 2:
        print "Test usage: %s file.ui" % sys.argv[0], 
        sys.exit(1)

    app = qt.QApplication(sys.argv)
    app.connect(app, qt.SIGNAL("lastWindowClosed()"), app, qt.SLOT("quit()"))

    klass = load(sys.argv[1])
    w = klass()
    app.setMainWidget(w)
    w.show()
    print "Widget: %r" % w
    app.exec_loop()

In PyQt4

In PyQt4 you can just use the PyQt4.uic module, which will return a class, with PyQt4.uic.loadUiType().

PyQtWiki: LoadingUIFilesAtRuntime (last edited 2009-04-14 07:27:21 by localhost)