A quick Qt GUI tool development template for cross-platform standalone desktop tools, and sub-tools for python supported host applications, including
It automatically supports any combination of (Python 2.x, Python 3.x in 32bit and 64bit) with (PySide, PyQt4, PySide2, PyQt5)
Detail Documentation at wiki: https://github.com/shiningdesign/universal_tool_template.py/wiki
The light-weight version of universal_tool_template.py, designed for widget level implementation but still with utt qui() features.
Feature | Description |
---|---|
Host Detection | Maya, Houdini, Nuke, Fusion, Blender, Notepad++, Desktop |
Python Detection | 2.x, 3.x, 32bit, 64bit |
Qt Binding Detection | PySide, PyQt4, PySide2, PyQt5 |
Universal Coding | seamlessly works in above 2x2x4=16 combinations |
File IO Support | json, cPicle binary, plain text |
oneline multi-UI creation | quickUI() v4.0; qui() v1.0 |
one-stop UI management | self.uiList, self.iconList, self.hotkey |
auto UI-Action bindinig | button, menuItem, message button |
auto Icon loading | maya shelf icon, class name icon |
Template Style Option | standalone, frameless, trans-irregular, on-top |
Global Style Option | modern style |
Interface Interaction | Drag, Move, right-click-menu |
Element Style Option | invisible-functional button |
Extension Features | self location for script mode and app mode |
Language Features | auto Export and Load UI language json |
v1010 (public release):
v2010 (public release):
(for Version 9 and 10 and newer):
(for Version 8 and older)
* Prepare into Your Tool:
1. global replace class name "UniversalToolUI" to "YourToolName" in your editor,
* in icons folder, the Tool GUI icon should name as "YourToolName.png"
2. change file name "universal_tool_template.py" to "YourPythonFileName.py",
* in icons folder, the Maya shelf icon should name as "YourPythonFileName.png", if you name all name the same, then 1 icon is enough
3. load it up and run
loading template - Run in Application's python panel:
import sys;myPath='/path_to_universal_tool_or_custom_name/';myPath in sys.path or sys.path.append(myPath);
import universal_tool_template
universal_tool_template.main() # no need this line for blender
loading template - Run in system command console
python universal_tool_template.py
UPDATE: 2020/06.23 install PySide2 for Blender 2.83
cd D:\YourPathToThat\blender-2.83.0-windows64\2.83\python\bin
D:
.\python.exe -m pip install PySide2
quickUI(["elementA_btn;QPushButton;Title Here"], "config_layout;QVBoxLayout")
element_list:
ui_name@ui_label;ui_type;ui_opts ##################################### elementA_btn;QPushButton;Title Here elementA_btn@User label;QPushButton;Title Here elementB_choice@Choose one;QComboBox;(A,B,C,4) partB_space;QSpacerItem;(200,10,5,4) elementC_txtEdit@Custom label;LNTextEdit partB_layout;vbox partB_QVBoxLayout
main_split;QSplitter;v main_tab;QTabWidget elementB_grp@System label;QGroupBox;vbox,My Group Title
parentObject parent_name;parent_type;parent_opts ##################################### config_layout;QVBoxLayout config_vbox config_split;QSplitter;v config_split;v config_grp;QGroupBox;vbox,My Group Title main_tab main_tab;QTabWidget
parentObject's insert_opt ##################################### gridLayout: "h", "v" for grid insertion tab: (Tab A, Tab B, Tab C) for tab name insertion
#####################################
qui('elementA_btn;Title Here | elementB_label;Label here', 'config_vbox')
element_list:
ui_name@ui_label;ui_opts ##################################### can't use ; in ui_opts
parentObject parent_name;parent_type;parent_opts ##################################### parentObject's insert_opt #####################################
Universal Tool Template UI creation syntax
-------------
now all the creation is using qui function
| UI element | syntax |
| --------- | ------------ |
| **QVBoxLayout** | `self.qui('my_vbox')` <br> `self.qui('my_layout;vbox')` |
| **QHBoxLayout** | `self.qui('my_hbox')` <br> `self.qui('my_layout;hbox')` |
| **QGridLayout** | `self.qui('my_grid')` <br> `self.qui('my_layout;grid')` |
| | `self.qui('box_btn;Box \| sphere_btn;Sphere \| ring_btn;Ring', 'my_layout;grid', 'h')` |
| | create and insert those 3 buttons as horizontal row in the grid |
| | `self.qui('box_btn;Box \| sphere_btn;Sphere \| ring_btn;Ring', 'my_layout;grid', 'h')` <br> `self.qui('box2_btn;Box2 \| sphere2_btn;Sphere2 \| ring2_btn;Ring2', 'my_layout', 'h')` |
| | create 2 rows of button, first row (Box,Sphere,Rig), second row (Box2,Sphere2,Ring2) |
| | `self.qui('cat_btn;Cat \| dog_btn;Dog \| pig_btn;Pig', 'pet_layout;grid', 'v') ` <br> `self.qui('cat2_btn;Cat2 \| dog2_btn;Dog2 \| pig2_btn;Pig2', 'pet_layout', 'v') ` |
| | create 2 column of button, first column (cat,dog,pig), second column (cat2,dog2,pig2) |
| **QFormLayout** | self.qui('my_form') <br> self.qui('my_layout;form') |
| | `self.qui('name_input@Name:;John \| email_input@Email:;test@test.com', 'entry_form')` |
| | create a name input with label "Name:" and default text "John", and then create email input with label "Email" and default text "test@test.com" inside entry_form form layout |
| | |
| **QSplitter** | `self.qui('user_layout \| info_layout', 'my_split;h')` <br> `self.qui('user_grp \| info_tab', 'my_split;h')` |
| | put user UI and info UI side by side in split, <br> child can be either a layout or widget |
| **QGroupBox** | `self.qui('user2_btn;User2 \| info2_btn;Info2', 'my_grp;vbox,Personal Data')` |
| | put user UI and info UI inside "my_grp" group box and with internal vbox layout and title as "Personal Data" <br> Note:**no ,() in title** |
| **QWidget** | `self.qui('user_label;Name \| user_input', 'user_widget;vbox')` |
| | like Groupbox widget, but without a title, useful for hide show a set of UIs |
| **QTabWidget** | `self.qui('client_layout \| product_layout', 'database_tab;h', '(Client,Product)')` |
| | put client UI and product UI into database_tab tabwidget, as horizontal tab, with title as "Client", "Product", UI can be either widget or layout |
| | |
| **QPushButton** | `self.qui('my_btn;Submit')` |
| | create pushbutton with title "Submit" |
| | `self.qui('my_btnMsg;Info')` |
| | create pushbutton with title "Info" with automatically pop up a dialog show text string stored in self.uiList['my_msg'] |
| **QLabel** | `self.qui('info_label;Please select all objects')` |
| | a label with text "Please select all objects" |
| **QLineEdit** | `self.qui('user_input;Your Email')` |
| | a line input with default text "Your Email" |
| **QCheckBox** | `self.qui('testOnly_check;Run as Test Only')` |
| | create a check box with title "Run as Test Only" |
| **QComboBox** | `self.qui('objectType_choice;(Box,Sphere,Ring)')` |
| | create a drop down list with option "Box", "Sphere", "Ring"|
| ||
| **QTextEdit** | `self.qui('comment_txt;Please write details here')` |
| | create a text area with default text "Please write details here" |
| ||
| **QListWidget** | `self.qui('name_list')` |
| | note, list has no header and single column |
| **QTreeWidget** | `self.qui('file_tree;(Name,Path)')` |
| | create a tree widget with column names as "Name", "Path"|
| **QTableWidget** | `self.qui('data_table;(Name,Email,Phone)')` |
| | create a data table with column name as "Name", "Email", "Phone" |
| ||
| **QSpacerItem** | `self.qui('user_space;(100,30,4,3)')` |
| | create a space item with policy expanding horizontally and normal vertically <br> # 0 = fixed; 1 > min; 2 \< max; 3 = prefered; 4 = \<expanding>; 5 = expanding> Aggresive; 6=4 ignored size input |
now all menu creation is using qui_atn,qui_menu,qui_menubar function (replaced quickMenuAction, quickMenu since v11)
| menu element | syntax |
| --------- | ------------ |
| **QMenu** | `self.qui_menu('right_menu_createFolder_atn;Create Folder,Ctrl+D \| _ \| right_menu_openFolder_atn;Open Folder', 'right_menu')` |
| | create a list of menu QAction with title and optional shortcut and form (into) a menu object, while _ means a seperator in menu |
| | `self.qui_menubar('file_menu;&File \| setting_menu;&Setting \| help_menu;&Help')` |
| | create a list of menu in menubar |
| **QAction** | `self.qui_atn(ui_name, title, tip=None, icon=None, parent=None, key=None)` <br> `self.qui_atn('importConfig_atn','Import Config (&I)','Import Config Setting.','importConfig.png', 'setting_menu', 'Ctrl+I')` |
| | create a detailed QAction with uiList name, title, tip, icon, optionally inside a menu; for simple menuAction creation, just use with qui_menu |
| **QShortcut** | `self.qui_key('editNoter', 'Alt+E', self.editNoter_action )` |
| | create hotkey without a menuAction, stored in self.hotkey |
File Structure
===================
* **universal_tool_template_VERSION.py**: main TEMPLATE and USERCLASS and MAIN()
* **GearBox**: a json config file editor for both language dict files and config export dict files from template.
* **LNTextEdit.py**: (optional) if you want to use LNTextEdit in your tool.
* **UITranslator.py**: (optional) a GUI tool to create translation json file from template's exported default language json
* **install-v5.0_App.mel**: (optional) a quick Maya shelf installer that auto put python tool in maya shelf based on naming format and ctrl+RMB menu for reload your tool, good for code test
* **universal_tool_template.bat**: window console mode or window mode auto launcher, it detect whether to use launch with pythonw or python by the file name
* if you name as YourPythonFileName.bat, it will launch a console to run your Py
* if you name as YourPythonFileName_w.bat, it will use Pythonw to directly run your Py without pop-up console floating there
* if you name as YourPythonFileName_z.bat, it will launch without console in Python3 (change the py3 path inside)
* if you name as YourPythonFileName_x.bat, it will launch with console in Python3 (change the py3 path inside)
Screenshot
===================
![universal_tool_template_2010_krita.png](screenshot/universal_tool_template_2010_krita.png?raw=true)
![universal_tool_template_v10.2.png](screenshot/universal_tool_template_v10.2.png?raw=true)
![universal_tool_template_v10.2_blender.png](screenshot/universal_tool_template_v10.2_blender.png?raw=true)
![universal_tool_template_v10.2_fusion.png](screenshot/universal_tool_template_v10.2_fusion.png?raw=true)
![universal_tool_template_v10.2_houdini.png](screenshot/universal_tool_template_v10.2_houdini.png?raw=true)
![universal_tool_template_v8.0.png](screenshot/universal_tool_template_v8.0.png?raw=true)
![universal_tool_template_v7.3.png](screenshot/universal_tool_template_v7.3.png?raw=true)
![universal_tool_template_v5.0.png](screenshot/universal_tool_template_v5.0.png?raw=true)
![universal_tool_template_v4.0.png](screenshot/universal_tool_template_v4.0.png?raw=true)
![universal_tool_template_v3.0.png](screenshot/universal_tool_template_v3.0.png?raw=true)
File Structure in Details
===================
GearBox
-------------
* a json dict file editor, for editing config dict file exported from template for external config file use.
* it also can serve a lite or better version of UITranslator.py
* you can also use it as a example of how universal_tool_template.py can be used to create tools
**screenshot**
![GearBox_v1.0.png](screenshot/GearBox_v1.0.png?raw=true)
![GearBox_v1.0_2.png](screenshot/GearBox_v1.0_2.png?raw=true)
**usage**
* run the GearBox_w.bat to start it if you have python on windows
* load the json file from browser inside it,
* click on the tree item to show in the bottom left preview text box, and use right text box to draft out your edit content and then copy paste into edit field on the tree, by double click the tree item.
* change the file name if you want to export as different file
* click export button below to save it
**feature list**
* v001.1 (2017.09.25)
* update GearBox to template 1010,
* fix export null error when there is single data entry in the config_tree and the data has a name
* v001. (2017.04.17)
* support dict type json file only
* file loading and exporting
* add, remove tree branch
* edit the field and has a large preview text box to auto loading from selection
* show pure text for json text data, and show json format text for other type data in tree view
LNTextEdit.py
-------------
* a line number text edit Ui element, a replacement of the original LNTextEdit and my variations like LNTextEditEx, LNTextEdit_Pside, LNTextEdit_PyQt
**usage**
```python
import LNTextEdit
display_textEdit = LNTextEdit.LNTextEdit()
display_textEdit.setWrap(0)
display_textEdit.setReadOnly(1)
display_textEdit.setReadOnlyStyle(1)
display_textEdit.setZoom(1) # enable text zoom feature
feature list
Feature
How to Use
import UITranslator
UITranslator.main()
python UITranslator.py
ToolName_lang_YourLanguageName.json
File Structure
screenshot
feature and usage:
python ToolName_buildScript.py build