其实,我本人是抗拒使用 Python 去实现一个 UI 框架的,因为做 App 应用,React Native,Flutter 基本上在江湖上已经是公认的比较合适的选择,而且对于技术栈是 Python 的朋友,有一些流行的UI框架,可以用于构建跨平台的桌面应用程序。其中一些框架包括Tkinter、PyQt、wxPython和Kivy等。这些框架提供了创建窗口、按钮、文本框等UI元素的功能,并且可以在不同的操作系统上运行。但是,我想要说的,别人有是有,自己动手整一个,是可以加深对这方面原理的了解的,这很重要,你会用是一回事,能不能用的好那就是另外一回事了,想必作为 Pythoner,你是希望作为后者的,那我建议你还是来看看。因此,学习本文,你可以了解如何自己动手实现一个 Python 上的 UI 框架。
我思来想去,打算使用 SwiftUI的方式来实现一个最最基本的 Python 上的响应式 UI 吧,ok,我们的目标大概是会写起来想这样。
SwiftUI是一个声明式的UI框架,它允许开发者以一种非常简洁和直观的方式来描述用户界面,而不是通过命令式的代码来操作UI元素。
在Python中,要创建一个类似的框架,咱们需要考虑以下几个关键点:
怎么说呢,实现这些东西,的确是比较难的,尤其是让我们实现渲染引擎,这么一篇短短的文章,不是太现实,但是,我们总算是有点思路的,千里之行,始于足下,我们先往前走两步。站在巨人的肩膀上,如,站在Tkinter的肩膀上,来实现一个极其简单的,就登录页面,来验证下吧。为什么选择Tkinter呢,问这这个正好操作系统上自带就有,免得去安装,占用磁盘空间,另外一个,这个确实也比较熟悉了。那么我们将Tkinter 略微封装封装,整成一个声明式的 UI的化,也不准备给他实现的太全了,就封装一下 Button,Label,Input 吧,因为基于这个我们就可以实现一个简单的登录页了。ok,代码如下:
import tkinter as tk
# 声明式组件类
class Label:
def __init__(self, text):
self.text = text
self.widget = None
def render(self, parent):
if self.widget is None:
self.widget = tk.Label(
parent, justify="left", text=self.text, font=('Arial', 12))
return self.widget
class Input:
def __init__(self, placeholder, isPassWord=False):
self.placeholder = placeholder
self.widget = None
self.isPassWord = isPassWord
def render(self, parent):
if self.widget is None:
self.widget = tk.Entry(parent, font=('Arial', 12))
self.widget.bind("<FocusIn>", self.on_focus_in)
self.widget.bind("<FocusOut>", self.on_focus_out)
if self.isPassWord:
self.widget["show"] = "*" # 隐藏密码
return self.widget
def on_focus_in(self, event):
if self.widget.get() == self.placeholder:
self.widget.delete(0, tk.END)
def on_focus_out(self, event):
if not self.widget.get():
self.widget.insert(0, self.placeholder)
class Button:
def __init__(self, text, on_click):
self.text = text
self.on_click = on_click
self.widget = None
def render(self, parent):
if self.widget is None:
self.widget = tk.Button(parent, text=self.text, command=self.on_click, font=(
'Arial', 12))
return self.widget
# 应用类
class App:
def __init__(self, title, components):
self.title = title
self.components = components
self.root = tk.Tk()
self.root.title(title)
def run(self):
for component in self.components:
widget = component.render(self.root)
widget.pack(pady=5)
self.root.mainloop()
# 创建登录页面的组件
username_label = Label(text="Username:")
username_entry = Input(placeholder="Enter your username")
password_label = Label(text="Password:")
password_entry = Input(placeholder="", isPassWord=True)
login_button = Button("Login", lambda: print(
f"Logging in with {username_entry.widget.get()}"))
# 创建并运行应用
app = App("Login Page", [username_label, username_entry,
password_label, password_entry, login_button])
app.run()
运行一下:
不出意外的画,结果肯定是测试成功了。ok,起码一个声明式的架子是看到了,但是似乎有点寒碜啊,我们家一个布局组件进来来管理一下组件的摆放,原汁原味的组件虽然又不是不能用,但是没布局还是不可以的。我们增加一个布局组件,就比如 FlexLayout,毕竟我们熟悉,ok ,我们增加一个布局类,FlexLayout,如下,当然比较简单,如果是下全部的 FlexLayout,我可能会疯,毕竟虽然 FlexLayout 好用,但是并不那么好写一个完整的,因此只写一个极其简单的。
class FlexLayout:
def __init__(self, direction='row', justify='start', align='start'):
self.direction = direction
self.justify = justify
self.align = align
self.children = []
self.widget = None
def add(self, component):
self.children.append(component)
def render(self, parent):
if self.widget is None:
self.widget = tk.Frame(parent)
self.widget.pack(fill='both', expand=True)
for child in self.children:
widget = child.render(self.widget)
if self.direction == 'row':
widget.pack(side='left', fill='both', expand=True)
else:
widget.pack(side='top', fill='both', expand=True)
return self.widget
随后,我们将之前的代码改造一下,主要是把 Input,Label,Button 放入到布局中:
# 创建布局
username_frame = FlexLayout(direction='row', justify='center', align='center')
username_frame.add(username_label)
username_frame.add(username_entry)
password_frame = FlexLayout(direction='row', justify='center', align='center')
password_frame.add(password_label)
password_frame.add(password_entry)
button_frame = FlexLayout(direction='row', justify='center', align='center')
button_frame.add(login_button)
# 创建并运行应用
app = App("Login Page", [username_frame, password_frame, button_frame])
app.run()
走你,看看效果:
顺利将 姓名的 Label 和 Input 摆放到了一行啦。
我们这个声明式的 Python UI 框架最终实现的效果基本上算是有了一点点改进,但是恐怕离好用还有着巨大的差距,个人认为,写 UI 的最佳方式应该是类 HTML 那种方式,无论是 React 也好,还是 Vue 也好,更不要说html 原生方式,UI 的部分基本上都是这种方式在做,anyway,有那么一点点改进也是推进了一点。
我更加推荐你去看一看其他的一些Python 的 UI 框架,如:kivy ,它的这种写法已经基本趋向于 web 的方式了,但是还有极大的差距,没办法,这就是语言之间的差距,嗯,这是一道难以逾越的鸿沟,但也不是说不可能,肯定是有大神会想办法变为可能,只不过,这个代驾是否值得而已。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。