Python GUI应用程序开发之wxPython使用详解
概要
wxPython是一个强大的跨平台GUI工具包,它使用Python编程语言开发,提供了丰富的控件功能。如果你是一名Python开发者,而且希望创建一个功能齐全的桌面应用程序,那么wxPython是一个值得考虑的选择。
什么是wxPython
wxPython是wxWidgets C++库的Python绑定版本,它支持各种操作系统,包括Windows、Linux和macOS。wxPython提供了各种标准控件,如按钮、文本框、下拉列表、菜单、对话框等,以及许多高级控件,如网格、树形结构、列表框等,使开发者可以创建复杂的GUI应用程序。
安装
安装wxPython非常简单。只需在终端或命令提示符中键入以下命令:
pip install wxPython
然后就可以开始使用wxPython来创建GUI应用程序了。
创建一个GUI应用程序
让我们来看一个简单的wxPython示例程序。下面的程序创建一个简单的窗口,其中包含一个按钮。当用户单击按钮时,程序将显示一个对话框。
import wxclass MyFrame(wx.Frame):def __init__(self, parent, title):wx.Frame.__init__(self, parent, title=title, size=(300, 200))self.panel = wx.Panel(self)self.button = wx.Button(self.panel, label="Click Me")self.Bind(wx.EVT_BUTTON, self.on_button_click, self.button)self.Show(True)def on_button_click(self, event):wx.MessageBox('Hello wxPython', 'Message', wx.OK | wx.ICON_INFORMATION)app = wx.App(False)
frame = MyFrame(None, 'Hello wxPython')
app.MainLoop()
在这个例子中,先定义了一个名为MyFrame的类,该类继承自wx.Frame类,并重写了它的构造函数。在构造函数中,我们创建了一个名为panel的wx.Panel对象,该对象是一个容器,用于包含其他控件。我们还创建了一个名为button的wx.Button对象,并将其添加到panel中。最后,使用Bind()方法将wx.EVT_BUTTON事件与on_button_click()方法关联起来。
on_button_click()方法是一个事件处理程序,它在用户单击按钮时被调用。在这个方法中,使用wx.MessageBox()方法创建了一个简单的消息框。
最后,创建一个wx.App对象,并将它的参数设置为False,这表示我们不希望wxPython创建一个控制台窗口。然后创建一个MyFrame对象,并将其显示出来。
控件
wxPython支持各种控件,包括文本框、按钮、下拉列表、菜单、对话框等。下面是一些常用的wxPython控件:
wx.TextCtrl
wx.TextCtrl控件用于显示和编辑文本。它可以用于单行文本框或多行文本框。
import wxclass MyFrame(wx.Frame):def __init__(self, parent, title):wx.Frame.__init__(self, parent, title=title, size=(300, 200))self.panel = wx.Panel(self)self.textctrl = wx.TextCtrl(self.panel, style=wx.TE_MULTILINE)app = wx.App(False)
frame = MyFrame(None, 'TextCtrl Example')
frame.Show(True)
app.MainLoop()
在这个例子中,我们创建了一个名为textctrl的wx.TextCtrl对象,并将其添加到panel中。还使用style参数指定了wx.TE_MULTILINE样式,这表示这个文本框是一个多行文本框。
wx.Button
wx.Button控件用于创建按钮。
import wxclass MyFrame(wx.Frame):def __init__(self, parent, title):wx.Frame.__init__(self, parent, title=title, size=(300, 200))self.panel = wx.Panel(self)self.button = wx.Button(self.panel, label="Click Me")app = wx.App(False)
frame = MyFrame(None, 'Button Example')
frame.Show(True)
app.MainLoop()
在这个例子中,我们创建了一个名为button的wx.Button对象,并将其添加到panel中。
wx.StaticText
wx.StaticText控件用于显示静态文本。
import wxclass MyFrame(wx.Frame):def __init__(self, parent, title):wx.Frame.__init__(self, parent, title=title, size=(300, 200))self.panel = wx.Panel(self)self.statictext = wx.StaticText(self.panel, label="Hello World")app = wx.App(False)
frame = MyFrame(None, 'StaticText Example')
frame.Show(True)
app.MainLoop()
在这个例子中,我们创建了一个名为statictext的wx.StaticText对象,并将其添加到panel中。
布局管理器
wxPython支持多种布局管理器,包括BoxSizer、GridSizer、FlexGridSizer、GridBagSizer等。布局管理器用于控制控件的位置和大小。
BoxSizer
BoxSizer布局管理器用于在水平或垂直方向上排列控件。下面是一个使用BoxSizer布局管理器的例子:
import wxclass MyFrame(wx.Frame):def __init__(self, parent, title):super(MyFrame, self).__init__(parent, title=title, size=(300, 200))self.InitUI()def InitUI(self):panel = wx.Panel(self)vbox = wx.BoxSizer(wx.VERTICAL)hbox1 = wx.BoxSizer(wx.HORIZONTAL)st = wx.StaticText(panel, label='This is a static text')hbox1.Add(st, proportion=1)vbox.Add(hbox1, flag=wx.EXPAND|wx.LEFT|wx.RIGHT|wx.TOP, border=10)hbox2 = wx.BoxSizer(wx.HORIZONTAL)btn1 = wx.Button(panel, label='Quit')btn2 = wx.Button(panel, label='Open')hbox2.Add(btn1, proportion=0)hbox2.Add(btn2, proportion=0, flag=wx.LEFT|wx.BOTTOM, border=5)vbox.Add(hbox2, flag=wx.ALIGN_RIGHT|wx.RIGHT, border=10)panel.SetSizer(vbox)self.Centre()self.Show(True)app = wx.App(False)
frame = MyFrame(None, 'BoxSizer Example')
app.MainLoop()
在这个例子中,创建了一个wx.Frame对象,并为它设置了标题和大小。我们还创建了一个wx.Panel对象,并将其添加到框架中。使用wx.BoxSizer(wx.VERTICAL)创建了一个垂直方向的BoxSizer对象,并将其设置为panel的sizer。
创建两个wx.BoxSizer(wx.HORIZONTAL)对象,一个用于放置静态文本控件,一个用于放置两个按钮控件。使用wx.StaticText创建了一个静态文本控件,并将其添加到第一个水平方向的BoxSizer对象中。使用wx.Button创建了两个按钮控件,并将它们添加到第二个水平方向的BoxSizer对象中。
GridSizer
GridSizer布局管理器用于创建网格布局。下面是一个使用GridSizer布局管理器的例子:
import wxclass MyFrame(wx.Frame):def __init__(self, parent, title):wx.Frame.__init__(self, parent, title=title, size=(300, 200))self.panel = wx.Panel(self)grid = wx.GridSizer(2, 2, 10, 10)self.statictext1 = wx.StaticText(self.panel, label="Name:")self.statictext2 = wx.StaticText(self.panel, label="Age:")self.textctrl1 = wx.TextCtrl(self.panel)self.textctrl2 = wx.TextCtrl(self.panel)grid.Add(self.statictext1, 0, wx.ALIGN_RIGHT)grid.Add(self.textctrl1, 0, wx.EXPAND)grid.Add(self.statictext2, 0, wx.ALIGN_RIGHT)grid.Add(self.textctrl2, 0, wx.EXPAND)self.panel.SetSizer(grid)app = wx.App(False)
frame = MyFrame(None, 'GridSizer Example')
frame.Show(True)
app.MainLoop()
在这个例子中,我们创建了一个名为grid的wx.GridSizer对象,并将其添加到panel中。该网格布局由两行两列组成,每个单元格之间的间距为10像素。
FlexGridSizer
FlexGridSizer布局管理器用于创建灵活的网格布局。它允许某些行和/或列具有不同的大小和/或比例。下面是一个使用FlexGridSizer布局管理器的例子:
import wxclass MyFrame(wx.Frame):def __init__(self, parent, title):wx.Frame.__init__(self, parent, title=title, size=(300, 200))self.panel = wx.Panel(self)flexgrid = wx.FlexGridSizer(2, 2, 10, 10)self.statictext1 = wx.StaticText(self.panel, label="Name:")self.statictext2 = wx.StaticText(self.panel, label="Age:")self.textctrl1 = wx.TextCtrl(self.panel)self.textctrl2 = wx.TextCtrl(self.panel)flexgrid.Add(self.statictext1, 0, wx.ALIGN_RIGHT)flexgrid.Add(self.textctrl1, 0, wx.EXPAND)flexgrid.Add(self.statictext2, 1, wx.ALIGN_RIGHT)flexgrid.Add(self.textctrl2, 1, wx.EXPAND)flexgrid.AddGrowableCol(1)self.panel.SetSizer(flexgrid)app = wx.App(False)
frame = MyFrame(None, 'FlexGridSizer Example')
frame.Show(True)
app.MainLoop()
在这个例子中,我们创建了一个名为flexgrid的wx.FlexGridSizer对象,并将其添加到panel中。该网格布局由两行两列组成,每个单元格之间的间距为10像素。第一行和第二行的第二列具有相同的大小和比例,因为它们都使用了默认值。但是,我们使用了AddGrowableCol(1)方法,使第二列变得可扩展,这意味着当窗口调整大小时,第二列将增长并填充任何可用空间。
WrapSizer
WrapSizer布局管理器用于创建自动换行的布局。它允许您添加任意数量的控件,并自动将它们排列成多行。下面是一个使用WrapSizer布局管理器的例子:
import wxclass MyFrame(wx.Frame):def __init__(self, parent, title):wx.Frame.__init__(self, parent, title=title, size=(300, 200))self.panel = wx.Panel(self)wrapsizer = wx.WrapSizer(wx.HORIZONTAL)self.button1 = wx.Button(self.panel, label="Button 1")self.button2 = wx.Button(self.panel, label="Button 2")self.button3 = wx.Button(self.panel, label="Button 3")self.button4 = wx.Button(self.panel, label="Button 4")self.button5 = wx.Button(self.panel, label="Button 5")wrapsizer.Add(self.button1, 0, wx.EXPAND|wx.ALL, 5)wrapsizer.Add(self.button2, 0, wx.EXPAND|wx.ALL, 5)wrapsizer.Add(self.button3, 0, wx.EXPAND|wx.ALL, 5)wrapsizer.Add(self.button4, 0, wx.EXPAND|wx.ALL, 5)wrapsizer.Add(self.button5, 0, wx.EXPAND|wx.ALL, 5)self.panel.SetSizer(wrapsizer)app = wx.App(False)
frame = MyFrame(None, 'WrapSizer Example')
frame.Show(True)
app.MainLoop()
在这个例子中,我们创建了一个名为wrapsizer的wx.WrapSizer对象,并将其添加到panel中。该布局管理器使用水平方向,因此当添加的控件超过可用空间时,它们将自动换行到下一行。
ScrolledWindow
ScrolledWindow控件允许您在包含大量内容的窗口中滚动内容。下面是一个使用ScrolledWindow控件的例子:
import wxclass MyFrame(wx.Frame):def __init__(self, parent, title):wx.Frame.__init__(self, parent, title=title, size=(300, 200))self.panel = wx.Panel(self)scrolled = wx.ScrolledWindow(self.panel, -1)vbox = wx.BoxSizer(wx.VERTICAL)for i in range(30):label = "Line {}".format(i+1)statictext = wx.StaticText(scrolled, label=label)vbox.Add(statictext, 0, wx.EXPAND|wx.ALL, 5)scrolled.SetSizer(vbox)scrolled.SetScrollRate(0, 10)app = wx.App(False)
frame = MyFrame(None, 'ScrolledWindow Example')
frame.Show(True)
app.MainLoop()
在这个例子中,我们创建了一个名为scrolled的wx.ScrolledWindow对象,并将其添加到panel中。使用wx.BoxSizer创建了一个垂直布局,其中包含30个静态文本标签。将vbox布局添加到scrolled窗口中,并使用SetSizer方法将布局应用于窗口。
为了启用滚动,我们使用SetScrollRate方法设置垂直滚动条的滚动速度为10个像素。
GridBagSizer
GridBagSizer布局管理器允许您以网格的形式布置控件,并允许您自由控制每个单元格中控件的大小和位置。下面是一个使用GridBagSizer布局管理器的例子:
import wxclass MyFrame(wx.Frame):def __init__(self, parent, title):wx.Frame.__init__(self, parent, title=title, size=(300, 200))self.panel = wx.Panel(self)gridbag = wx.GridBagSizer(5, 5)self.button1 = wx.Button(self.panel, label="Button 1")self.button2 = wx.Button(self.panel, label="Button 2")self.button3 = wx.Button(self.panel, label="Button 3")self.button4 = wx.Button(self.panel, label="Button 4")self.button5 = wx.Button(self.panel, label="Button 5")gridbag.Add(self.button1, pos=(0, 0), span=(1, 1), flag=wx.EXPAND|wx.ALL, border=5)gridbag.Add(self.button2, pos=(0, 1), span=(1, 1), flag=wx.EXPAND|wx.ALL, border=5)gridbag.Add(self.button3, pos=(1, 0), span=(1, 2), flag=wx.EXPAND|wx.ALL, border=5)gridbag.Add(self.button4, pos=(2, 0), span=(1, 1), flag=wx.EXPAND|wx.ALL, border=5)gridbag.Add(self.button5, pos=(2, 1), span=(1, 1), flag=wx.EXPAND|wx.ALL, border=5)self.panel.SetSizer(gridbag)app = wx.App(False)
frame = MyFrame(None, 'GridBagSizer Example')
frame.Show(True)
app.MainLoop()
在这个例子中,我们创建了一个名为gridbag的wx.GridBagSizer对象,并将其添加到panel中。使用Add方法将5个按钮添加到网格中,并使用pos参数指定按钮在网格中的位置,使用span参数指定按钮跨越的行数和列数。
我们还可以使用flag参数来指定控件在单元格中的对齐方式和填充方式,以及使用border参数来指定控件周围的边框宽度。
颜色选择器和文件对话框
除了各种布局管理器和控件之外,wxPython还提供了一些方便的对话框和工具类,用于处理常见的任务,例如选择颜色或文件。下面是两个示例:
import wxclass MyFrame(wx.Frame):def __init__(self, parent, title):wx.Frame.__init__(self, parent, title=title, size=(300, 200))self.panel = wx.Panel(self)self.colorbutton = wx.Button(self.panel, label="Choose Color", pos=(50, 50))self.filebutton = wx.Button(self.panel,label="Choose File", pos=(150, 50))self.colorbutton.Bind(wx.EVT_BUTTON, self.OnColor)self.filebutton.Bind(wx.EVT_BUTTON, self.OnFile)def OnColor(self, event):dlg = wx.ColourDialog(self.panel)if dlg.ShowModal() == wx.ID_OK:color = dlg.GetColourData().GetColour().Get()print("You selected color:", color)dlg.Destroy()def OnFile(self, event):dlg = wx.FileDialog(self.panel, "Choose a file", wildcard="*.txt")if dlg.ShowModal() == wx.ID_OK:path = dlg.GetPath()print("You selected file:", path)dlg.Destroy()app = wx.App(False)
frame = MyFrame(None, 'Dialogs and Tools Example')
frame.Show(True)
app.MainLoop()
在这个例子中,我们创建了两个wx.Button对象,一个用于选择颜色,一个用于选择文件。我们将它们添加到panel中,并为它们绑定了OnColor和OnFile方法,用于处理单击事件。
当用户单击颜色选择按钮时,我们创建了一个wx.ColourDialog对象,并使用GetColourData方法获取用户选择的颜色。当用户单击文件选择按钮时,我们创建了一个wx.FileDialog对象,并使用GetPath方法获取用户选择的文件路径。
最后,本文介绍了wxPython中的一些常见布局管理器和控件,以及如何使用对话框和工具类。希望这篇文章对您学习和使用wxPython有所帮助。
相关文章:

