从本节开始,我们将开始新的学习和分享:360百科:GUI编程。前面写过的程序都是基于控制台的,即用户与程序的交互通过Console来完成的。
所谓GUI(图形用户接口)是相较于命令行(DOS UNIX系列)而言的:和计算机使用的命令行界面相比,图形界面对于用户来说在视觉上更易于接受,展示效果也更加美观。
这种图形化交互界面在实现上类似于搭积木(or 拼图)游戏,一个个的组件(Widget)被组合到一起 放置到一个窗口(Window)里面,进行渲染呈现。(对于Python而言,丰富的组件足以支撑快速的实现 图形界面和用户交互)
我们大家如果学过JSP 或者 ASP等,这些组件相当于页面的button text list等。然后加上对于组件上发生事件的处理响应就构成了一个完整的程序。
OK,我们这里主要是学习Tkinter库,其中文文档在线阅读:Tk图形用户界面(GUI)和An Introduction to Tkinter
注:上面的root.mainloop()
是主窗口的mainloop事件循环。负责监听用户的触发事件的发生,然后在发生事件后调用相应的事件处理,最后反复监测(死循环相当于)。
但是上面仅仅是一个空的主窗口,OK 下面我们来加上一些组件:
但是上面程序运行之后,弹出来的主窗口位置在于左上角。我们下面就借助于geometry('wxh±x±y')
来设置主窗口的位置:
w 是宽度,h是高度
+x 表示距屏幕左边的距离,-x表示距屏幕右边的距离
+y 表示距屏幕上边的距离,-y表示距屏幕下边的距离
示例如下:
OK,这就是GUI编程的一个简单实例,下面我们来详细看一下GUI编程的整体描述:
上面也说过了 图形用户界面的设计和 “拼图游戏” 差不多,相互集成在主窗口里面的诸多组件共同组成了整个用户界面。(若是一个组件里面还可以放置组件,则其被称为容器)下面是Tkinter的GUI组件继承关系图:
1、上面的Wm:主要是用作和窗口管理器间的通信
2、Misc:所有组件的根父类
3、TopLevel:最顶层的一个弹框(不搞定它,别的都动不了)
4、Pack、Place和Grid:布局管理器(管理组件 大小 位置),合理的排布组件
5、Tk:应用程序的主窗口(一般应用程序都直接or间接使用Tk)
6、BaseWidget:所有组件的父类
7、Widget:所有组件类的父类,它继承自四个父类。所有GUI组件都有这四个父类的属性和方法
8、Frame:它是Tkinter组件的一个框架组件,表示一个虚拟长方形的区域(可以看做是一个容器,从而实现复杂的布局)。
OK,如果我们大家想看一下 类的继承层次结构
,可以在类的定义处,右键-->Diagram-->show Diagram
Lable 即标签,主要用于显示静态信息 如:文本信息(也可以显示图像)。其常见属性如下:
1、width 和 height:用于指定区域大小。如果显示是文本,则以单个英文字符大小为单位(一个汉字宽度占 2个字符位置,高度和英文字符一样);如果显示是图像,则以像素为单位。默认值是根据具体显示的内容动态调整。
2、font:指定字体和字体大小,如:font = (font_name,size)
3、image:显示在 Label 上的图像,目前 tkinter 只支持.gif
格式。
4、fg(foreground)和 bg(background):前景色 背景色
5、justify:针对多行文字的对齐,可设置 justify 属性,可选值"left"、"center" 和 "right"
下面来看一下,我们使用Lable实现:一行、图像和多行:
#coding=utf-8
'''
Author :SongBaoBao
Project :MyGUI
FileName:Simple_Template.py
Currtime:2020/7/11--04:42
Commpany:Tsinghua University
MyCsdnIs:https://blog.csdn.net/weixin_43949535
MyLolLpl:Royal Never Give Up
'''
#
# 以后所有的GUI编程(OOP)的初始模板
#
from tkinter import *
from tkinter import messagebox
class myApplication(Frame):
"""
类 Application继承了 Frame 及通过继承拥有了父类的特性;并组织整个 GUI 程序
"""
def __init__(self,master=None): # 定义构造函数
super().__init__(master) # 调用父类的构造器,super()是代表父类的定义 而非父类对象
self.master=master
self.pack() # self本身就是一个组件
self.createWidget()
def showMessage(self):
messagebox.showinfo("Message","Your name is SongBaoBao!")
def createWidget(self):
"""
创建窗口中的对象(创建组件),self就是这个组件容器
:return:
"""
# ************************************** #
# 创建一个Lable
self.lable1=Label(self,text="你是一个小可爱!",width=16,height=2,bg="gold",fg="#5CADAD",font=("STXingkai",24))
self.lable1.pack()
# ************************************** #
# 创建一个Lable 显示图像
global myPicture # 声明成全局变量,否则本方法执行完 图像对象消失了(窗口无法显示)
myPicture=PhotoImage(file="../bear.gif")
self.lable2=Label(self,image=myPicture)
self.lable2.pack()
# ************************************** #
# 创建一个Lable 显示多行文本
self.lable3=Label(self,text="Royal Never Give Up\n皇族永不言败",borderwidth=2,relief="groove",justify="center"
,font=("STXingkai",32))
self.lable3.pack()
if __name__=='__main__':
# 第一步:通过类的默认构造函数 来构造主窗口对象
root=Tk()
root.title("这是窗口标题")
root.geometry('1000x800+500+250') # 距离左边500 上边250
# 第二步:创建一个对象
app = myApplication(master=root) # 意思就是这个app会放在root主窗口里面
# 最后开启事件循环
root.mainloop()
来看一下效果:
大家也注意到了在上面 创建一个Lable的时候,通过传入不同的参数实现标签的不一样的展示效果
。
这些Options的设置控制了该组件的不同状态,选项参数的设置是可以有下面三种方式:
# 创建一个Lable
self.lable1 = Label(self,text="你是一个小可爱!",height=2,fg="#5CADAD",font=("STXingkai",24)) # 这样传 这些参数都被构造方法的 **kw 进行接收
self.lable1["width"]=16 # 创建对象后,使用字典索引方式([]运算符重载)
self.lable1.config(bg="gold") # 创建对象后,使用 config()方法
# 注:上面三种最终都会到_configure方法上
对于上面的这个Lable组件,我们可以有两种方式来查看Options选项:
第一种:config()方法的返回值字典
mydict=self.lable1.config()
print(type(mydict))
for key, value in mydict.items():
print('\033[1;35m',key,'\033[0m',end=":")
print(value)
class Label(Widget):
"""Label widget which can display text and bitmaps."""
def __init__(self, master=None, cnf={}, **kw):
"""Construct a label widget with the parent MASTER.
STANDARD OPTIONS
activebackground, activeforeground, anchor,
background, bitmap, borderwidth, cursor,
disabledforeground, font, foreground,
highlightbackground, highlightcolor,
highlightthickness, image, justify,
padx, pady, relief, takefocus, text,
textvariable, underline, wraplength
WIDGET-SPECIFIC OPTIONS
height, state, width
"""
Widget.__init__(self, master, 'label', cnf, kw)
上面两种方式都可以看到组件Lable的诸多Option,在第二种里面有:“standard options 标准选项”和“widget-specific options 组件特定选项”
:
OK 我们将常见的选项做一个简单的汇总 如下:
按钮用来执行用户的单击操作。Button 可以包含文本,也可以包含图像。按钮被单击后会自动调用对应事件所绑定的方法。
下面就来看一下,文本button和图片button的使用:
注:有时候我们会遇到一个处于灰色状态的按钮(点不动):
self.b = Button(self, text="Help", state=DISABLED)
self.b.pack()
上面汇总的时候,已经提过了:Entry是一个单行输入框(用户可以输入内容)。注:当用户输入的文字长度超过Entry 组件的宽度时,文字会自动向后滚动。如果想输入多行文本,则需要使用 Text 控件。
下面来看一个实例:
上面就完美的显示了:输入框里的默认提示信息的打印
上面我们在登录点击之后,后台就得到了用户输入的相关信息(上面在输入密码的时候是明码)于是修正如下:
源代码如下:
#coding=utf-8
'''
Author :SongBaoBao
Project :MyGUI
FileName:myEntry.py
Currtime:2020/7/11--15:37
Commpany:Tsinghua University
MyCsdnIs:https://blog.csdn.net/weixin_43949535
MyLolLpl:Royal Never Give Up
'''
#
# 测试单行文本框
#
from tkinter import *
from tkinter import messagebox
class myApplication(Frame):
"""
类 Application继承了 Frame 及通过继承拥有了父类的特性;并组织整个 GUI 程序
"""
def __init__(self,master=None): # 定义构造函数
super().__init__(master) # 调用父类的构造器,super()是代表父类的定义 而非父类对象
self.master=master
self.pack() # self本身就是一个组件
self.createWidget()
def showMessage(self,messageStr):
print("现在登录的人的用户名:"+self.entry2.get()+" 密码是:",self.entry4.get())
messagebox.showinfo("Message",messageStr)
def createWidget(self):
"""
创建窗口中的对象(创建组件),self就是这个组件容器
:return:
"""
# ************************************** #
# 创建一个Lable 显示一行文本
self.lable1 = Label(self, text="用户名:", borderwidth=2, relief="groove",
justify="left", font=("STXingkai", 32),fg="#5CADAD")
self.lable1.pack()
# ************************************** #
# 创建一个Entry 存放单行内容
inputStr=StringVar() # 初始化 然后绑定(之后组件内容和inputStr变量内容保持一样)
self.entry2=Entry(self,textvariable=inputStr)
self.entry2.pack()
inputStr.set("这里输入用户名")
# ************************************** #
# 创建一个Lable 显示一行文本
self.lable3 = Label(self, text="密码", borderwidth=2, relief="groove",
justify="left", font=("STXingkai", 32),fg="#5CADAD")
self.lable3.pack()
# ************************************** #
# 创建一个Entry 存放单行内容
inputStr2 = StringVar() # 初始化 然后绑定(之后组件内容和inputStr变量内容保持一样)
self.entry4 = Entry(self, textvariable=inputStr2,show='*')
self.entry4.pack()
# ************************************** #
# 创建一个Button 进行登录
self.button5=Button(self,text="点击登录",command=lambda:self.showMessage("登录成功!"))
self.button5.pack()
if __name__=='__main__':
# 第一步:通过类的默认构造函数 来构造主窗口对象
root=Tk()
root.title("这是窗口标题")
root.geometry('1000x800+500+250') # 距离左边500 上边250
# 第二步:创建一个对象
app = myApplication(master=root) # 意思就是这个app会放在root主窗口里面
# 最后开启事件循环
root.mainloop()
多行文本框主要是用于显示多行文本,还可以显示网页链接、图片、 HTML 页面、CSS 样式表、添加图片和添加组件等。因此,也常被当做简单的文本处理器、文本编辑器或者网页浏览器来使用。
OK,下面实现一个简单的文本编译器来详解Text特性(行开始为1,列开始为0)
首先第一点 请大家先看一下下面这个例子(然后思考一下问题所在):
上面这个插入位置 好像没有起作用 !
我们来详细看一下这个代码就会发现问题所在:在self.text1.insert(1.0,"hello宋")
结束的时候 还没有第二行,插入位置虽然为2.0 但是不起作用的。于是就直接将0123456789\nabcdefg
接在宋
的后面 于是结果如下:
此时的Text里面也就没有第三行,同理 上面的llllll
也就跟在上面一行的后面!
于是我们可以这么做:加上一行self.text1.insert(1.0,"hello\n宋")
OK,我们来把这个过程捋一捋:
1、在
self.text1.insert(1.0,"hello\n宋")
之后,实际上Text已经有两行内容
2、上面这句之后,第二行只有一个 宋 字(为2字符)。2.0, "0123456789\nabcdefg"
的意思就是在第二行的开始插入这句话。于是结束之后 新的第二行为:0123456789
,第三行为:abcdefg宋
3、于是这时候我们也有了第三行,3.3, "llllll\n"
表示在d
的位置放置"llllll\n"
。于是换行之后,新的第三行为:abcllllll
,第四行为:defg宋
好,这个小例子之后相信大家已经对Text的一些特点有了更加深入的了解!下面我们开始第二个例子(简易文本编译器):(下面是创建插件部分的内容)
def createWidget(self):
"""
创建窗口中的对象(创建组件),self就是这个组件容器
:return:
"""
# ************************************** #
# 创建一个Text 显示多行文本
self.text1=Text(root,width=160,height=120,bg="gold",fg="#5CADAD",font=(72))
self.text1.pack()
self.text1.insert(1.0,"hello\n宋baobao")
# ************************************** #
# 创建多个Button 存放多个内容
Button(self, text="插入文本", command = lambda:self.insertText("hello","world")).pack(side="left")
Button(self, text="返还文本函数1", command=lambda: self.returnText1(1.1,2.1)).pack(side="left")
Button(self, text="返还文本函数2", command=self.returnText2).pack(side="left")
Button(self, text="增加一个图片", command=lambda: self.addPicture("../bear.gif")).pack(side="left")
Button(self, text="增加一个按钮", command=lambda: self.addButton("这是一个按钮")).pack(side="left")
Button(self, text="tag控制文本", command=self.setTag).pack(side="left")
最开始的效果就是:
1、插入文本函数:插入一个你喜欢的文本字符串
注:现在我的光标放在“宋”
后面,结果如下:
2、返还文本函数1:一个区域内的文本
3、返还文本函数2:Text全部的文本
4、增加一个图片
注:它是在Text的最后面做的增加(我这里若是多次添加图片 显示不出来)
5、增加一个按钮
注:在光标后面添加的按钮
6、通过tag控制文本
点击上面的下划线链接之后:(是由webShow(self,event)
实现的)
上面这个简单的小程序源码如下:
#coding=utf-8
'''
Author :SongBaoBao
Project :MyGUI
FileName:myText.py
Currtime:2020/7/10--9:43
Commpany:Tsinghua University
MyCsdnIs:https://blog.csdn.net/weixin_43949535
MyLolLpl:Royal Never Give Up
'''
#
# 测试多行文本框 Text
#
from tkinter import *
import webbrowser
from tkinter import messagebox
class myApplication(Frame):
"""
类 Application继承了 Frame 及通过继承拥有了父类的特性;并组织整个 GUI 程序
"""
def __init__(self,master=None): # 定义构造函数
super().__init__(master) # 调用父类的构造器,super()是代表父类的定义 而非父类对象
self.master=master
self.pack() # self本身就是一个组件
self.createWidget()
# ************************************** #
# 插入文本函数:插入一个你喜欢的文本字符串
def insertText(self,message1,message2):
self.text1.insert(INSERT,message1) # 意思是:在光标所在的地方插入message1
self.text1.insert(END,message2) # 意思是:在光标所在的地方插入message2
# ************************************** #
# 返还文本函数1
def returnText1(self,str1,str2):
print(self.text1.get(str1,str2))
# ************************************** #
# 返还文本函数2
def returnText2(self):
print(self.text1.get(1.0,END))
# ************************************** #
# 增加一个图片
def addPicture(self,path):
global mypicture
self.mypicture=PhotoImage(file=path)
self.text1.image_create(END,image=self.mypicture)
# ************************************** #
# 增加一个按钮
def addButton(self,text):
button1=Button(self.text1,text=text)
self.text1.window_create(INSERT,window=button1)
# ************************************** #
# 打开一个链接
def webShow(self,event):
webbrowser.open("https://blog.csdn.net/weixin_43949535")
# ************************************** #
# 设置相关标签
def setTag(self):
# 清理掉Text全部数据
self.text1.delete(1.0,END)
# 插入一些文本信息
self.text1.insert(1.0,"皇族永不言败\nRNG加油!\n这是一个链接!")
# 设置第一个标签
self.text1.tag_add("皇族",1.0,1.12)
self.text1.tag_config("皇族",background="gold",font=("STXingkai",12))
# 设置第二个tag
self.text1.tag_add("link",3.0,3.12)
self.text1.tag_config("link",underline=True)
self.text1.tag_bind("link","" ,self.webShow)
def createWidget(self):
"""
创建窗口中的对象(创建组件),self就是这个组件容器
:return:
"""
# ************************************** #
# 创建一个Text 显示多行文本
self.text1=Text(root,width=160,height=120,bg="gold",fg="#5CADAD",font=(72))
self.text1.pack()
self.text1.insert(1.0,"hello\n宋baobao")
# ************************************** #
# 创建多个Button 存放多个内容
Button(self, text="插入文本", command = lambda:self.insertText("hello","world")).pack(side="left")
Button(self, text="返还文本函数1", command=lambda: self.returnText1(1.1,2.1)).pack(side="left")
Button(self, text="返还文本函数2", command=self.returnText2).pack(side="left")
Button(self, text="增加一个图片", command=lambda: self.addPicture("../bear.gif")).pack(side="left")
Button(self, text="增加一个按钮", command=lambda: self.addButton("这是一个按钮")).pack(side="left")
Button(self, text="tag控制文本", command=self.setTag).pack(side="left")
if __name__=='__main__':
# 第一步:通过类的默认构造函数 来构造主窗口对象
root=Tk()
root.title("这是窗口标题")
root.geometry('1000x800+500+250') # 距离左边500 上边250
# 第二步:创建一个对象
app = myApplication(master=root) # 意思就是这个app会放在root主窗口里面
# 最后开启事件循环
root.mainloop()
注:这个自从进入GUI编程之后,下面的代码示例(OOP思想)可以作为将来这一类的程序开发的最初模板:
#coding=utf-8
'''
Author :SongBaoBao
Project :MyGUI
FileName:Simple_Template.py
Currtime:2020/7/11--04:42
Commpany:Tsinghua University
MyCsdnIs:https://blog.csdn.net/weixin_43949535
MyLolLpl:Royal Never Give Up
'''
#
# 以后所有的GUI编程(OOP)的初始模板
#
from tkinter import *
from tkinter import messagebox
class myApplication(Frame):
"""
类 Application继承了 Frame 及通过继承拥有了父类的特性;并组织整个 GUI 程序
"""
def __init__(self,master=None): # 定义构造函数
super().__init__(master) # 调用父类的构造器,super()是代表父类的定义 而非父类对象
self.master=master
self.pack() # self本身就是一个组件
self.createWidget()
def showMessage(self):
messagebox.showinfo("Message","Your name is SongBaoBao!")
def createWidget(self):
"""
创建窗口中的对象(创建组件),self就是这个组件容器
:return:
"""
# ************************************** #
# 创建一个button
self.button1=Button(self)
self.button1["text"]="点击一下"
self.button1.pack() # 通过布局管理器放到窗口里面
self.button1["command"]=self.showMessage
# ************************************** #
# 创建一个退出button
self.quitButton1=Button(self,text="退出",command=root.destroy)
self.quitButton1.pack()
if __name__=='__main__':
# 第一步:通过类的默认构造函数 来构造主窗口对象
root=Tk()
root.title("这是窗口标题")
root.geometry('500x300+500+250') # 距离左边500 上边250
# 第二步:创建一个对象
app = myApplication(master=root) # 意思就是这个app会放在root主窗口里面
# 最后开启事件循环
root.mainloop()