Tkinter布局助手代码模版语法
   7

简介

为满足不同用户的需求,tkinter布局助手推出了自定义模版,用户可根据自己的需求生成代码。

tk文件

在开始学习模板语法前,需要先了解一下tk文件是什么,tk文件其实是一段json代码经过base64编码后的一段文本。详见源码

以下是一个tk文件中的json信息,在顶层是窗口的坐标以及配置信息,“elements"键下是窗口内的组件相应信息,如果是容器类组件也包含"elements"键

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
{
    "top": 20,
    "left": 152,
    "width": 462,
    "height": 332,
    "id": "lj0ubsuf",
    "type": "tk_win",
    "text": "Tkinter布局助手",
    "is_ttkbootstrap": false,
    "ttkbootstrap_theme": "cosmo",
    "frame": true,
    "elements": [{
        "top": 70,
        "left": 90,
        "width": 50,
        "event_bind_list": [],
        "boot_color": "default",
        "boot_type": "",
        "is_show": true,
        "height": 30,
        "text": "标签",
        "id": "lj9in5xy",
        "type": "tk_label"
    }, {
        "top": 100,
        "left": 200,
        "width": 50,
        "event_bind_list": [],
        "boot_color": "default",
        "boot_type": "",
        "is_show": true,
        "height": 30,
        "text": "按钮",
        "id": "ljnwqonp",
        "type": "tk_button"
    }],
    "event_bind_list": [],
    "menus": []
}

模版语法

在模版中读取窗口的json信息

要使用模板的语法,必须用“{{}}”将语法包裹起来

{{it.text}}  // 输出: Tkinter布局助手
{{it.is_ttkbootstrap}}  // 输出: false

条件语句 if elif else

条件分支语句