Python GUI应用程序开发之wxPython使用详解
概要 wxPython是一个强大的跨平台GUI工具包,它使用Python编程语言开发,提供了丰富的控件功能。如果你是一名Python开发者,而且希望创建一个功能齐全的桌面应用程序,那么wxPython是一个值得考虑的选择。 什么是wxPython wxPython…...

【电子学会真题】青少年软件编程(C语言)等级考试试卷(一级) 2021年9月
试卷下载 pdf 格式下载:https://download.csdn.net/download/SHUTIAN2010/88255543 word 格式下载:https://download.csdn.net/download/SHUTIAN2010/88255558 1.计算乘积 一行两个整数a、b,以空格分隔。(0࿱…...

学习完毕JavaSE的感想
今天,把Java复习完毕了,之前学习的时候,学校里学的总是有限的 ,自己上手操作之后才发觉差的很多,部署服务器发现要学操作系统,学完了web基础 ,又发现还得学前后端分离vue react这些,…...

FastJson的学习
fastjson是阿里巴巴的开源JSON解析库,它可以解析JSON格式的字符串,支持将Java Bean序列化为JSON字符串,也可以从JSON字符串反序列化到JavaBean。 fastjson是json的序列化和反序列化 一、添加依赖 <dependency><groupId>com.ali…...

