diff options
author | coutinho <coutinho@esrf.fr> | 2015-03-26 12:21:41 +0100 |
---|---|---|
committer | coutinho <coutinho@esrf.fr> | 2015-03-26 12:21:41 +0100 |
commit | 6f2a9f4d65212ef253a5ce6fc173d52b8a470c57 (patch) | |
tree | 674c3ba22a326794b20abf345ec5e01c102a1b11 /lib/taurus/qt/qtgui/util | |
parent | 3d39d0a483286c6cc6abc58d5514dc5390104736 (diff) |
First commit in tauruslib. Restructure according to SEP10
Diffstat (limited to 'lib/taurus/qt/qtgui/util')
20 files changed, 2077 insertions, 0 deletions
diff --git a/lib/taurus/qt/qtgui/util/__init__.py b/lib/taurus/qt/qtgui/util/__init__.py new file mode 100644 index 00000000..44c8730f --- /dev/null +++ b/lib/taurus/qt/qtgui/util/__init__.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python + +############################################################################# +## +## This file is part of Taurus +## +## http://taurus-scada.org +## +## Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain +## +## Taurus is free software: you can redistribute it and/or modify +## it under the terms of the GNU Lesser General Public License as published by +## the Free Software Foundation, either version 3 of the License, or +## (at your option) any later version. +## +## Taurus 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 Taurus. If not, see <http://www.gnu.org/licenses/>. +## +############################################################################# + +"""This package provides a set of taurus wiget utilities like color management, +configuration, actions.""" + +__docformat__ = 'restructuredtext' + +from .taurusactionfactory import * +from .taurusaction import * +from .tauruscolor import * +from .tauruswidgetfactory import * +from .taurusscreenshot import * +from .qdraganddropdebug import * +from .ui import * diff --git a/lib/taurus/qt/qtgui/util/qdraganddropdebug.py b/lib/taurus/qt/qtgui/util/qdraganddropdebug.py new file mode 100644 index 00000000..c767e011 --- /dev/null +++ b/lib/taurus/qt/qtgui/util/qdraganddropdebug.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python + +############################################################################# +## +## This file is part of Taurus +## +## http://taurus-scada.org +## +## Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain +## +## Taurus is free software: you can redistribute it and/or modify +## it under the terms of the GNU Lesser General Public License as published by +## the Free Software Foundation, either version 3 of the License, or +## (at your option) any later version. +## +## Taurus 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 Taurus. If not, see <http://www.gnu.org/licenses/>. +## +############################################################################# + +__all__ = ["DropDebugger"] +__docformat__ = 'restructuredtext' + +from taurus.external.qt import Qt + +class DropDebugger(Qt.QLabel): + '''A simple utility for debugging drag&drop. + This widget will accept drops and show a pop-up with the contents + of the MIME data passed in the drag&drop''' + + def __init__(self, parent=None): + Qt.QLabel.__init__(self, parent) + self.setAcceptDrops(True) + self.setText('Drop something here') + self.setMinimumSize(300,200) + self.setWindowTitle('Drag&Drop Debugger') + + def dragEnterEvent(self,event): + event.acceptProposedAction() + + def dropEvent(self, event): + '''reimplemented to support drag&drop of models. See :class:`QWidget`''' + msg = '<b>MIMETYPE</b>: DATA. <ul>' + mimedata = event.mimeData() + for format in mimedata.formats(): + data = mimedata.data(format) + msg += '<li><b>{0}</b>: "{1}"</li>'.format(format, data) + msg+='</ul>' + Qt.QMessageBox.information( self, "Drop event received", msg) + + +if __name__=='__main__': + import sys + from taurus.qt.qtgui.application import TaurusApplication + + app = TaurusApplication() + w=DropDebugger() + w.show() + sys.exit(app.exec_()) diff --git a/lib/taurus/qt/qtgui/util/taurusaction.py b/lib/taurus/qt/qtgui/util/taurusaction.py new file mode 100644 index 00000000..a53767dc --- /dev/null +++ b/lib/taurus/qt/qtgui/util/taurusaction.py @@ -0,0 +1,317 @@ +#!/usr/bin/env python + +############################################################################# +## +## This file is part of Taurus +## +## http://taurus-scada.org +## +## Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain +## +## Taurus is free software: you can redistribute it and/or modify +## it under the terms of the GNU Lesser General Public License as published by +## the Free Software Foundation, either version 3 of the License, or +## (at your option) any later version. +## +## Taurus 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 Taurus. If not, see <http://www.gnu.org/licenses/>. +## +############################################################################# + +"""This module is designed to provide a library of taurus Qt actions""" + +#__all__ = [] + +__docformat__ = 'restructuredtext' + +import os +import xml.dom.minidom + +from taurus.external.qt import Qt +from taurus.qt.qtcore.configuration import BaseConfigurableClass +from taurus.qt.qtgui.resource import getThemeIcon + +class ExternalAppAction(Qt.QAction, BaseConfigurableClass): + """ An specialized QAction for launching external applications + + Signals: apart of those from QAction, it emits a "cmdArgsChanged" signal + with the current cmdArgs list as its argument. + """ + DEFAULT_ICON_NAME = 'application-x-executable' + def __init__(self, cmdargs, text=None, icon=None, parent=None, interactive=True): + '''creator + + :param cmdargs: (list<str> or str) A list of strings to be passed to + :meth:`subprocess.Popen` for launching the external + application. It can also be a single string containing a + command. See :meth:`setCmdArgs` + :param text: (str) see :class:`Qt.QAction` + :param icon: (QIcon or any other object that can be passed to QIcon creator) see :class:`Qt.QAction` + :param parent: (QObject) The parent object + ''' + if isinstance(cmdargs, (basestring, Qt.QString)): + import shlex + cmdargs = shlex.split(str(cmdargs)) + self.path = os.path.realpath(cmdargs and cmdargs[0] or '') + if text is None: text = os.path.basename(cmdargs and cmdargs[0] or '') + if icon is None: + icon = getThemeIcon(self.DEFAULT_ICON_NAME) + elif isinstance(icon,basestring): + tmp = getThemeIcon(icon) + if not tmp.isNull(): icon=tmp + + Qt.QAction.__init__(self, Qt.QIcon(icon), text, parent) + BaseConfigurableClass.__init__(self) + self.interactive = interactive + self._process = [] + self.setCmdArgs(cmdargs) + self.connect(self, Qt.SIGNAL("triggered()"), self.actionTriggered) + self.setToolTip("Launches %s (external application)"%text) + self.registerConfigProperty(self.cmdArgs, self.setCmdArgs, 'cmdArgs') + + def setCmdArgs(self, cmdargs): + '''Sets the command args for executing this external application. + + It emits the "cmdArgsChanged" signal with the new cmdArgs list + + :param cmdargs: (list<str> or str) A list of strings to be passed to + :meth:`subprocess.Popen` for launching the external + application. It can also be a string containing a + command, which will be automatically converted to a list + ''' + if isinstance(cmdargs, (basestring, Qt.QString)): + import shlex + cmdargs = shlex.split(str(cmdargs)) + self.__cmdargs = cmdargs + self.emit(Qt.SIGNAL("cmdArgsChanged"), self.__cmdargs) + + def cmdArgs(self): + return self.__cmdargs + + @Qt.pyqtSignature("triggered()") + def actionTriggered(self,args=None): + '''launches the external application as a subprocess''' + import subprocess + try: + if args is not None: + if isinstance(args, (basestring, Qt.QString)): + import shlex + args = shlex.split(str(args)) + args = self.cmdArgs()+args + else: + args = self.cmdArgs() + if any(args): + #Qt.QMessageBox.warning(self.parentWidget(),'Warning','In ExternalAppAction(%s)'%args) + self._process.append(subprocess.Popen(args)) + return True + else: + return False + except OSError: + err = "Error launching %s"%unicode(self.text()) + msg = "Cannot launch application:\n" + \ + " ".join(self.__cmdargs) + \ + "\nHint: Check that %s is installed and in the path"%unicode(self.text()) + if self.interactive: + Qt.QMessageBox.warning(self.parentWidget(), err, msg) + from taurus.core.util import Logger + Logger().warning('%s:\n%s'%(err,msg)) + return False + + def kill(self): + #Kills all processes opened by this application + [p.kill() for p in self._process] + + def check(self): + '''Returns True if the application is available for executing + + :return: (bool) + ''' + #raise NotImplementedError #@todo: implement a checker (check if self.cmdargs[0] is in the execution path and is executable + path = os.path.realpath(self.cmdArgs()[0]) + try: + os.stat(path) + return True + except: + return False + +class TaurusMenu(Qt.QMenu): + """Base class for Taurus Menus""" + + def __init__(self, parent): + Qt.QMenu.__init__(self, parent) + + def build(self, data): + m_node = xml.dom.minidom.parseString(data).childNodes[0] + self.buildFromXML(m_node) + + def getActionFactory(self): + import taurusactionfactory + return taurusactionfactory.ActionFactory() + + def buildFromXML(self, m_node): + widget = self.parent() + if m_node.hasAttribute('label'): + self.setTitle(m_node.getAttribute('label')) + + for node in m_node.childNodes: + name = node.nodeName + if name == 'Menu': + m = self.getActionFactory().buildMenu(widget, node) + if m: + self.addMenu(m) + else: + a = self.getActionFactory().buildAction(widget, node) + if a: + self.addAction(a) + + +class TaurusAction(Qt.QAction): + """Base class for Taurus Actions""" + + def __init__(self, parent): + import taurus.qt.qtgui.base + + if (parent is None) or \ + (not isinstance(parent, Qt.QWidget)) or \ + (not isinstance(parent, taurus.qt.qtgui.base.TaurusBaseComponent)): + raise RuntimeError("Invalid parent. Must be a valid Taurus widget.") + + Qt.QAction.__init__(self, parent) + + self.connect(parent, Qt.SIGNAL('modelChanged(const QString &)'), self.modelChanged) + self.connect(self, Qt.SIGNAL("triggered()"), self.actionTriggered) + + self.update() + + def update(self): + model = self.parent().getModelObj() + self.setDisabled(model is None) + + @Qt.pyqtSignature("modelChanged(const QString &)") + def modelChanged(self, modelName): + self.update() + + @Qt.pyqtSignature("triggered()") + def actionTriggered(self): + pass + + +class SeparatorAction(TaurusAction): + + menuID = "_Separator_" + + def __init__(self, parent=None): + Qt.QAction.__init__(self, parent) + self.setSeparator(True) + + +class AttributeHistoryAction(TaurusAction): + + menuID = "AttrHistory" + + def __init__(self, parent=None): + TaurusAction.__init__(self, parent) + self.setText("Show history...") + + def actionTriggered(self): + dialog = Qt.QDialog() + dialog.exec_() + + +class AttributeAllConfigAction(TaurusAction): + + menuID = "AttrConfig" + + def __init__(self, parent=None): + TaurusAction.__init__(self, parent) + self.setText("All...") + + def actionTriggered(self): + #raise NotImplementedError('This action is not yet implemented') + taurus_widget = self.parent() + from taurus.qt.qtgui.dialog.taurusconfigurationdialog import TaurusConfigurationDialog + d = TaurusConfigurationDialog() + d.setModel(taurus_widget.getModelName()) + d.exec_() + + +class AttributeMonitorDeviceAction(TaurusAction): + + menuID = "MonitorDevice" + + def __init__(self, parent=None): + TaurusAction.__init__(self, parent) + self.setText("&Monitor device ...") + + def actionTriggered(self): + taurus_widget = self.parent() + model_name = taurus_widget.getModelName() + w = model_name.split("/") + if len(w) == 3: + dev_name = model_name + elif len(w) == 4: + dev_name = w[0] + "/" + w[1] + "/" + w[2] + elif len(w) == 5:#FB: If the first parameter is the database + dev_name = w[1] + "/" + w[2] + "/" + w[3] + else: + return + + cmd = "atkpanel " + dev_name + " &" + os.system(cmd) + + +class AttributeImageDisplayAction(TaurusAction): + + menuID = "ImageDisplay" + + def __init__(self, parent=None): + TaurusAction.__init__(self, parent) + self.setText("&Image Display ...") + + def actionTriggered(self): + taurus_widget = self.parent() + model_name = taurus_widget.getModelName() + w = model_name.split("/") + if len(w) == 3: + dev_name = model_name + elif len(w) == 4: + dev_name = w[0] + "/" + w[1] + "/" + w[2] + else: + return + + attr_name = dev_name + "/Image" + + import qub + im = qub._TaurusQubDataImageDisplay(data=attr_name) + im.show() + + +class AttributeMenu(TaurusMenu): + + menuID = "AttrMenu" + menuData = "<Menu label='Attribute'>" \ + "<MenuItem class='AttrHistory'/>"\ + "<MenuItem class='_Separator_'/>"\ + "<Menu class='AttrConfig'/>"\ + "</Menu>" + + def __init__(self, parent): + TaurusMenu.__init__(self, parent) + self.build(self.menuData) + + +class ConfigurationMenu(TaurusMenu): + menuID = "AttrConfigMenu" + menuData = "<Menu label='Configuration'>" \ + "<MenuItem class='AttrConfig'/>"\ + "</Menu>" + + def __init__(self, parent): + TaurusMenu.__init__(self, parent) + self.build(self.menuData) + diff --git a/lib/taurus/qt/qtgui/util/taurusactionfactory.py b/lib/taurus/qt/qtgui/util/taurusactionfactory.py new file mode 100644 index 00000000..53ced986 --- /dev/null +++ b/lib/taurus/qt/qtgui/util/taurusactionfactory.py @@ -0,0 +1,147 @@ +#!/usr/bin/env python + +############################################################################# +## +## This file is part of Taurus +## +## http://taurus-scada.org +## +## Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain +## +## Taurus is free software: you can redistribute it and/or modify +## it under the terms of the GNU Lesser General Public License as published by +## the Free Software Foundation, either version 3 of the License, or +## (at your option) any later version. +## +## Taurus 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 Taurus. If not, see <http://www.gnu.org/licenses/>. +## +############################################################################# + +"""This module is designed to provide a factory class for taurus Qt actions """ + +__all__ = ["ActionFactory"] + +__docformat__ = 'restructuredtext' + +from taurus.core.util.log import Logger +from taurus.core.util.singleton import Singleton +from taurus.external.qt import Qt +from taurus.qt.qtgui.resource import getThemeIcon + +import taurusaction + + +class ActionFactory(Singleton, Logger): + """A Singleton class designed to provide Action related objects.""" + + def __init__(self): + """ Initialization. Nothing to be done here for now.""" + pass + + def init(self, *args): + """Singleton instance initialization.""" + self.call__init__(Logger, 'ActionFactory') + self.actions = self.__getActions() + self.menus = self.__getMenus() + + def __getClasses(self, super_class): + ret = {} + klass_type = type(super_class) + for name in dir(taurusaction): + klass = getattr(taurusaction, name) + if klass == super_class: + continue + if (type(klass) == klass_type and issubclass(klass, super_class)): + ret[klass.menuID] = klass + return ret + + def __getActions(self): + """Calculates the map of existing action classes""" + return self.__getClasses(taurusaction.TaurusAction) + + def __getMenus(self): + """Calculates the map of existing menu classes""" + return self.__getClasses(taurusaction.TaurusMenu) + + def getActions(self): + return self.actions + + def getMenus(self): + return self.menus + + def getNewAction(self, widget, id): + klass = self.actions.get(id) + if klass is None: + return None + return klass(widget) + + def getNewMenu(self, widget, data): + import xml.dom.minidom + doc = xml.dom.minidom.parseString(data) + m_node = doc.childNodes[0] + return self.buildMenu(widget, m_node) + + def buildAction(self, widget, a_node): + if not a_node.hasAttribute('class'): + return None + + id = a_node.getAttribute('class') + action = self.getNewAction(widget, id) + + # if node has alternative label, display it instead + if a_node.hasAttribute('label'): + action.setText(a_node.getAttribute('label')) + + if a_node.hasAttribute('checkable'): + action.setCheckable(bool(a_node.getAttribute('checkable'))) + + if a_node.hasAttribute('icon'): + icon = a_node.getAttribute('icon') + #TODO + #action.setIcon(icon) + return action + + def buildMenu(self, widget, m_node): + menu = None + if m_node.hasAttribute('class'): + klass = self.menus.get(m_node.getAttribute('class')) + if klass is None: + return None + menu = klass(widget) + else: + menu = taurusaction.TaurusMenu(widget) + menu.buildFromXML(m_node) + return menu + + def createAction(self, parent, text, shortcut=None, icon=None, tip=None, + toggled=None, triggered=None, data=None, + context=Qt.Qt.WindowShortcut): + """Create a QAction""" + action = Qt.QAction(text, parent) + if triggered is not None: + parent.connect(action, Qt.SIGNAL("triggered()"), triggered) + if toggled is not None: + parent.connect(action, Qt.SIGNAL("toggled(bool)"), toggled) + action.setCheckable(True) + if icon is not None: + if isinstance(icon, (str, unicode)): + icon = getThemeIcon(icon) + action.setIcon( icon ) + if shortcut is not None: + action.setShortcut(shortcut) + if tip is not None: + action.setToolTip(tip) + action.setStatusTip(tip) + if data is not None: + action.setData(data) + #TODO: Hard-code all shortcuts and choose context=Qt.WidgetShortcut + # (this will avoid calling shortcuts from another dockwidget + # since the context thing doesn't work quite well with these widgets) + action.setShortcutContext(context) + return action
\ No newline at end of file diff --git a/lib/taurus/qt/qtgui/util/tauruscolor.py b/lib/taurus/qt/qtgui/util/tauruscolor.py new file mode 100644 index 00000000..eb450efe --- /dev/null +++ b/lib/taurus/qt/qtgui/util/tauruscolor.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python + +############################################################################# +## +## This file is part of Taurus +## +## http://taurus-scada.org +## +## Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain +## +## Taurus is free software: you can redistribute it and/or modify +## it under the terms of the GNU Lesser General Public License as published by +## the Free Software Foundation, either version 3 of the License, or +## (at your option) any later version. +## +## Taurus 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 Taurus. If not, see <http://www.gnu.org/licenses/>. +## +############################################################################# + +"""This module provides Qt color management for taurus""" + +__docformat__ = 'restructuredtext' + +from taurus.external.qt import Qt + +from taurus.core.util.colors import ColorPalette, \ + DEVICE_STATE_DATA, ATTRIBUTE_QUALITY_DATA + +class QtColorPalette(ColorPalette): + + def __init__(self, dat, int_decoder_dict): + ColorPalette.__init__(self, dat, int_decoder_dict) + self._qcolor_cache_fg = dict() + self._qcolor_cache_bg = dict() + self._qbrush_cache_fg = dict() + self._qbrush_cache_bg = dict() + self._qvariant_cache_fg = dict() + self._qvariant_cache_bg = dict() + + def qbrush(self, stoq): + #print stoq + """Returns the brush for the specified state or quality""" + name = self._decoder(stoq) + + f = self._qbrush_cache_fg + b = self._qbrush_cache_bg + if not f.has_key(name): + f[name] = Qt.QBrush(self.qcolor(stoq)[1]) + + if not b.has_key(name): + b[name] = Qt.QBrush(self.qcolor(stoq)[0]) + if name == 'None': + b[name].setStyle(Qt.Qt.BDiagPattern) + + return ( b[name], f[name] ) + + def qcolor(self, stoq): + """Returns the color for the specified state or quality""" + name = self._decoder(stoq) + + f = self._qcolor_cache_fg + b = self._qcolor_cache_bg + if not f.has_key(name): + f[name] = Qt.QColor(self.number(name, True)) + + if not b.has_key(name): + b[name] = Qt.QColor(self.number(name)) + + return ( b[name], f[name] ) + + def qvariant(self, stoq): + """Returns the color for the specified state or quality""" + name = self._decoder(stoq) + + f = self._qvariant_cache_fg + b = self._qvariant_cache_bg + if not f.has_key(name): + (back,fore) = self.qcolor(name) + f[name] = Qt.QVariant(fore) + b[name] = Qt.QVariant(back) + + return ( b[name], f[name] ) + +import PyTango + +QT_DEVICE_STATE_PALETTE = QtColorPalette(DEVICE_STATE_DATA, PyTango.DevState) +QT_ATTRIBUTE_QUALITY_PALETTE = QtColorPalette(ATTRIBUTE_QUALITY_DATA, PyTango.AttrQuality) diff --git a/lib/taurus/qt/qtgui/util/taurusropepatch.py b/lib/taurus/qt/qtgui/util/taurusropepatch.py new file mode 100644 index 00000000..4c7a4291 --- /dev/null +++ b/lib/taurus/qt/qtgui/util/taurusropepatch.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python
+
+#############################################################################
+##
+## This file is part of Taurus
+##
+## http://taurus-scada.org
+##
+## Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain
+##
+## Taurus is free software: you can redistribute it and/or modify
+## it under the terms of the GNU Lesser General Public License as published by
+## the Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## Taurus 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 Taurus. If not, see <http://www.gnu.org/licenses/>.
+##
+#############################################################################
+
+"""Rope patch for better performances. Based on spyderlib.rope_patch"""
+
+__all__ = ["apply"]
+
+__docformat__ = 'restructuredtext'
+
+def apply():
+ """Monkey patching rope for better performances"""
+ import rope
+ if rope.VERSION not in ('0.9.3', '0.9.2'):
+ raise ImportError, "rope %s can't be patched" % rope.VERSION
+
+ # Patching pycore.PyCore, so that forced builtin modules (i.e. modules
+ # that were declared as 'extension_modules' in rope preferences)
+ # will be indeed recognized as builtins by rope, as expected
+ from rope.base import pycore
+ class PatchedPyCore(pycore.PyCore):
+ def get_module(self, name, folder=None):
+ """Returns a `PyObject` if the module was found."""
+ # check if this is a builtin module
+ pymod = self._builtin_module(name)
+ if pymod is not None:
+ return pymod
+ module = self.find_module(name, folder)
+ if module is None:
+ raise pycore.ModuleNotFoundError(
+ 'Module %s not found' % name)
+ return self.resource_to_pyobject(module)
+ pycore.PyCore = PatchedPyCore
+
+ # Patching BuiltinFunction for the calltip/doc functions to be
+ # able to retrieve the function signatures with forced builtins
+ from rope.base import builtins, pyobjects
+ from spyderlib.utils.dochelpers import getargs
+ class PatchedBuiltinFunction(builtins.BuiltinFunction):
+ def __init__(self, returned=None, function=None, builtin=None,
+ argnames=[], parent=None):
+ builtins._BuiltinElement.__init__(self, builtin, parent)
+ pyobjects.AbstractFunction.__init__(self)
+ self.argnames = argnames
+ if not argnames and builtin:
+ self.argnames = getargs(self.builtin)
+ if self.argnames is None:
+ self.argnames = []
+ self.returned = returned
+ self.function = function
+ builtins.BuiltinFunction = PatchedBuiltinFunction
+
+ # Patching BuiltinName for the go to definition feature to simply work
+ # with forced builtins
+ from rope.base import libutils
+ import inspect
+ class PatchedBuiltinName(builtins.BuiltinName):
+ def _pycore(self):
+ p = self.pyobject
+ while p.parent is not None:
+ p = p.parent
+ if isinstance(p, builtins.BuiltinModule) and p.pycore is not None:
+ return p.pycore
+ def get_definition_location(self):
+ if not inspect.isbuiltin(self.pyobject):
+ _lines, lineno = inspect.getsourcelines(self.pyobject.builtin)
+ path = inspect.getfile(self.pyobject.builtin)
+ pycore = self._pycore()
+ if pycore and pycore.project:
+ resource = libutils.path_to_resource(pycore.project, path)
+ module = pyobjects.PyModule(pycore, None, resource)
+ return (module, lineno)
+ return (None, None)
+ builtins.BuiltinName = PatchedBuiltinName
diff --git a/lib/taurus/qt/qtgui/util/taurusscreenshot.py b/lib/taurus/qt/qtgui/util/taurusscreenshot.py new file mode 100644 index 00000000..8dea6e6d --- /dev/null +++ b/lib/taurus/qt/qtgui/util/taurusscreenshot.py @@ -0,0 +1,136 @@ +#!/usr/bin/env python + +############################################################################# +## +## This file is part of Taurus +## +## http://taurus-scada.org +## +## Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain +## +## Taurus is free software: you can redistribute it and/or modify +## it under the terms of the GNU Lesser General Public License as published by +## the Free Software Foundation, either version 3 of the License, or +## (at your option) any later version. +## +## Taurus 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 Taurus. If not, see <http://www.gnu.org/licenses/>. +## +############################################################################# + +"""This module provides Qt color management for taurus""" + +__all__ = ["Grabber", "grabWidget"] + +__docformat__ = 'restructuredtext' + +import time +import threading +import os.path + +from taurus.external.qt import Qt +from taurus.core.util.log import Logger + +_LOGGER = None + +def _getLogger(): + global _LOGGER + if _LOGGER is None: + _LOGGER = Logger('Grabber') + return _LOGGER + + +class GrabberThread(threading.Thread): + + def __init__(self, widget, fileName, period): + threading.Thread.__init__(self, name="Grabber") + self.daemon = True + if period <= 0: + raise ValueError("period MUST be greater than 0") + self._period = period + self._continue = True + self._grabber = Grabber(widget, fileName) + + def run(self): + period = self._period + while self._continue: + self._grabber.grabTrigger() + time.sleep(period) + + def stop(self): + self._continue = False + + +class Grabber(Qt.QObject, Logger): + + def __init__(self, widget, fileName): + Qt.QObject.__init__(self) + Logger.__init__(self) + self._widget = widget + self._fileName = fileName + self.connect(self, Qt.SIGNAL("grab"), self.onGrab) + + def grabTrigger(self): + self.emit(Qt.SIGNAL("grab")) + + def onGrab(self): + Grabber._grabWidget(self._widget, self._fileName) + + @staticmethod + def _grabWidget(widget, fileName): + _getLogger().debug("Grabbing widget to '%s':", fileName) + try: + pixmap = Qt.QPixmap.grabWidget(widget) + if fileName.endswith('.svg'): + from taurus.external.qt import QtSvg + generator = QtSvg.QSvgGenerator() + generator.setFileName(fileName) + generator.setSize(pixmap.size()); + if hasattr(generator, 'setViewBox'): + viewBox = Qt.QRect(Qt.QPoint(0, 0), pixmap.size()) + generator.setViewBox(viewBox) + generator.setTitle("Taurus widget") + generator.setDescription("An SVG drawing created by the taurus " + "widget grabber") + painter = Qt.QPainter() + painter.begin(generator) + try: + painter.drawPixmap(0, 0, -1, -1, pixmap) + finally: + painter.end() + else: + pixmap.save(fileName, quality=100) + except: + _getLogger().warning("Could not save file into '%s':", fileName, + exc_info=1) + + @staticmethod + def grabWidget(widget, fileName, period=None): + """Grabs the given widget into the given image filename. If period is + not given (or given with None) means grab immediately once and return. + If period is given and >0 means grab the image every period seconds + + .. warning: + this method **MUST** be called from the same thread which created + the widget + + :param widget: (Qt.QWidget) the qt widget to be grabbed + :param fileName: (str) the name of the image file + :param period: (float) period (seconds) + """ + if period is None: + return Grabber._grabWidget(widget, fileName) + ret = GrabberThread(widget, fileName, period) + ret.start() + return ret + + +def grabWidget(widget, fileName, period=None): + return Grabber.grabWidget(widget, fileName, period=period) + +grabWidget.__doc__ = Grabber.grabWidget.__doc__ diff --git a/lib/taurus/qt/qtgui/util/tauruswidget_qtdesignerplugin_template b/lib/taurus/qt/qtgui/util/tauruswidget_qtdesignerplugin_template new file mode 100644 index 00000000..d76bfef9 --- /dev/null +++ b/lib/taurus/qt/qtgui/util/tauruswidget_qtdesignerplugin_template @@ -0,0 +1,21 @@ +import tauplugin + +from <_Module_> import <_TauClass_> + +class <_TauClass_>Plugin(tauplugin.TauWidgetPlugin): + + """<_TauClass_>Plugin(tauplugin.TauWidgetPlugin) + + Provides a Python custom plugin for Qt Designer by implementing the + QDesignerCustomWidgetPlugin via a PyQt-specific custom plugin class. + """ + + def getWidgetClass(self): + return <_TauClass_> + + def getIconName(self): + # give your widget icon file name here + return 'label.png' + + def includeFile(self): + return "<_Module_>"
\ No newline at end of file diff --git a/lib/taurus/qt/qtgui/util/tauruswidget_template b/lib/taurus/qt/qtgui/util/tauruswidget_template new file mode 100644 index 00000000..3cab2f00 --- /dev/null +++ b/lib/taurus/qt/qtgui/util/tauruswidget_template @@ -0,0 +1,141 @@ +#!/usr/bin/env python + +""" module containing the Tau Widget: <_TauClass_> """ + +from taurus.qt import QtGui, QtCore +import tau.core +from tau.widget import TauBaseWidget + +class <_TauClass_>(<_SuperClass_>, TauBaseWidget): + """ <_TauClass_> is a Tau widget designed to represent...""" + + #--------------------------------------------------------------------------- + # Write your own code here to define the signals generated by this widget + # + __pyqtSignals__ = ("modelChanged(const QString &)",) + + def __init__(self, parent = None, designMode = False): + name = self.__class__.__name__ + + self.call__init__wo_kw(<_SuperClass_>, parent) + self.call__init__(TauBaseWidget, name, parent, designMode=designMode) + + self.defineStyle() + + def defineStyle(self): + """ Defines the initial style for the widget """ + #----------------------------------------------------------------------- + # Write your own code here to set the initial style of your widget + # + self.updateStyle() + + def sizeHint(self): + return <_SuperClass_>.sizeHint(self) + + def minimumSizeHint(self): + return <_SuperClass_>.minimumSizeHint(self) + + #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- + # TauBaseWidget over writing + #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- + + def getModelClass(self): + #----------------------------------------------------------------------- + # [MANDATORY] + # Replace your own code here + # ex.: return tau.core.Attribute + raise RuntimeError("Forgot to overwrite %s.getModelClass" % str(self)) + + + def attach(self): + """Attaches the widget to the model""" + + if self.isAttached(): + return True + + #----------------------------------------------------------------------- + # Write your own code here before attaching widget to attribute connect + # the proper signal so that the first event is correctly received by the + # widget + # + # Typical code is: + #self.connect(self, QtCore.SIGNAL('valueChangedDueToEvent(QString)'), + # self.setTextValue) + + + ret = TauBaseWidget.attach(self) + + # by default enable/disable widget according to attach state + self.setEnabled(ret) + return ret + + def detach(self): + """Detaches the widget from the model""" + + TauBaseWidget.detach(self) + + #----------------------------------------------------------------------- + # Write your own code here after detaching the widget from the model + # + # Typical code is: + #self.emit(QtCore.SIGNAL('valueChangedDueToEvent(QString)'), + # QtCore.QString(value_str)) + #self.disconnect(self, QtCore.SIGNAL('valueChangedDueToEvent(QString)'), + # self.setTextValue) + + # by default disable widget when dettached + self.setEnabled(False) + + def eventReceived(self, src, type, data): + """ eventReceived(src, TauEventType type, data) -> None + + Called by the model when an event is fired. + + Parameters: + src: Source of the event. Usually a tau.core.Attribute or + tau.core.Device object + type: a TauEventType describing the type of event. + data: A PyTango object with the event data. It can be None. + - For TauEventType.change events is a PyTango.AttributeValue + object or None; + - For TauEventType.attr_conf events is a + PyTango.AttrConfEventData object or None. + Return: + None + """ + #----------------------------------------------------------------------- + # Write your own code here to handle event + pass + + #--------------------------------------------------------------------------- + # [MANDATORY] + # Uncomment the following method if your superclass does not provide with a + # isReadOnly() method or if you need to change its behavior + + #def isReadOnly(self): + # return True + + def updateStyle(self): + #----------------------------------------------------------------------- + # Write your own code here to update your widget style + + # send a repaint in the end + self.repaint() + + #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- + # QT properties + #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- + + model = QtCore.pyqtProperty("QString", TauBaseWidget.getModel, + TauBaseWidget.setModel, + TauBaseWidget.resetModel) + + useParentModel = QtCore.pyqtProperty("bool", + TauBaseWidget.getUseParentModel, + TauBaseWidget.setUseParentModel, + TauBaseWidget.resetUseParentModel) + + #--------------------------------------------------------------------------- + # Write your own code here for your own widget properties + + diff --git a/lib/taurus/qt/qtgui/util/tauruswidgetfactory.py b/lib/taurus/qt/qtgui/util/tauruswidgetfactory.py new file mode 100644 index 00000000..8a45c5f2 --- /dev/null +++ b/lib/taurus/qt/qtgui/util/tauruswidgetfactory.py @@ -0,0 +1,209 @@ +#!/usr/bin/env python + +############################################################################# +## +## This file is part of Taurus +## +## http://taurus-scada.org +## +## Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain +## +## Taurus is free software: you can redistribute it and/or modify +## it under the terms of the GNU Lesser General Public License as published by +## the Free Software Foundation, either version 3 of the License, or +## (at your option) any later version. +## +## Taurus 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 Taurus. If not, see <http://www.gnu.org/licenses/>. +## +############################################################################# + +""" +tauruswidgetfactory.py: +""" + +__all__ = ["TaurusWidgetFactory", "getWidgetsOfType"] + +__docformat__ = 'restructuredtext' + +import imp +import os.path + +from taurus.external.qt import Qt + +from taurus.core.util.log import Logger +from taurus.core.util.singleton import Singleton + +import taurus.qt.qtgui.base + +def _getWidgetsOfType(widget, widgets, class_or_type_or_tuple): + + if isinstance(widget, class_or_type_or_tuple): + widgets.append(widget) + for w in widget.children(): + if isinstance(w, Qt.QWidget): + _getWidgetsOfType(w, widgets, class_or_type_or_tuple) + +def getWidgetsOfType(widget, class_or_type_or_tuple): + """Returns all widgets in a hierarchy of a certain type + + :param widget: the widget to be inspected + :type widget: Qt.QWidget + :param class-or-type-or-tuple: type to be checked + :type class-or-type-or-tuple: type class or a tuple of type classes + + :return: a sequence containning all widgets in the hierarchy that match the + given type + :rtype: seq<Qt.QWidget>""" + widgets = [] + _getWidgetsOfType(widget, widgets, class_or_type_or_tuple) + return widgets + + +class TaurusWidgetFactory(Singleton, Logger): + """The TaurusWidgetFactory is a utility class that provides information + about all Qt widgets (Taurus and non Taurus) that are found in the + current taurus distribution. + TaurusWidgetFactory is a singleton class so you can freely create new + instances since they will all reference the same object. + Usage:: + + from taurus.qt.qtgui.util import TaurusWidgetFactory + + wf = TaurusWidgetFactory() + print wf.getTaurusWidgetClassNames()""" + + skip_modules = ('widget', 'util', 'qtdesigner', 'uic') + + def __init__(self): + """ Initialization. Nothing to be done here for now.""" + + def init(self, *args): + """Singleton instance initialization.""" + name = self.__class__.__name__ + self.call__init__(Logger, name) + + path = os.path.dirname(os.path.abspath(__file__)) + path, tail = os.path.split(path) + self._taurus_widgets, self._qt_widgets = self._buildWidgets('taurus.qt.qtgui', path) + self._addExtraTaurusWidgets(self._taurus_widgets, self._qt_widgets) + + def _buildWidgets(self, module_name, path, recursive = True): + import taurus.qt.qtgui.base + + if Qt.QApplication.instance() is None: + app = Qt.QApplication([]) + #raise Exception("Cannot build widget list without an instance of "\ + # "QApplication. Please create one before.") + + elems = os.listdir(path) + taurus_ret, qt_ret = {}, {} + if not '__init__.py' in elems: + return taurus_ret, qt_ret + + try: + m = __import__(module_name, fromlist=['*'], level=0) + dir_names = dir(m) + for dir_name in dir_names: + if dir_name.startswith("_"): + continue + try: + attr = getattr(m, dir_name) + if issubclass(attr, Qt.QWidget): + package = m.__package__ + qt_ret[dir_name] = package, attr + if issubclass(attr, taurus.qt.qtgui.base.TaurusBaseWidget): + taurus_ret[dir_name] = package, attr + except Exception, e: + pass + except Exception,e: + return taurus_ret, qt_ret + + if not recursive: + return taurus_ret, qt_ret + + for elem in elems: + abs_elem = os.path.join(path, elem) + if (not elem.startswith('.')) and os.path.isdir(abs_elem) and \ + (not elem in self.skip_modules): + m_name = os.path.splitext(elem)[0] + new_module_name = '%s.%s' % (module_name, m_name) + new_taurus_ret, new_qt_ret = self._buildWidgets(new_module_name, abs_elem, True) + taurus_ret.update(new_taurus_ret) + qt_ret.update(new_qt_ret) + return taurus_ret, qt_ret + + def _addExtraTaurusWidgets(self, taurus_ret, qt_widgets): + designer_path = os.environ.get('TAURUSQTDESIGNERPATH') + if designer_path is None: + return taurus_ret + designer_path = designer_path.split(os.path.pathsep) + for path in designer_path: + self._addExtraTaurusWidgetsPath(taurus_ret, qt_widgets, path) + + def _addExtraTaurusWidgetsPath(self, taurus_ret, qt_widgets, path): + self.debug("Trying extra taurus widgets in %s", path) + path = os.path.abspath(path) + if not os.path.isdir(path): + return + elems = os.listdir(path) + for elem in elems: + m_name, ext = os.path.splitext(elem) + if ext != '.py': continue + try: + self.debug("Trying to find extra module %s", m_name) + f, fname, data = imp.find_module(m_name, [path]) + except ImportError, ie: + self.debug("Could not find extra module %s:%s", m_name, ie) + continue + try: + self.debug("Trying to load extra module %s", m_name) + mod = imp.load_module(m_name, f, fname, data) + except ImportError, ie: + self.debug("Could not load extra module %s:%s", m_name, ie) + continue + dir_names = dir(mod) + for dir_name in dir_names: + if dir_name.startswith("_"): + continue + if dir_name in taurus_ret: + continue + try: + attr = getattr(mod, dir_name) + if issubclass(attr, Qt.QWidget): + if issubclass(attr, taurus.qt.qtgui.base.TaurusBaseWidget): + qt_info = attr.getQtDesignerPluginInfo() + taurus_ret[dir_name] = qt_info['module'], attr + qt_widgets[dir_name] = qt_info['module'], attr + self.debug("registered taurus widget %s", dir_name) + except Exception, e: + pass + + def getWidgets(self): + return self._qt_widgets + + def getTaurusWidgets(self): + return self._taurus_widgets + + def getWidgetClassNames(self): + return self._qt_widgets.keys() + + def getWidgetClasses(self): + return [ klass for mod_name, klass in self._qt_widgets.values()] + + def getWidgetClass(self, name): + return self._qt_widgets[name][1] + + def getTaurusWidgetClassNames(self): + return self._taurus_widgets.keys() + + def getTaurusWidgetClasses(self): + return [ klass for mod_name, klass in self._taurus_widgets.values()] + + def getTaurusWidgetClass(self, name): + return self._taurus_widgets.get(name)[1] diff --git a/lib/taurus/qt/qtgui/util/tauruswidgettree.py b/lib/taurus/qt/qtgui/util/tauruswidgettree.py new file mode 100644 index 00000000..dbaecc47 --- /dev/null +++ b/lib/taurus/qt/qtgui/util/tauruswidgettree.py @@ -0,0 +1,229 @@ +#!/usr/bin/env python + +############################################################################# +## +## This file is part of Taurus +## +## http://taurus-scada.org +## +## Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain +## +## Taurus is free software: you can redistribute it and/or modify +## it under the terms of the GNU Lesser General Public License as published by +## the Free Software Foundation, either version 3 of the License, or +## (at your option) any later version. +## +## Taurus 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 Taurus. If not, see <http://www.gnu.org/licenses/>. +## +############################################################################# + +""" +""" + +__all__ = ["QObjectRepresentation", "get_qobject_tree", "get_qobject_tree_str", + "TreeQObjectModel", "TreeQObjectWidget"] + +__docformat__ = 'restructuredtext' + +import weakref + +from taurus.external.qt import Qt + +from taurus.core.util.enumeration import Enumeration + +QObjectRepresentation = Enumeration('QObjectRepresentation', + ('ClassName', 'ObjectName', 'FullName')) + +def _build_qobjects_as_dict(qobject, container): + + container[qobject] = childs = {} + for child in qobject.children(): + if isinstance(child, Qt.QWidget): + _build_qobjects_as_dict(child, childs) + +def get_qobject_tree_as_dict(qobject=None): + + if qobject is None: + app = Qt.QApplication.instance() + qobjects = app.topLevelWidgets() + else: + qobjects = [qobject] + + tree = {} + for qobject in qobjects: + _build_qobjects_as_dict(qobject, tree) + + return tree + +def _build_qobjects_as_list(qobject, container): + + children = qobject.children() + node = qobject, [] + container.append(node) + for child in children: + if isinstance(child, Qt.QWidget): + _build_qobjects_as_list(child, node[1]) + +def get_qobject_tree_as_list(qobject=None): + + if qobject is None: + app = Qt.QApplication.instance() + qobjects = app.topLevelWidgets() + else: + qobjects = [qobject] + + tree = [] + for qobject in qobjects: + _build_qobjects_as_list(qobject, tree) + + return tree + +get_qobject_tree = get_qobject_tree_as_list + +def _get_qobject_str(qobject, representation): + if representation == QObjectRepresentation.ClassName: + return qobject.__class__.__name__ + elif representation == QObjectRepresentation.ObjectName: + return str(qobject.objectName()) + elif representation == QObjectRepresentation.FullName: + return '{0}("{1}")'.format(qobject.__class__.__name__, str(qobject.objectName())) + return str(qobject) + +def _build_qobject_str(node, str_tree, representation=QObjectRepresentation.ClassName): + + qobject, children = node + str_node = _get_qobject_str(qobject, representation) + str_children = [] + str_tree.append((str_node, str_children)) + for child in children: + _build_qobject_str(child, str_children, representation=representation) + +def get_qobject_tree_str(qobject=None, representation=QObjectRepresentation.ClassName): + + tree, str_tree = get_qobject_tree(qobject=qobject), [] + for e in tree: + _build_qobject_str(e, str_tree, representation=representation) + return str_tree + + +from taurus.qt.qtgui.tree.qtree import QBaseTreeWidget +from taurus.qt.qtcore.model import TaurusBaseModel, TaurusBaseTreeItem + +QR = QObjectRepresentation + + +class TreeQObjecttItem(TaurusBaseTreeItem): + + def __init__(self, model, data, parent = None): + TaurusBaseTreeItem.__init__(self, model, data, parent=parent) + if data is not None: + self.qobject = weakref.ref(data) + dat = _get_qobject_str(data, QR.ClassName), \ + _get_qobject_str(data, QR.ObjectName) + self.setData(0, dat) + + +class TreeQObjectModel(TaurusBaseModel): + + ColumnNames = "Class", "Object name" + ColumnRoles = (QR.ClassName,), QR.ObjectName + + def __init__(self, parent=None, data=None): + TaurusBaseModel.__init__(self, parent=parent, data=data) + +# def createNewRootItem(self): +# return TreeQObjecttItem(self, self.ColumnNames) + + def role(self, column, depth=0): + if column == 0: + return self.ColumnRoles[column][0] + return self.ColumnRoles[column] + + def roleIcon(self, taurus_role): + return Qt.QIcon() + + def roleSize(self, taurus_role): + return Qt.QSize(300, 70) + + def roleToolTip(self, role): + return "widget information" + + @staticmethod + def _build_qobject_item(model, parent, node): + qobject, children = node + item = TreeQObjecttItem(model, qobject, parent) + parent.appendChild(item) + for child in children: + TreeQObjectModel._build_qobject_item(model, item, child) + + def setupModelData(self, data): + if data is None: + return + rootItem = self._rootItem + for node in data: + TreeQObjectModel._build_qobject_item(self, rootItem, node) + + +class TreeQObjectWidget(QBaseTreeWidget): + + KnownPerspectives = { + "Default" : { + "label" : "Default perspecive", + "tooltip" : "QObject tree view", + "icon" : "", + "model" : [TreeQObjectModel], + }, + } + + DftPerspective = "Default" + + + def __init__(self, parent=None, designMode=False, with_navigation_bar=True, + with_filter_widget=True, perspective=None, proxy=None, + qobject_root=None): + QBaseTreeWidget.__init__(self, parent, designMode=designMode, + with_navigation_bar=with_navigation_bar, + with_filter_widget=with_filter_widget, + perspective=perspective, proxy=proxy) + qmodel = self.getQModel() + qmodel.setDataSource(get_qobject_tree(qobject=qobject_root)) + + +def build_gui(): + mw = Qt.QMainWindow() + mw.setObjectName("main window") + w = Qt.QWidget() + w.setObjectName("central widget") + mw.setCentralWidget(w) + l = Qt.QVBoxLayout() + w.setLayout(l) + l1 = Qt.QLabel("H1") + l1.setObjectName("label 1") + l.addWidget(l1) + l2 = Qt.QLabel("H2") + l2.setObjectName("label 2") + l.addWidget(l2) + mw.show() + return mw + + +def main(): + from taurus.qt.qtgui.application import TaurusApplication + app = TaurusApplication() + + w = build_gui() + tree = TreeQObjectWidget(qobject_root=w) + tree.show() + #import pprint + #pprint.pprint(get_qobject_tree_str()) + w.dumpObjectTree() + app.exec_() + +if __name__ == "__main__": + main() diff --git a/lib/taurus/qt/qtgui/util/test/__init__.py b/lib/taurus/qt/qtgui/util/test/__init__.py new file mode 100644 index 00000000..bfa1d383 --- /dev/null +++ b/lib/taurus/qt/qtgui/util/test/__init__.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python + +############################################################################# +## +## This file is part of Taurus +## +## http://taurus-scada.org +## +## Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain +## +## Taurus is free software: you can redistribute it and/or modify +## it under the terms of the GNU Lesser General Public License as published by +## the Free Software Foundation, either version 3 of the License, or +## (at your option) any later version. +## +## Taurus 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 Taurus. If not, see <http://www.gnu.org/licenses/>. +## +############################################################################# + +"""tests for taurus.qt.qtgui.util""" diff --git a/lib/taurus/qt/qtgui/util/test/test_ui/__init__.py b/lib/taurus/qt/qtgui/util/test/test_ui/__init__.py new file mode 100644 index 00000000..c8c1e555 --- /dev/null +++ b/lib/taurus/qt/qtgui/util/test/test_ui/__init__.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python + +############################################################################# +## +## This file is part of Taurus +## +## http://taurus-scada.org +## +## Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain +## +## Taurus is free software: you can redistribute it and/or modify +## it under the terms of the GNU Lesser General Public License as published by +## the Free Software Foundation, either version 3 of the License, or +## (at your option) any later version. +## +## Taurus 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 Taurus. If not, see <http://www.gnu.org/licenses/>. +## +############################################################################# + +"""This package provides a set of taurus wiget utilities like color management, +configuration, actions.""" + +from .test_ui import * diff --git a/lib/taurus/qt/qtgui/util/test/test_ui/mywidget3/__init__.py b/lib/taurus/qt/qtgui/util/test/test_ui/mywidget3/__init__.py new file mode 100644 index 00000000..9ffe6b53 --- /dev/null +++ b/lib/taurus/qt/qtgui/util/test/test_ui/mywidget3/__init__.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python + +############################################################################# +## +## This file is part of Taurus +## +## http://taurus-scada.org +## +## Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain +## +## Taurus is free software: you can redistribute it and/or modify +## it under the terms of the GNU Lesser General Public License as published by +## the Free Software Foundation, either version 3 of the License, or +## (at your option) any later version. +## +## Taurus 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 Taurus. If not, see <http://www.gnu.org/licenses/>. +## +############################################################################# + +from taurus.external.qt import Qt +from taurus.qt.qtgui.util.ui import UILoadable + +@UILoadable +class MyWidget3(Qt.QWidget): + + def __init__(self, parent=None): + Qt.QWidget.__init__(self, parent) + self.loadUi() diff --git a/lib/taurus/qt/qtgui/util/test/test_ui/mywidget3/ui/MyWidget3.ui b/lib/taurus/qt/qtgui/util/test/test_ui/mywidget3/ui/MyWidget3.ui new file mode 100644 index 00000000..7dcbbfa0 --- /dev/null +++ b/lib/taurus/qt/qtgui/util/test/test_ui/mywidget3/ui/MyWidget3.ui @@ -0,0 +1,32 @@ +<?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>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <widget class="QPushButton" name="my_button"> + <property name="geometry"> + <rect> + <x>120</x> + <y>80</y> + <width>91</width> + <height>27</height> + </rect> + </property> + <property name="text"> + <string>PushButton</string> + </property> + </widget> + </widget> + <resources/> + <connections/> +</ui> diff --git a/lib/taurus/qt/qtgui/util/test/test_ui/test_ui.py b/lib/taurus/qt/qtgui/util/test/test_ui/test_ui.py new file mode 100644 index 00000000..3673099c --- /dev/null +++ b/lib/taurus/qt/qtgui/util/test/test_ui/test_ui.py @@ -0,0 +1,102 @@ +# -*- coding: utf-8 -*- + +############################################################################# +## +## This file is part of Taurus +## +## http://taurus-scada.org +## +## Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain +## +## Taurus is free software: you can redistribute it and/or modify +## it under the terms of the GNU Lesser General Public License as published by +## the Free Software Foundation, either version 3 of the License, or +## (at your option) any later version. +## +## Taurus 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 Taurus. If not, see <http://www.gnu.org/licenses/>. +## +############################################################################# + +"""Unit tests for UILoadable decorator""" + +import os.path + +from taurus.external import unittest +from taurus.external.qt import Qt +from taurus.qt.qtgui.util.ui import UILoadable +from taurus.qt.qtgui.test import BaseWidgetTestCase +from mywidget3 import MyWidget3 + + +class UILoadableTestCase(unittest.TestCase): + """ + Test cases for UILoadable decorator + """ + + @UILoadable + class MyWidget1(Qt.QWidget): + + def __init__(self, parent=None): + Qt.QWidget.__init__(self, parent) + self.loadUi() + self.my_button.setText("This is MY1 button") + + @UILoadable(with_ui="ui") + class MyWidget2(Qt.QWidget): + + def __init__(self, parent=None): + Qt.QWidget.__init__(self, parent) + path = os.path.join(os.path.dirname(__file__), "ui", "mywidget2") + self.loadUi(filename="mywidget2_custom.ui", path=path) + self.ui.my_button.setText("This is MY2 button") + + def setUp(self): + app = Qt.QApplication.instance() + if app is None: + app = Qt.QApplication([]) + self.__app = app + + def test_uiloadable_default(self): + """Test UILoadable with default arguments""" + widget = self.MyWidget1() + self.assertEquals(widget.my_button.text(), "This is MY1 button", + "button text differs from expected") + + def test_uiloadable_customized(self): + """Test UILoadable with customized filename and path""" + widget = self.MyWidget2() + self.assertTrue(hasattr(widget, "ui"), + "widget doesn't have 'ui' member") + self.assertTrue(hasattr(widget.ui, "my_button"), + "widget.ui doesn't have a 'my_button' member") + self.assertFalse(hasattr(widget, "my_button"), + "widget has a my_button member") + self.assertEquals(widget.ui.my_button.text(), "This is MY2 button", + "button text differs from expected") + + +class Bug339_TestCase(BaseWidgetTestCase, unittest.TestCase): + '''Test for bug 339: https://sourceforge.net/p/sardana/tickets/339/''' + def test_bug339(self): + '''Check inheritance of UILoadable classes across packages (bug #339) + ''' + class Bug339_Widget(MyWidget3): + pass + try: + Bug339_Widget() + except: + self.fail('Inheriting from UILoadable from another package fails') + + +def main(): + unittest.main() + + +if __name__ == "__main__": + main() diff --git a/lib/taurus/qt/qtgui/util/test/test_ui/ui/MyWidget1.ui b/lib/taurus/qt/qtgui/util/test/test_ui/ui/MyWidget1.ui new file mode 100644 index 00000000..7dcbbfa0 --- /dev/null +++ b/lib/taurus/qt/qtgui/util/test/test_ui/ui/MyWidget1.ui @@ -0,0 +1,32 @@ +<?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>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <widget class="QPushButton" name="my_button"> + <property name="geometry"> + <rect> + <x>120</x> + <y>80</y> + <width>91</width> + <height>27</height> + </rect> + </property> + <property name="text"> + <string>PushButton</string> + </property> + </widget> + </widget> + <resources/> + <connections/> +</ui> diff --git a/lib/taurus/qt/qtgui/util/test/test_ui/ui/mywidget2/mywidget2_custom.ui b/lib/taurus/qt/qtgui/util/test/test_ui/ui/mywidget2/mywidget2_custom.ui new file mode 100644 index 00000000..7dcbbfa0 --- /dev/null +++ b/lib/taurus/qt/qtgui/util/test/test_ui/ui/mywidget2/mywidget2_custom.ui @@ -0,0 +1,32 @@ +<?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>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <widget class="QPushButton" name="my_button"> + <property name="geometry"> + <rect> + <x>120</x> + <y>80</y> + <width>91</width> + <height>27</height> + </rect> + </property> + <property name="text"> + <string>PushButton</string> + </property> + </widget> + </widget> + <resources/> + <connections/> +</ui> diff --git a/lib/taurus/qt/qtgui/util/ui.py b/lib/taurus/qt/qtgui/util/ui.py new file mode 100644 index 00000000..0c939738 --- /dev/null +++ b/lib/taurus/qt/qtgui/util/ui.py @@ -0,0 +1,187 @@ +# -*- coding: utf-8 -*- + +############################################################################# +## +## This file is part of Taurus +## +## http://taurus-scada.org +## +## Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain +## +## Taurus is free software: you can redistribute it and/or modify +## it under the terms of the GNU Lesser General Public License as published by +## the Free Software Foundation, either version 3 of the License, or +## (at your option) any later version. +## +## Taurus 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 Taurus. If not, see <http://www.gnu.org/licenses/>. +## +############################################################################# + +"""utilities to load ui files for widgets""" + +import os +import sys +import functools + +from taurus.external.qt import Qt +from taurus.external.qt import uic + + +class __UI(object): + pass + + +def loadUi(obj, filename=None, path=None, with_ui=None): + """ + Loads a QtDesigner .ui file into the given widget. + If no filename is given, it tries to load from a file name which is the + widget class name plus the extension ".ui" (example: if your + widget class is called MyWidget it tries to find a MyWidget.ui). + If path is not given it uses the directory where the python file which + defines the widget is located plus a *ui* directory (example: if your widget + is defined in a file /home/homer/workspace/taurusgui/my_widget.py then it uses + the path /home/homer/workspace/taurusgui/ui) + + :param filename: the QtDesigner .ui file name [default: None, meaning + calculate file name with the algorithm explained before] + :type filename: str + :param path: directory where the QtDesigner .ui file is located + [default: None, meaning calculate path with algorithm explained + before] + :type path: str + :param with_ui: if True, the objects defined in the ui file will be + accessible as submembers of an ui member of the widget. If + False, such objects will directly be members of the widget. + :type with_ui: bool + """ + if path is None: + obj_file = sys.modules[obj.__module__].__file__ + path = os.path.join(os.path.dirname(obj_file), 'ui') + if filename is None: + filename = obj.__class__.__name__ + os.path.extsep + 'ui' + full_name = os.path.join(path, filename) + + if with_ui is not None: + ui_obj = __UI() + setattr(obj, with_ui, ui_obj) + previous_members = set(dir(obj)) + + uic.loadUi(full_name, baseinstance=obj) + + post_members = set(dir(obj)) + new_members = post_members.difference(previous_members) + for member_name in new_members: + member = getattr(obj, member_name) + setattr(ui_obj, member_name, member) + delattr(obj, member_name) + else: + uic.loadUi(full_name, baseinstance=obj) + +def UILoadable(klass=None, with_ui=None): + """ + A class decorator intended to be used in a Qt.QWidget to make its UI + loadable from a predefined QtDesigner UI file. + This decorator will add a :func:`loadUi` method to the decorated class and + optionaly a property with a name given by *with_ui* parameter. + + The folowing example assumes the existence of the ui file + :file:`<my_widget_dir>/ui/MyWidget.ui` which is a QWidget panel with *at + least* a QPushButton with objectName *my_button* :: + + from taurus.external.qt import Qt + from taurus.qt.qtgui.util.ui import UILoadable + + @UILoadable + class MyWidget(Qt.QWidget): + + def __init__(self, parent=None): + Qt.QWidget.__init__(self, parent) + self.loadUi() + self.my_button.setText("This is MY button") + + Another example using a :file:`superUI.ui` file in the same directory as + the widget. The widget UI components can be accessed through the widget + member *_ui* :: + + import os.path + + from taurus.external.qt import Qt + from taurus.qt.qtgui.util.ui import UILoadable + + @UILoadable(with_ui="_ui") + class MyWidget(Qt.QWidget): + + def __init__(self, parent=None): + Qt.QWidget.__init__(self, parent) + self.loadUi(filename="superUI.ui", path=os.path.dirname(__file__)) + self._ui.my_button.setText("This is MY button") + + :param with_ui: assigns a member to the decorated class from which you + can access all UI components [default: None, meaning no + member is created] + :type with_ui: str + + .. warning:: + the current implementation (Jul14) doesn't prevent Qt from overloading + any members you might have defined previously by the widget object names + from the UI file. This happens even if *with_ui* parameter is given. + For example, if the UI contains a QPushButton with objectName + *my_button*:: + + @UILoadable(with_ui="_ui") + class MyWidget(Qt.QWidget): + + def __init__(self, parent=None): + Qt.QWidget.__init__(self, parent) + self.my_button = "hello" + self.loadUi() + widget = MyWidget() + print widget.my_button + <PyQt4.QtGui.QPushButton object at 0x159e2f8> + + This little problem should be solved in the next taurus version. + """ + if klass is None: + return functools.partial(UILoadable, with_ui=with_ui) + + klass_name = klass.__name__ + klass_file = sys.modules[klass.__module__].__file__ + klass_path = os.path.join(os.path.dirname(klass_file), 'ui') + + def _loadUi(self, filename=None, path=None): + if filename is None: + filename = klass_name + os.path.extsep + 'ui' + if path is None: + path = klass_path + return loadUi(self, filename=filename, path=path, with_ui=with_ui) + + klass.loadUi = _loadUi + return klass + + +def main(): + from taurus.qt.qtgui.application import TaurusApplication + + app = TaurusApplication([]) + + @UILoadable(with_ui="ui") + class A(Qt.QWidget): + + def __init__(self, parent=None): + Qt.QWidget.__init__(self, parent) + import taurus.qt.qtgui.panel.ui + path = os.path.dirname(taurus.qt.qtgui.panel.ui.__file__) + self.loadUi(filename='TaurusMessagePanel.ui', path=path) + + gui = A() + gui.show() + app.exec_() + +if __name__ == "__main__": + main() diff --git a/lib/taurus/qt/qtgui/util/widgetgen.py b/lib/taurus/qt/qtgui/util/widgetgen.py new file mode 100644 index 00000000..757950aa --- /dev/null +++ b/lib/taurus/qt/qtgui/util/widgetgen.py @@ -0,0 +1,114 @@ +#!/usr/bin/env python + +############################################################################# +## +## This file is part of Taurus +## +## http://taurus-scada.org +## +## Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain +## +## Taurus is free software: you can redistribute it and/or modify +## it under the terms of the GNU Lesser General Public License as published by +## the Free Software Foundation, either version 3 of the License, or +## (at your option) any later version. +## +## Taurus 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 Taurus. If not, see <http://www.gnu.org/licenses/>. +## +############################################################################# + +""" +widgetgen.py: +""" + +#!/usr/bin/env python + +import os, sys + +usage = "usage: %s <widget class name> <widget super class> <output file name> [<qt designer plugin file>]" % os.path.basename(sys.argv[0]) + +DftModule = "taurus.widget" +DftIconFilename = "label.png" +DftIsContainer = str(False) + +def go(class_name, super_class, output_name): + + path = '' + try: + path = os.path.dirname(__file__) + except: + pass + input_name = os.path.realpath(path) + '/tauruswidget_template' + + try: + output = open(output_name, 'w') + except: + raise + + try: + input = open(input_name, 'r') + except: + output.close() + raise + + for s in input: + o = s.replace('<_SuperClass_>', super_class) + o = o.replace('<_TaurusClass_>', class_name) + o = o.replace('<_Module_>', DftModule) + o = o.replace('<_IconFileName_>', DftIconFilename) + o = o.replace('<_Container_>', DftIsContainer) + output.write(o) + + input.close() + output.close() + + +def go_with_designer(class_name, super_class, output_name, plugin_output_name): + + go(class_name, super_class, output_name) + + path = '' + try: + path = os.path.dirname(__file__) + except: + pass + input_name = os.path.realpath(path) + '/tauruswidget_qtdesignerplugin_template' + + try: + output = open(plugin_output_name, 'w') + except: + raise + + try: + input = open(input_name, 'r') + except: + output.close() + raise + + for s in input: + o = s.replace('<_SuperClass_>',super_class) + o = o.replace('<_TaurusClass_>',class_name) + o = o.replace('<_Module_>', DftModule) + o = o.replace('<_IconFileName_>', DftIconFilename) + o = o.replace('<_Container_>', DftIsContainer) + output.write(o) + + input.close() + output.close() + +if __name__ == "__main__": + + argc = len(sys.argv) + if argc < 4: + print usage + elif argc == 4: + go(sys.argv[1],sys.argv[2],sys.argv[3]) + else: + go_with_designer(sys.argv[1],sys.argv[2],sys.argv[3],sys.argv[4]) +
\ No newline at end of file |