Package: qute
#OverviewQute is a wrapped extension of Marcus Ottosson's Qt.py. The emphasis is on
utilising the convience of Qt.py (allowing for use of PyQt, PySide and
PySide2 seamlessly) whilst also exposing a set of common pieces of functionality
we tend to replicate and utilise in many places.#Key FeaturesGeneral UsageimportquteclassMyWidget(qute.QWidget):def__init__(self,parent=None):super(MyWidget,self).__init__(parent=parent)# -- Create a layout and set it as the base layout. Use# -- qute to slim the layout - removing marginsself.setLayout(qute.utilities.layouts.slimify(qute.QVBoxLayout()))# -- Create some widgetsself.spinner=qute.QSpinBox()self.checker=qute.QCheckBox()# -- Add these to our layoutself.layout().addWidget(self.spinner)self.layout().addWidget(self.checker)# -- Finally lets connect some signals and slots without# -- caring what it isqute.connectBlind(self.spinner,self.do_something)qute.connectBlind(self.checker,self.do_something)defdo_something(self,*args,**kwargs):print('doing something...')if__name__=='__main__':# -- Use qute to get or create the QApplication instanceq_app=qute.utilities.qApp()widget=MyWidget()widget.show()q_app.exec_()In this example we see some of the features of qute in use, but most importantly is that it is usable in environments using either PyQt, PySide or PySide2 (thanks to Qt.py), and then utilises the various helper functionality defined within qute which you can read about below.Cross Application SupportThis library is specifically intended for use when in environments where
you're actively trying to share/develop tools across multiple applications
which support PyQt, PySide or PySide2.The premise is that you can request the main application window using
a common function regardless of the actual application - making it trivial
to implement a tool which works in multiple host applications without any
bespoke code.The current list of supported applications are:* Native Python
* Maya
* 3dsmax
* Motion BuilderHere is an example:importquteclassMyCrossApplicationTool(qute.QWidget):def__init__(self,parent=None):super(MyCrossApplicationTool,self).__init__(parent=parent)self.setLayout(qute.QVBoxLayout())self.layout().addWidget(qute.QLabel('This tool will launch and parent under Max, Maya, Motion Builder or Pure Python'))# ------------------------------------------------------------------------------deflaunch(blocking=False,*args,**kwargs):# -- This will return the running QApplication instance, or create# -- one if one is not presentq_app=qute.qApp()# -- Create a window and set its parent 'blindly' to what qute# -- resolves as the main window.window=qute.QMainWindow(parent=qute.utilities.windows.mainWindow())# -- Assign our widget to the windowwindow.setCentralWidget(MyCrossApplicationTool(*args,**kwargs))window.show()ifblocking:q_app.exec_()launch()In the example above, we have a (somewhat simple!) tool, and we expose the
tool through a launch function which is creating a main window. The crucial
part is that the window is asking Qute to return the main application window
rather than you relying on an application specific Ui.In doing this, you can copy/paste the code from the example into Max, Maya or
Motion Builder and you will get the same widget, and that widget will be
correctly parented under that application, making your Ui incredibly portably
and re-usable without an application specific layer.StylingQute gives a convience function for applying stylesheets to Qt widgets. Crucually it also exposes a mechanism allowing you do define variables to be replaced within stylesheets. This helps when wanting to use the same values multiple times across a stylesheet.For example, if we have a stylesheet such as:QWidget{background-color:rgb(BG_COLOR);color:rgb(TEXT_COLOR);}QLabel{padding-top:7px;padding-bottom:7px;background-color:transparent;color:rgb(TEXT_COLOR);}This can be assigned to a widget using:importqutequte.utilities.styling.apply(css_str,apply_to=widget,BG_COLOR='50, 50, 50',TEXT_COLOR='255, 0, 0',)In this example we pass a CSS string and the widget we want to apply to. Any additional keywords will be used as search and replace elements. This is handy when wanting to change sections of your stylesheet easily. Your replacements can be numbers, strings or filepaths (just ensure your slashing is / and not \). Thespaceexample stylesheet demonstrates this by using png files for widget backgrounds.Equally, you can pass the full path to a css/qss file too:qute.utilities.styling.apply('/usr/styles/my_style.qss',widget,)Alternatively you can have a library of style sheets and set the environment variableQUTE_STYLE_PATHto that location. By doing this you can pass the name of the style rather than the absolute path. Qute comes with one example stylesheet calledspacewhich can be used to demonstrate this as below:qute.utilities.styling.apply('space',widget,)This is an example of the space stylesheet:Menu GenerationGenerating menu's can be tedious and involve a lot of repetative code. In many cases a menu is made up of either actions, sseperators or sub-menus.Each of these are supported by the menu generation functionqute.utilities.menus.menuFromDictionary. The format of the dictionary you provide must conform to:{'Label': function}or{'Label': dict}or{'Label': None}If a function is given then the function is set as the callable when the item is clicked. If a dictionary is given as the value a submenu is generated (this is recusive, so you can nest menus). If the value is None then a Seperator will be added regardless of the key.Here is an example:importqutedeffoo():print('calling foo')defbar():print('calling bar')menu_definition={'Foo':foo,'-':None,'More':dict(bar=bar)}menu=qute.utilities.menus.menuFromDictionary(menu_definition)In this example we define some functions and add them as keys, we can then generate a QMenu from that dictionary. This is especially useful when you're dynamically generating menu from variable data.You can also define icons for your menu. To utilise this mechanism your icons must have the same name as the label and end in .png. You can then define the path to the icons during the menu call as shown here:menu=qute.utilities.menus.menuFromDictionary(structure=menu_definition,icon_paths=[os.path.dirname(__file__),])DeriveDerive is all about dynamically generating ui elements based on data types and being able to extract values from widgets without having to know what they are. This is particularly useful when generating ui elements on the fly without knowing what they are up front.A good example of this is the exposure of options or attributes on a class without knowing exactly what those options are. We can see an example of that here:importquteclassNode:"""Define a base class for something"""def__init__(self):self.options=dict()classCircle(Node):def__init__(self):self.options['radius']=5self.options['closed']=TrueclassQuadtrilateral(Node):def__init__(self):self.options['force_rectangle']defexample_callback(*args,**kwargs):print('In Callback')nodes=[Circle(),Quadtrilateral(),Quadtrilateral(),Circle(),]fornodeinnodes:foroption,valueinnode.options:# -- Blindly create a widget to represent the widgetwidget=qute.utilities.derive.deriveWidget(value=value,label=option,)# -- Connect the change event of the widget# -- to our callback - without knowing what# -- the widget is or what to connectqute.utilities.derive.connectBlind(widget,example_callback)We can also ask for the value from a widget without knowing what the widget is. This can be done using:importqutevalue=qute.utilities.derive.deriveValue(widget)This mechanism makes it easier to create dynamic ui's, especially when you're trying to expose data which can be manipulated on code objects.CompatabilityThis has been tested under Python 2.7.13 and Python 3.6.6 under both Windows and Ubuntu.ContributeIf you would like to contribute thoughts, ideas, fixes or features please get in touch!mike@twisted.space