python scrapy框架
scrapy概述 Scrapy,Python开发的一个快速、高层次的屏幕抓取和web抓取框架,用于抓取web站点并从页面中提取结构化的数据。Scrapy用途广泛,可以用于数据挖掘、监测和自动化测试 scrapy安装 pip install scrapy -i https://pypi.tuna.tsinghua…...

滑动窗口系列3-Leetcode134题加油站
在一条环路上有 n 个加油站,其中第 i 个加油站有汽油 gas[i] 升。 你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。 给定两个整数数组 gas 和 cost &…...

LOIC(low orbit ion cannon)
前言 重要的话说三遍: 该程序仅用于学习用途,请勿用于非法行为上!!! 该程序仅用于学习用途,请勿用于非法行为上!!! 该程序仅用于学习用途,请勿用于非法行为上…...

从格灵深瞳中报稳定盈利,看AI公司的核心竞争力
2023年过半,人工智能产业话题不断。大模型和AIGC掀起热潮,让众多AI公司开始进入新一轮竞赛。但与此同时,不少AI公司依然处于亏损中,研发投入和商业产出难以实现正循环。如何形成健康的商业模式,仍是一大挑战。 AI公司…...

理解 Databend Cluster key 原理及使用
Databend Cluster Key 是指 Databend 可以按声明的 key 排序存储,主要用于用户对时间响应比较高,同时愿意为这个 cluster key 进行额排序操作的用户。 Databend 只支持一个 Cluster key,Cluster key中可以包含多列及表达式。 基本语法 -- 语…...