{{ @if(it.is_ttkbootstrap == true) }}
from ttkbootstrap import *
{{ #else }}
from tkinter.ttk import *
{{ /if }}

遍历所有组件

将json中的树状数据转为列表数据,该语句会返回所有的组件信息和它的父级组件的信息。

{{ @widgets => parent,widget }}
{{ @if(parent.type == 'tk_win') }}
如果父组件类型是“tk_win”输出这段内容
{{ #elif(parent.type == 'tk_tabs') }}
如果父组件类型是“tk_tabs”输出这段内容
{{ #else }}
其他情况输出这段内容
{{ /if }}

{{ /widgets }}

遍历列表 each

以下示例遍历窗口的菜单生成相应代码。

    def create_menu(self):
        menu = Menu(self,tearoff=False)
{{ @each(it.menus) => menu }}
        menu.add_command(label="{{menu.name}}",command=self.{{menu.call}})
{{ /each }}

遍历事件 event

遍历所有组件绑定的所有事件。示例中:为所有事件创建事件回调方法。

{{ @events=> ele }}
    def {{ele.func}}(self,evt):
        print("{{ele.evt | safe}}事件未处理:",evt)
{{ /events }}

bootstyle

返回 ttkbootstrap 的颜色和样式配置

btn = Button(parent, text="按钮", takefocus=False,{{ @bootstyle(widget) /}})

注释

{{! /* 这里是注释信息 不好被渲染 */}}

其他

{{ @if(it.menus.lenght == 0) }}
在表达式中 可以使用js语法
{{ /if}}

在语句中使用三元表达式

{{ it.frame == true ? '是容器' : '不是容器' }}

完整的模版示例

以下是tkinter布局助手默认的模板

"""
本代码由[Tkinter布局助手]生成
官网:https://www.pytk.net/tkinter-helper
QQ交流群:788392508
"""
from tkinter import *
from tkinter.ttk import *
from typing import Dict
{{ @if(it.is_ttkbootstrap) }}
from ttkbootstrap import *
from pytkUI.widgets import *
{{ /if }}

class WinGUI(Tk):
    widget_dic: Dict[str, Widget] = {}
    def __init__(self):
        super().__init__()
        self.__win()
{{ ! /* 处理组件创建和赋值 */ }}
{{ @widgets => parent,widget  }}
{{ @if(parent.type == 'tk_win') }}
        self.widget_dic["{{ widget.type }}_{{ widget.id }}"] = self.__{{ widget.type }}_{{ widget.id }}(self)
{{ #elif(parent.type == 'tk_tabs') }}
        self.widget_dic["{{ widget.type }}_{{ widget.id }}"] = self.__{{ widget.type }}_{{ widget.id }}( self.widget_dic["{{ parent.type }}_{{ parent.id }}_{{ widget.tab }}"])
{{ #else }}
        self.widget_dic["{{ widget.type }}_{{ widget.id }}"] = self.__{{ widget.type }}_{{ widget.id }}( self.widget_dic["{{ parent.type }}_{{ parent.id }}"]) 
{{ /if }}
{{ /widgets }}

    def __win(self):
        self.title("Tkinter布局助手")
        # 设置窗口大小、居中
        width = {{ it.width }}
        height = {{ it.height }}
        screenwidth = self.winfo_screenwidth()
        screenheight = self.winfo_screenheight()
        geometry = '%dx%d+%d+%d' % (width, height, (screenwidth - width) / 2, (screenheight - height) / 2)
        self.geometry(geometry)
        self.resizable(width=False, height=False)

        # 自动隐藏滚动条
    def scrollbar_autohide(self,bar,widget):
        self.__scrollbar_hide(bar,widget)
        widget.bind("<Enter>", lambda e: self.__scrollbar_show(bar,widget))
        bar.bind("<Enter>", lambda e: self.__scrollbar_show(bar,widget))
        widget.bind("<Leave>", lambda e: self.__scrollbar_hide(bar,widget))
        bar.bind("<Leave>", lambda e: self.__scrollbar_hide(bar,widget))
    
    def __scrollbar_show(self,bar,widget):
        bar.lift(widget)

    def __scrollbar_hide(self,bar,widget):
        bar.lower(widget)
    
    def vbar(self,ele, x, y, w, h, parent):
        sw = 15 # Scrollbar 宽度
        x = x + w - sw
        vbar = Scrollbar(parent)
        ele.configure(yscrollcommand=vbar.set)
        vbar.config(command=ele.yview)
        vbar.place(x=x, y=y, width=sw, height=h)
        self.scrollbar_autohide(vbar,ele)

{{ ! /* 组件创建函数 */ }}
{{ @widgets => parent,widget  }}
{{ ! /* 按钮 */ }}
{{ @if(widget.type == 'tk_button') }}
    def __tk_button_{{ widget.id }}(self,parent):
        btn = Button(parent, text="{{ widget.text }}", takefocus=False,{{ @bootstyle(widget) /}})
        btn.place(x={{ widget.left }}, y={{ widget.top }}, width={{ widget.width }}, height={{ widget.height }})
        return btn
{{ /if }}

{{ ! /* 标签 */ }}
{{ @if(widget.type == 'tk_label') }}
    def __tk_label_{{ widget.id }}(self,parent):
        label = Label(parent,text="{{ widget.text }}",anchor="center", {{ @bootstyle(widget) /}})
        label.place(x={{ widget.left }}, y={{ widget.top }}, width={{ widget.width }}, height={{ widget.height }})
        return label
{{ /if }}

{{ ! /* 输入框 */ }}
{{ @if(widget.type == 'tk_input') }}
    def __tk_input_{{ widget.id }}(self,parent):
        ipt = Entry(parent, {{ @bootstyle(widget) /}})
        ipt.place(x={{ widget.left }}, y={{ widget.top }}, width={{ widget.width }}, height={{ widget.height }})
        return ipt
{{ /if }}

{{ ! /* 文本框 */ }}
{{ @if(widget.type == 'tk_text') }}
    def __tk_text_{{ widget.id }}(self,parent):
        text = Text(parent)
        text.place(x={{ widget.left }}, y={{ widget.top }}, width={{ widget.width }}, height={{ widget.height }})
        self.vbar(text, {{ widget.left }}, {{ widget.top }}, {{ widget.width }}, {{ widget.height }},parent)
        return text
{{ /if }}

{{ ! /* 单选框 */ }}
{{ @if(widget.type == 'tk_radio_button') }}
    def __tk_radio_button_{{ widget.id }}(self,parent):
        rb = Radiobutton(parent,text="{{ widget.text }}",{{ @bootstyle(widget) /}})
        rb.place(x={{ widget.left }}, y={{ widget.top }}, width={{ widget.width }}, height={{ widget.height }})
        return rb
{{ /if }}

{{ ! /* 多选框 */ }}
{{ @if(widget.type == 'tk_check_button') }}
    def __tk_check_button_{{ widget.id }}(self,parent):
        cb = Checkbutton(parent,text="{{ widget.text }}",{{ @bootstyle(widget) /}})
        cb.place(x={{ widget.left }}, y={{ widget.top }}, width={{ widget.width }}, height={{ widget.height }})
        return cb
{{ /if }}

{{ ! /* 列表框 */ }}
{{ @if(widget.type == 'tk_list_box') }}
    def __tk_list_box_{{ widget.id }}(self,parent):
        lb = Listbox(parent)
        {{ @each(widget.options) => option }}
        lb.insert(END, "{{ option }}")
        {{ /each }}
        lb.place(x={{ widget.left }}, y={{ widget.top }}, width={{ widget.width }}, height={{ widget.height }})
        return lb
{{ /if }}

{{ ! /* 下拉选择框 */ }}
{{ @if(widget.type == 'tk_select_box') }}
    def __tk_select_box_{{ widget.id }}(self,parent):
        cb = Combobox(parent, state="readonly", {{ @bootstyle(widget) /}})
        cb['values'] = ("{{ widget.options.join('","') | safe }}")
        cb.place(x={{ widget.left }}, y={{ widget.top }}, width={{ widget.width }}, height={{ widget.height }})
        return cb
{{ /if }}

{{ ! /* 仪表盘 进度盘 */ }}
{{ @if(widget.type == 'tb_meter') }}
    def __tb_meter_{{ widget.id }}(self,parent):
        meter = Meter(parent,amountused=100, amounttotal=100, subtext="{{ widget.sub_text }}", metersize={{ widget.width }},
        textleft="{{ widget.text_left }}", textright="{{ widget.text_right }}", interactive={{ widget.interactive ? 'True': 'False' }},
        {{ @bootstyle(widget) /}})
        meter.place(x={{ widget.left }}, y={{ widget.top }}, width={{ widget.width }}, height={{ widget.height }})
        return meter
{{ /if }}

{{ ! /* 图标组件 */ }}
{{ @if(widget.type == 'ext_icon') }}
    def __ext_icon_{{ widget.id }}(self,parent):
        icon = Icon(parent,icon_name="{{ widget.icon }}", size={{ Math.round(widget.width * 0.9) }}, color="{{ widget.color }}")
        icon.place(x={{ widget.left }}, y={{ widget.top }}, width={{ widget.width }}, height={{ widget.height }})
        return icon
{{ /if }}

{{ ! /* 进度条 */ }}
{{ @if(widget.type == 'tk_progressbar') }}
    def __tk_progressbar_{{ widget.id }}(self,parent):
        progressbar = Progressbar(parent, orient={{ widget.width < widget.height ? 'VERTICAL' : 'HORIZONTAL' }},{{ @bootstyle(widget) /}})
        progressbar.place(x={{ widget.left }}, y={{ widget.top }}, width={{ widget.width }}, height={{ widget.height }})
        return progressbar
{{ /if }}

{{ ! /* 滑块 */ }}
{{ @if(widget.type == 'tk_scale') }}
    def __tk_scale_{{ widget.id }}(self,parent):
        scale = Scale(parent, orient={{ widget.width < widget.height ? 'VERTICAL' : 'HORIZONTAL' }}, {{ @bootstyle(widget) /}})
        scale.place(x={{ widget.left }}, y={{ widget.top }}, width={{ widget.width }}, height={{ widget.height }})
        return scale
{{ /if }}

{{ ! /* 表格 */ }}
{{ @if(widget.type == 'tk_table') }}
    def __tk_table_{{ widget.id }}(self,parent):
        # 表头字段 表头宽度
        columns = {{ @tblcolumns(widget.columns,widget.width-1) /}}
        tk_table = Treeview(parent, show="headings", columns=list(columns),{{ @bootstyle(widget) /}})
        for text, width in columns.items():  # 批量设置列属性
            tk_table.heading(text, text=text, anchor='center')
            tk_table.column(text, anchor='center', width=width, stretch=False)  # stretch 不自动拉伸
        
        tk_table.place(x={{ widget.left }}, y={{ widget.top }}, width={{ widget.width }}, height={{ widget.height }})
        self.vbar(tk_table, {{ widget.left }}, {{ widget.top }}, {{ widget.width }}, {{ widget.height }},parent)
        return tk_table
{{ /if }}

{{ ! /* 框架 */ }}
{{ @if(widget.type == 'tk_frame') }}
    def __tk_frame_{{ widget.id }}(self,parent):
        frame = Frame(parent,{{ @bootstyle(widget) /}})
        frame.place(x={{ widget.left }}, y={{ widget.top }}, width={{ widget.width }}, height={{ widget.height }})
        return frame
{{ /if }}

{{ ! /* 标签框架 */ }}
{{ @if(widget.type == 'tk_label_frame') }}
    def __tk_label_frame_{{ widget.id }}(self,parent):
        frame = LabelFrame(parent,text="{{ widget.text }}",{{ @bootstyle(widget) /}})
        frame.place(x={{ widget.left }}, y={{ widget.top }}, width={{ widget.width }}, height={{ widget.height }})
        return frame
{{ /if }}


{{ ! /* tabs框架 */ }}
{{ @if(widget.type == 'tk_tabs') }}
    def __tk_tabs_{{ widget.id }}(self,parent):
        frame = Notebook(parent)
{{ @each(widget.tabs) => tab,i }}
        self.widget_dic["{{ widget.type }}_{{ widget.id }}_{{ i }}"] = self.__tk_frame_{{ widget.id }}_{{ i }}(frame)
        frame.add(self.widget_dic["{{ widget.type }}_{{ widget.id }}_{{ i }}"], text="{{ tab }}")
{{ /each }}
        frame.place(x={{ widget.left }}, y={{ widget.top }}, width={{ widget.width }}, height={{ widget.height }})
        return frame

{{ @each(widget.tabs) => tab,i }}
    def __tk_frame_{{ widget.id }}_{{ i }}(self,parent):
        frame = Frame(parent)
        frame.place(x={{ widget.left }}, y={{ widget.top }}, width={{ widget.width }}, height={{ widget.height }})
        return frame
{{ /each }}

{{ /if }}
{{ /widgets }}

class Win(WinGUI):
    def __init__(self):
        super().__init__()
        self.__event_bind()
{{ ! /* 配置菜单 */ }}
{{ @if(it.menus.length > 0) }}
        self.config(menu=self.create_menu())
{{ /if }}

{{ ! /* 创建菜单相关方法 */ }}
{{ @if(it.menus.length > 0) }}
    def create_menu(self):
        menu = Menu(self,tearoff=False)
{{ @each(it.menus) => menu }}
{{ @if(menu?.children) }}
        menu.add_cascade(label="{{ menu.name }}",menu=self.menu_{{ menu.id }}(menu))
{{ #else }}
        menu.add_command(label="{{ menu.name }}",command=self.{{ menu.call }})
{{ /if }}
{{ /each }}
        return menu
{{ /if }}

{{ @each(it.menus) => menu }}
{{ @if(menu?.children) }}
{{ @each(menu.children) => submenu }}
    def menu_{{ menu.id }}(self,parent):
        menu = Menu(parent,tearoff=False)
        menu.add_command(label="{{ submenu.name }}",command=self.{{ submenu.call }})
        return menu
{{ /each }}
{{ /if }}
{{ /each }}


{{ ! /* 创建菜单回调方法 */ }}
{{ @if(it.menus.length > 0) }}

{{ @each(it.menus) => menu }}

{{ @if(menu?.children) }}
{{ @each(menu.children) => submenu }}
    def {{ submenu.call }}(self):
        print("点击了菜单")
{{ /each }}
{{ #else }}
    def {{ menu.call }}(self):
        print("点击了菜单")
{{ /if }}

{{ /each }}
{{ /if }}


{{ ! /* 创建事件处理并绑定方法 */ }}
{{ @events=> ele }}
    def {{ ele.func }}(self,evt):
        print("{{ ele.evt | safe }}事件未处理:",evt)
{{ /events }}

    def __event_bind(self):
{{ @events=> ele }}
{{ @if(ele.type == 'tk_win') }}
        self.bind('{{ ele.evt | safe }}',self.{{ ele.func }})
{{ #else }}
        self.widget_dic["{{ ele.type }}_{{ ele.id }}"].bind('{{ ele.evt | safe }}',self.{{ ele.func }})
{{ /if }}
{{ /events }}
        pass

if __name__ == "__main__":
    win = Win()
    win.mainloop()

参考文档

https://squirrelly.js.org/docs/

站长微信
请备注来意
二维码