Package: qmlease
中文版QmlEaseQmlEase is a flexible toolkit for Python programmer to efficiently develop QML
graphical user interface, it is available for PySide6/PyQt6/PySide2/PyQt5.NoteThis project is formerly known aslk-qtquick-scaffold, we rename it to be
"qmlease" and bring up the version from2.0to3.0. It means the the first
release of this project would be3.0.0.lk-qtquick-scaffold is going to be archived and no longer maintained, once
after qmlease enters stable phase.HighlightsSupport PySide6/PyQt6/PySide2/PyQt5.Simple to launch a QML application.Hot reload QML files in debug mode.Pythonicsignalslotstyle.Powerful integrating Python with QML by register system.Show QML print messages in Python console.Built-in widgets library.Improved layout engine.Auto complete stylesheet.InstallationInstall QmlEaseWarning: qmlease is not officially released yet. Below is a pre-documented
guide for the near future. (i.e. Below command is not available for now.)Use pip to install qmlease:pipinstallqmleaseThe latest version is3.0+.Install Qt backendInstalling qmlease doesn't include any of Python for Qt's libraries. You need
to manually install one of the follows:# choose one to installpipinstallpyside6
pipinstallpyqt6
pipinstallpyside2
pipinstallpyqt5QmlEase auto detects the Qt backend you've installed (you can also explicitly
set specific one), it usesqtpytoprovide an uniform layer overrides PySide6/PyQt6/PySide2/PyQt5.Examples quick throughHello worldview.qmlimportQtQuickimportQtQuick.WindowWindow{visible:truewidth:400height:300Text{anchors.centerIn:parenttext:'Hello world!'}}main.pyfromqmleaseimportappapp.run('view.qml')Hot loaderTheapp.runmethod acceptsdebug(bool type) parameter, to enable hot
loader mode:fromqmleaseimportappapp.run('view.qml',debug=True)It starts a floating window that includes a button "RELOAD", each time when you
modify "view.qml", click "RELOAD" to refresh your GUI:BTW you can run "view.qml" in command line:# see helppy-mqmlease-h# runpy-mqmleaserunview.qml# run in debug modepy-mqmleaserunview.qml--debugIt has the same result like above "main.py" does.Register Python funtions to QMLfromqmleaseimportQObject,app,pyside,slotclassAAA(QObject):@slot(result=str)defhello(self):return'hello world (aaa)'classBBB(QOjbect):@slot(result=str)defhello(self):return'hello world (bbb)'# 1. register instanceaaa=AAA()app.register(aaa,'aaa')# 2. register classapp.register(BBB,'MyBbbType',namespace='dev.likianta.qmlease')# 3. register regular function.deffoo(a:int,b:int,c:int):returna+b+cpyside.register(foo)pyside.register(foo,name='foo_alias')view.qmlimportQtQuick2.15importdev.likianta.qmlease1.0Item{MyBbbType{// from `dev.likianta.qmlease`id: bbb}Component.onCompleted:{console.log(py.aaa.hello())// -> 'hello world'console.log(bbb.hello())// -> 'hello world'console.log(py.call('foo',[1,2,3]))// -> 6console.log(py.call('foo_alias',[1,2,3])// -> 6}}Access QML object propertiesfromqmleaseimportQObject,slotclassMyObject(QObject):@slot(object)definit_view(self,button:QObject)->None:print(button['text'])# -> 'AAA'button['text']='BBB'# this will emit a textChanged signal.print(button['text'])# -> 'BBB'Connect QML signal to Python functionsfromlambda_eximportgraftingfromqmleaseimportQObject,slotclassMyObject(QObject):@slot(object)definit_view(self,button:QObject)->None:@grafting(button.clicked.connect)def_():print('clicked',button['text'])Integrate qt logging in python consoleWhen you useconsole.login QML side, it will be printed in Python console:Signal and SlotThesignalandslotwrap on Qt'sSignalandSlotdecorators, but
extended their functionalities:You can get the correct type hint in IDE:Theslotaccepts more types as alias to "QObject" and "QVariant" -- it is
more convenient and more readable:fromqmleaseimportQObject,slotclassMyObject(QObject):@slot(int,dict,result=list)# <- heredeffoo(self,index,data):return[index,len(data)]'''it is more readable than:@Slot(int, QJSValue, result='QVariant')def foo(self, index, data):return [index, len(data)]'''Here is a full alias list (which is documented inqmlease/qt_core/signal_slot.py):slot(*args)AliasReal valueNoteboolboolbasic typefloatfloatbasic typeintintbasic typestrstrbasic typeQObjectQObjectobjectobjectQObjectobject'item'QObjectobject (string)'object'QObjectobject (string)'qobject'QObjectobject (string)dictQJSValueqjsvaluelistQJSValueqjsvaluesetQJSValueqjsvaluetupleQJSValueqjsvalue...QJSValueqjsvalue'any'QJSValueqjsvalue (string)slot(result=...)AliasReal valueNoteNoneNonebasic typeboolboolbasic typefloatfloatbasic typeintintbasic typestrstrbasic typedict'QVariant'qvariantlist'QVariant'qvariantset'QVariant'qvarianttuple'QVariant'qvariant...'QVariant'qvariantslotdecorator is non-intrusive -- it means the method been decorated can
be called in Python side as usual.fromqmleaseimportQObject,slotclassMyObject(QObject):@slot(int,str,result=list)deffoo(self,index,name):return[index,name]my_obj=MyObject()# you can call it like a regular method! (just 'ignore' its docorator.)my_obj.foo(1,'hello')# -> [1, 'hello']Built-in widgets libraryqmleaseprovides a set of built-in widgets under its~/widgetsdirectory.Basically, you can use it in QML by importing "LKWidgets" (or "LKWidgets 1.0"
for Qt 5.x):importLKWidgetsLKWindow{color:'#DBDBF7'// moon whiteLKRectangle{anchors.fill:parentanchors.margins:32color:'#ECDEC8'// parchment yellowLKColumn{anchors.centerIn:parentalignment:'hcenter'// horizontally center childrenLKGhostButton{text:'SUNDAY'}LKButton{text:'MONDAY'}LKGhostButton{text:'TUESDAY'}LKButton{text:'WEDNESDAY'}LKGhostButton{text:'THURSDAY'}LKButton{text:'FRIDAY'}LKGhostButton{text:'SATURDAY'}}}}The dark theme:More screenshots: seeexamples/lk_widgets/screenshot_*.All widget names are started with 'LK', the full list is inqmlease/widgets/LKWidgets/qmldirfile.Note: the widgets documentation is not ready. Currently you may have a look at
theexamples/lk_widgetsscreenshots, or view its source code for more details.High-level model, human-readable APITODOLayout engineLayout engine is powered byqmlease.qmlside.layout_helper, which is
registered aspylayoutin QML side.// some_view.qmlimportQtQuickColumn{height:100Item{id: item1;height:20}Item{id: item2;height:0.4}Item{id: item3;height:0}Item{id: item4;height:0}Component.onCompleted:{// horizontally center childrenpylayout.auto_align(this,'hcenter')// auto size children://  width > 1: as pixels//  width > 0 and < 1: as percent of left spared space//  width = 0: as stretch to fill the left spared spacepylayout.auto_size_children(this,'vertical')//  the result is://      item1: 20px//      item2: (100 - 20) * 0.4 = 32px//      item3: (100 - 20 - 32) * 0.5 = 24px//      item4: (100 - 20 - 32) * 0.5 = 24px//          (item 3 and 4 share the left space equally.)}}Executing Python snippet in QML, and vice versatest.pyfromqmleaseimporteval_jsdeffoo(item1:QObject,item2:QObject):eval_js('''$a.widthChanged.connect(() => {$b.width = $a.width * 2})''',{'a':item1,'b':item2})view.qmlimportQtQuickListView{model:pyside.eval(`importosfiles=os.listdir(input('target folder: '))returnfiles`)}Style managerqmleaseexposes a list of built-in style controlers to QML side as follows:StyleDescriptionpycolorAll color specifications defined in a canonical name formpyfontFont related specificationspysizeWidth, height, radius, padding, margin, spacing, etc.pymotionAnimation related specifications (duration, easing type, etc.)Usage examples (seen in all LKWidgets):You can overwrite the style by giving a YAML file to load, for example a
"dark-theme.yaml":# this is dark theme color scheme# == general ==blue_1:'#e4e5f8'blue_3:'#5294eb'blue_5:'#3844e6'blue_7:'#0f143b'dark_1:'#424141'dark_2:'#242529'dark_3:'#15141a'dark_5:'#050408'grey_3:'#e8eaed'grey_5:'#a9acb0'# == widgets spec ==border_active:'#797171'border_default:'#575757'border_glow:'$border_active'button_bg_active:'$blue_5'button_bg_default:'$panel_bg'button_bg_hovered:'$dark_1'button_bg_pressed:'$dark_3'button_bg_selected:'$button_bg_pressed'input_bg_active:'$dark_2'input_bg_default:'$panel_bg'input_border_active:'$border_active'input_border_default:'$border_default'input_indicator_active:'$blue_5'panel_bg:'$dark_3'prog_bg:'$blue_1'prog_fg:'$blue_5'sidebar_bg:'$panel_bg'text_default:'$grey_3'text_disabled:'$grey_5'text_hint:'$grey_5'win_bg_default:'$dark_5'The dollar symbol ($) is a simple pointer to the other key.You don't need to write all colors in the file,qmleasehas a great deduction
algorithm to automatically call back "defaults" when required colors are
missing from your sheet.Finally load it by callingpycolor.update_from_file():fromqmleaseimportpycolorpycolor.update_from_file('dark-theme.yaml')Warning: currently color name style is under refactoring, it is very unstable
to learn from its style.Galleryhttps://user-images.githubusercontent.com/27986259/180829198-7110831e-c060-436a-a9be-c41452f49932.mp4https://user-images.githubusercontent.com/27986259/180829267-cd497bcc-de38-4d00-bb19-c4a84b251031.mp4TODO:AddMoreWidgetsDemo