C++day3(类、this指针、类中的特殊成员函数)
一、Xmind整理: 二、上课笔记整理: 1.类的应用实例 #include <iostream> using namespace std;class Person { private:string name; public:int age;int high;void set_name(string n); //在类内声明函数void show(){cout << "na…...

Qt中的配置文件:实现个性化应用程序配置与保存加载
一、前言 在现代软件开发中,用户对于应用程序的个性化配置和设置变得越来越重要。为了满足用户需求并提供更好的用户体验,开发人员常常需要实现一种机制,以便在每次启动应用程序时能够记住用户上次的配置。这样用户就可以方便地恢复到他们熟悉的环境,无需重新进行所有设置…...

Navicat激活时出现rsa public key not find错误
Navicat激活时出现rsa public key not find错误 在激活时,先不打开应用,先用管理员身份打开注册机Navicat_Keygen_Patch_v5.6_By_DFoX.exe,Navicat v15——>MySql——>Simplified Chinese——>Patch,执行完这些步骤之后…...

FFmpeg5.0源码阅读——URLContext和URLProtocol
摘要:本文描述FFmpeg中URLContext和URLProtocal的实现。 关键字:URLContext、URLProtocal FFmpeg中URLProtocol是具体的协议的抽象,其中定义了对应协议的抽象,其中包含了具体协议的操作函数指针。而URLContext是对协议操作的抽…...

Qt的输出
目录 基本分类 C风格输出 C风格 可以抑制输出 方法一 方法二 在Qt中进行log输出, 一般不使用c中的printf, 也不是使用C中的cout, Qt框架提供了专门用于日志输出的类, 头文件名为 QDebug。 基本分类 qDebug:调试信息提示 qInfo :输出信息 qWarnin…...

长胜证券:久违普涨再现 大盘回升有望加速
获得利好支撑后,大盘开始继续反弹。 沪指周二一路震动反弹,站上3100点整数关口后继续上攻并打破10日均线限制。深成指同样低开高走,全日体现明显强于沪指。 到收盘,沪指报收3135.89点,上涨1.2%;深成指报收…...

WPF .NET 7.0学习整理(一)
参照文档进行不系统的整理,看到那写到那O.o 依赖属性 DependencyProperty:使用专有字段支持属性的标准模式的替代方法。 DependencyObject:定义了可以注册和拥有依赖属性的基类。 public static readonly DependencyProperty IsSpinningPr…...

数据分析简介
判断采集数据的有效性和进行数据校准是数据处理中重要的步骤。以下是一些常见的方法和步骤可以帮助你进行数据有效性的判断和数据校准: 数据有效性判断: 数据范围:检查数据是否落在合理的范围内。根据具体情况,确定真实数据的上下限ÿ…...

解读未知:文本识别算法的突破与实际应用
解读未知:文本识别算法的突破与实际应用 1.文本识别算法理论 背景介绍 文本识别是OCR(Optical Character Recognition)的一个子任务,其任务为识别一个固定区域的的文本内容。在OCR的两阶段方法里,它接在文本检测后面…...

[第七届蓝帽杯全国大学生网络安全技能大赛 蓝帽杯 2023]——Web方向部分题 详细Writeup
Web LovePHP 你真的熟悉PHP吗? 源码如下 <?php class Saferman{public $check True;public function __destruct(){if($this->check True){file($_GET[secret]);}}public function __wakeup(){$this->checkFalse;} } if(isset($_GET[my_secret.flag]…...

el-backtop返回顶部的使用
2023.8.26今天我学习了如何使用el-backtop组件进行返回页面顶部的效果,效果如: <el-backtop class"el-backtop"style"right: 20px; bottom: 150px;"><i class"el-icon-caret-top"></i></el-backtop&…...

Go 官方标准编译器中所做的优化
本文是对#102 Go 官方标准编译器中实现的优化集锦汇总[1] 内容的记录与总结. 优化1-4: 字符串和字节切片之间的转化 1.紧跟range关键字的 从字符串到字节切片的转换; package mainimport ( "fmt" "strings" "testing")var cs10086 s…...

C语言程序设计——小学生计算机辅助教学系统
题目:小学生计算机辅助教学系统 编写一个程序,帮助小学生学习乘法。然后判断学生输入的答案对错与否,按下列任务要求以循序渐进的方式分别编写对应的程序并调试。 任务1 程序首先随机产生两个1—10之间的正整数,在屏幕上打印出问题…...

SQL自动递增的列恢复至从0开始
在许多数据库管理系统中,当你删除表格中的所有数据时,自动递增的列(也称为自增列、标识列或序列)的计数器通常不会重置为 0。这是出于性能和数据完整性方面的考虑,以避免因删除数据而导致的自增列值冲突。即使你删除了…...

介绍一下CDN
CDN(内容分发网络,Content Delivery Network)是一个由多个服务器组成的分布式网络,它的目的是将内容高效地传送到用户。下面是CDN的工作原理及其主要特点: 内容分发:当用户首次请求某一特定内容时ÿ…...

2023年最新 Github Pages 使用手册
参考:GitHub Pages 快速入门 1、什么是 Github Pages GitHub Pages 是一项静态站点托管服务,它直接从 GitHub 上的仓库获取 HTML、CSS 和 JavaScript 文件,(可选)通过构建过程运行文件,然后发布网站。 可…...

docker 安装 Nginx
1、下载 docker pull nginx:latest 2、本地创建管理目录 mkdir -p /var/docker/nginx/conf mkdir -p /var/docker/nginx/log mkdir -p /var/docker/nginx/html 3、将容器中的相应文件复制到管理目录中 /usr/docker/nginx docker run --name nginx -p 80:80 -d nginxdocke…...

【NLP的python库(01/4) 】: NLTK
一、说明 NLTK是一个复杂的库。自 2009 年以来不断发展,它支持所有经典的 NLP 任务,从标记化、词干提取、词性标记,包括语义索引和依赖关系解析。它还具有一组丰富的附加功能,例如内置语料库,NLP任务的不同模型以及与S…...

Java IDEA Web 项目 1、创建
环境: IEDA 版本:2023.2 JDK:1.8 Tomcat:apache-tomcat-9.0.58 maven:尚未研究 自行完成 IDEA、JDK、Tomcat等安装配置。 创建项目: IDEA -> New Project 选择 Jakarta EE Template:选择…...

leetcode316. 去除重复字母(单调栈 - java)
去除重复字母 题目描述单调栈代码演示进阶优化 上期经典 题目描述 难度 - 中等 leetcode316. 去除重复字母 给你一个字符串 s ,请你去除字符串中重复的字母,使得每个字母只出现一次。需保证 返回结果的字典序最小(要求不能打乱其他字符的相对…...

零散笔记:《Spring实战》Thymeleaf
1、Thymeleaf模板就是增加一些额外元素属性的HTML,这些属性能够指导模板如何渲染request数据。 <p th:test "${message}">placeholder message</p> th我推测是中文的”替换“。 2、th:each,迭代元素集合。 <div th:each &qu…...