python中GUI之tkinter 模块

目录

1.tkinter 模块使用

tkinter 介绍

创建一个简单的 GUI

给窗口添加小构件

小构件种类

小构件参数说明

查看某个小构件的所有方法和属性

常用小构件用法

Button:按钮用法

Label:标签用法

Radiobutton:单选按钮用法

Checkbutton:多选按钮用法

Entry:输入框用法

Combobox:下拉框用法

数据显示小构件

Listbox 显示数据

Text 文本编辑器(也可展示数据)

Treeview:表格数据

Treeview:树状数据

2.画布类 Canvas

3.几何管理器

网格管理器

包管理器

位置管理器

4.显示图像

5.菜单

下拉菜单

弹出菜单

6.鼠标、按键事件和绑定

7.动画

原理

示例1:滑动窗口

示例2:四辆汽车比赛

示例3:转动的时钟

8.滚动条

9.选择或者输入对话框

10.作业练习

简单的文本编辑器

贷款计算器

查找四个连续相同的数字

井字游戏

拖动小构件

显示 n 个颜色随机的球

n 个随机运动的球

画函数图像


1.tkinter 模块使用

tkinter 介绍

图形用户界面,简称GUI。Tkinter 是 Python 中开发 GUI 的其中一个实用模块。
Tkinter = T-K-inter,是 Tk interface 的缩写。
Tk 是一个可以被许多基于 Windows、Mac、UNIX 的程序设计语言同来开发 GUI 程序的 GUI 库。

创建一个简单的 GUI

import tkinter

mytk = tkinter.Tk()  # Tk 是一个类,用来创建一个窗口
mytk.title("hello tkinter")  # 设置窗口的名字,默认名字为 tk
mytk.geometry("600x400+200+0")  # 宽度为 600,高度为 400,x,y 窗口左上角的坐标
mytk.mainloop()  # 启动

给窗口添加小构件

小构件种类

Tkinter小构件种类
小构件类描述
Button单一按钮
Checkbutton多种选择时的多选按钮
Radiobutton 多种选择时的单选按钮
Entry文本输入框
Frame局部框架。包含其它小构件的一个容器小构件。
Canvas画布。结构化的图形,用于绘制图形、创建图形编辑器。或者实现自定的小构件类
Menu菜单栏。可实现下拉菜单或者弹出菜单。
Menubutton实现下拉菜单的菜单按钮
Label标签。用来显示文本或者图像。
Message显示文本,类似标签,但是能自定将文本放在指定的宽度或高度比内
Text格式化的文本显示,允许不同的风格和属性显示文本,还可以编辑文本,也支持内嵌的图片和窗口。

小构件参数说明

小构件的参数:
  • 第一个参数总是父容器(就是小构件将要放置在哪个容器里)
  • 命名参数 ,text 或者 image,可以通过插入换行字符\n 来多行显示文本,text = "大威\n 天龙"
  • 字体参数 ,font,与 text 参数配合使用,比如font = "Times 20 bold",font = "Times 20 bold italic overstrike underline",font = ("华文彩云", 260)
  • 颜色参数 ,fg 和 bg,"red"、"yellow",也可以使用字符串"#RRGGBB",比如"#AB84F9"。fg 默认黑色,bg 默认白色。
  • 对齐 参数 justify 设置标签、按钮、输入框等的内容靠左还是靠右,默认居中
  • 鼠标参数 ,cursor,"arrow"(默认)、"circle"、"cross"
小构件的 pack 方法作用在于通过包管理器小构件放在父容器内

查看某个小构件的所有方法和属性

以Label为例
import tkinter

for item in dir(tkinter.Label):
    print(item)

常用小构件用法

Button:按钮用法

import tkinter
import os

window = tkinter.Tk()
window.geometry("600x400+0+0")


def func():
    print("正在打开记事本")
    os.system("notepad")


button1 = tkinter.Button(window, text="打开记事本", command=func)
button1.pack()

window.mainloop()

Label:标签用法

例子1:基础用法

import tkinter

window = tkinter.Tk()
window.geometry("600x400+0+0")

label1 = tkinter.Label(window, text="这是第一个标签", bg="red")
label1.pack()

label2 = tkinter.Label(window, text="这是第二个标签", bg="red")
label2["text"] = "这是第二个标签:新的内容"  # 改变内容
label2["fg"] = "white"  # 改变颜色
label2.pack()

window.mainloop()

例子2:搭配button按钮和变量

import tkinter

window = tkinter.Tk()
window.geometry("600x400+0+0")

v1 = tkinter.StringVar()
v2 = tkinter.StringVar()

label1 = tkinter.Label(window, text="这是第一个标签", bg="yellow", textvariable=v1)
label1.pack()

label2 = tkinter.Label(window, text="这是第二个标签", bg="blue", textvariable=v2)
label2["text"] = "这是第二个标签:新的内容"  # 改变内容
label2["fg"] = "white"  # 改变颜色
label2.pack()


def func(var1, var2):
    content1 = var1.get()
    content2 = var2.get()
    print(content1)
    print(content2)

    var1.set("[var1]" + content1)
    var2.set("[var2]" + content2)


button1 = tkinter.Button(window, text="搜索", command=lambda: func(v1, v2))
button1.pack()

window.mainloop()

Radiobutton:单选按钮用法

import tkinter


window = tkinter.Tk()
window.geometry("600x400+0+0")
label1 = tkinter.Label(window, text="这是一个标签", bg="green")
label1.pack()


def change_label_color(label, var):
    if var.get() == "R":
        label["bg"] = "red"
    elif var.get() == "Y":
        label["bg"] = "yellow"


v1 = tkinter.StringVar()

rbRed = tkinter.Radiobutton(window, text="red", bg="red", variable=v1, value='R',
                            command=lambda: change_label_color(label1, v1))
rbRed.pack()

rbYellow = tkinter.Radiobutton(window, text="yellow", bg="yellow", variable=v1, value='Y',
                               command=lambda: change_label_color(label1, v1))
rbYellow.pack()

window.mainloop()

Checkbutton:多选按钮用法

import tkinter

window = tkinter.Tk()
window.geometry("600x400+0+0")
label1 = tkinter.Label(window, text="这是一个标签", bg="yellow")
label1.pack()


def change_label_text(label, var1, var2):
    if var1.get() == 1 and var2.get() == 1:
        label["text"] = "中国和美国"
    elif var1.get() == 1:
        label["text"] = "中国"
    else:
        label["text"] = "美国"


v1 = tkinter.IntVar()
v2 = tkinter.IntVar()

c1 = tkinter.Checkbutton(window, text="中国", variable=v1,
                         command=lambda: change_label_text(label1, v1, v2))
c1.pack()

c2 = tkinter.Checkbutton(window, text="美国", variable=v2,
                         command=lambda: change_label_text(label1, v1, v2))
c2.pack()

window.mainloop()

Entry:输入框用法

第一种:输入直接获取值

import tkinter
import os


def go(entry1, entry2):
    print(entry1.get())
    print(entry2.get())
    if entry2.get() == "记事本":
        os.system("notepad")
    else:
        pass


win = tkinter.Tk()

button = tkinter.Button(win, text="搜索", command=lambda: go(e1, e2))
button.pack()

e1 = tkinter.Entry(win, width=50, bg="red", fg="black")
e2 = tkinter.Entry(win, show="*", width=50, bg="yellow", fg="black")  # show="*"表示让内容不显示出来
e1.pack()
e2.pack()

win.mainloop()

第二种:引入变量获取值

变量的get方法可以获取输入框的值,set方法可以改变输入框中的值
label1.pack()


def change_label_text(label, var1, var2):
    # var1.set("[var1]" + var1.get())
    # var2.set("[var2]" + var2.get())
    label["text"] = var1.get() + var2.get()


button1 = tkinter.Button(win, text="点击这里根据输入框改变标签内容的按钮",
                         command=lambda: change_label_text(label1, v1, v2))
button1.pack()

v1 = tkinter.StringVar()
entry1 = tkinter.Entry(win, width=50, fg="green", textvariable=v1)

v2 = tkinter.StringVar()
entry2 = tkinter.Entry(win, show="*", width=50, fg="red", textvariable=v2)

entry1.pack()
entry2.pack()

win.mainloop()

Combobox:下拉框用法

import tkinter
from tkinter import ttk


def go(com_list, _event, var):  # 处理事件
    print(var.get())
    var.set("[xxx]" + var.get())
    print(_event)
    print(com_list.get())  # 选中当前的值


win = tkinter.Tk()  #

v1 = tkinter.StringVar()  # 窗体自带的文本,新建一个值
combox_list = ttk.Combobox(win, textvariable=v1)  # 初始化
combox_list["values"] = ("1", "2", "3", "4")
combox_list.current(0)  # 默认选择第一个
combox_list.bind("<<ComboboxSelected>>", lambda event: go(combox_list, event, v1))  # 绑定事件
combox_list.pack()

win.mainloop()  # 进入消息循环

当事件被触发时,会产生event对象,lambda表达式会接收一个名为event的参数,并调用go函数,将combox_list和event作为参数传递。

在go函数中,您使用了com_list.get()来获取组合框的值,也可以用v1.get()用于获取通过StringVar关联的值。一般用关联的变量来或获取组合框的值。

数据显示小构件

Listbox 显示数据

import tkinter

win = tkinter.Tk()  # 构造窗体

mylist = tkinter.Listbox(win, width=100)  # 列表框

mylist.pack()

# 内容插入
for item in ["1", "asdsa", "asdsadsa", "asdsadsad"]:
    mylist.insert(tkinter.END, item)  # 尾部插入

win.mainloop()  # 进入消息循环

效果

Text 文本编辑器(也可展示数据)

import tkinter

win = tkinter.Tk()
text = tkinter.Text(win)
text.insert(tkinter.INSERT, "文本编辑器\r\n")
text.insert(tkinter.INSERT, "要掌握\r\n")
text.insert(tkinter.INSERT, "用来编辑文本和数据\r\n")
text.insert(tkinter.INSERT, "间接用来展示数据\r\n")
text.pack()
win.mainloop()

效果

Treeview:表格数据

import tkinter
from tkinter import ttk  # 导入内部包

win = tkinter.Tk()
tree = ttk.Treeview(win)  # 表格

tree['columns'] = ('姓名', '年龄', '身高')  # 像字典一样创建列对象

tree.column("姓名", width=100)  # 调整列的宽度,但不显示列
tree.column("年龄", width=100)
tree.column("身高", width=100)

tree.heading("姓名", text="姓名-name")  # 显示表头
tree.heading("年龄", text="年龄-age")
tree.heading("身高", text="身高-tall")

tree.insert("", 0, text="line1", values=('1', '2', '3'))  # 插入的行数
tree.insert("", 1, text="line2", values=('a', 'b', 'c'))
tree.insert("", 2, text="line3", values=('我', '爱', '你'))

tree.pack()

win.mainloop()

效果

Treeview:树状数据

import tkinter
from tkinter import ttk

win = tkinter.Tk()
tree = ttk.Treeview(win, height=10)

myid1 = tree.insert("", 0, "中国", text="中国 China", values=("1",))
# 前两个参数:parent,index。parent 为""表示根目录。index 为插入的位置。open 参数,布尔变量,是否显示子树枝

myid11 = tree.insert(myid1, 0, "广东", text="中国广东", values=("2",))
myid111 = tree.insert(myid11, 0, "广州", text="中国广东广州", values=("2",))
myid12 = tree.insert(myid1, 1, "江苏", text="中国江苏", values=("3",))
myid13 = tree.insert(myid1, 1, "四川", text="中国四川", values=("4",))


myid2 = tree.insert("", 1, "美国", text="美国 USA", values=("5",))
myid21 = tree.insert(myid2, 1, "加州", text="美国加州", values=("6",))

print(tree.item(myid11).keys())
print(dir(tree.item(myid11)))

tree.pack()
win.mainloop()

效果

2.画布类 Canvas

坐标系与参数说明

示例代码

from tkinter import *


class CanvasDemo:
    def __init__(self):
        window = Tk()
        window.title("Canvas Demo")

        self.canvas = Canvas(window, width=200, height=100, bg="white")
        self.canvas.pack()

        frame = Frame(window)
        frame.pack()

        btRectangle = Button(frame, text="Rectangle", command=self.displayRect)
        btOval = Button(frame, text="Oval", command=self.displayOval)
        btArc = Button(frame, text="Arc", command=self.displayArc)
        btPolygon = Button(frame, text="Polygon", command=self.displayPolygon)
        btLine = Button(frame, text="Line", command=self.displayLine)
        btString = Button(frame, text="String", command=self.displayString)
        btClear = Button(frame, text="Clear", command=self.clearCanvas)
        btRectangle.grid(row=1, column=1)
        btOval.grid(row=1, column=2)
        btArc.grid(row=1, column=3)
        btPolygon.grid(row=1, column=4)
        btLine.grid(row=1, column=5)
        btString.grid(row=1, column=6)
        btClear.grid(row=1, column=7)

        window.mainloop()

    def displayRect(self):
        self.canvas.create_rectangle(10, 10, 190, 90, tags="rect")

    def displayOval(self):
        self.canvas.create_oval(10, 10, 190, 90, fill="red", tags="oval")

    def displayArc(self):
        self.canvas.create_arc(10, 10, 190, 90, start=0, extent=90, width=8, fill="red", tags="arc")

    def displayPolygon(self):
        self.canvas.create_polygon(10, 10, 190, 90, 30, 50, tags="polygon")

    def displayLine(self):
        self.canvas.create_line(10, 10, 190, 90, fill="red", tags="line") # Arrow 参数末端为 last
        self.canvas.create_line(10, 90, 190, 10, width=9, arrow="last", activefill="blue", tags="line")

    def displayString(self):
        self.canvas.create_text(60, 40, text="Hi, I am a string", font="Times 10 bold underline", tags="String")

    def clearCanvas(self):
        self.canvas.delete("rect", "oval", "arc", "polygon", "line", "String")


CanvasDemo()

效果

点击按钮后

3.几何管理器

Tkinter 使用管理器将小构件放入容器中
Tkinter 支持三种几何管理器:网格管理器、包管理器、位置管理器
最好不要对同一容器中的小构件们使用多个管理器,但可以使用框架作为子容器以获取期望的布局

网格管理器

网格管理器将小构件放在容器中一个不可见网格的每个单元内
  • row 和 column 参数可以将小构件放在特定的行和列内
  • rowspan 和 columnspan 参数将小构件放在多行和多列中
  • sticky 参数将小构件放在所在行列的东南西北东北东南西北西南其中一个方位,命名常量 E, S, W, N, NW, NE, SW, SE
  • padx 和 pady 参数设置小构件四周边界距离单元格边界的距离,默认情况下填充为 0
  • ipadx 和 ipady 参数设置小构件内的文字距离小构件边界的距离,默认情况下填充为 0
  • 这两种选项情况下,小构件内的文字的大小不会发生变化,但是小构件和单元格会因边距太大时而自动调整
示例代码
from tkinter import *


class GridManagerDemo:
    window = Tk()
    window.title("Grid Manager Demo")
    message = Message(window, text="The message widget occupies three rows and two columns")
    message.grid(row=1, column=1, rowspan=3, columnspan=2)
    Label(window, text="First Name:").grid(row=1, column=3)
    Entry(window).grid(row=1, column=4,padx=4, pady=5)
    Label(window, text="Last Name:").grid(row=2, column=3)
    Entry(window).grid(row=2, column=4)
    Button(window, text="Get Name").grid(row=3, padx=5, pady=5, column=4, sticky=E)
    window.mainloop()


GridManagerDemo()

效果展示

包管理器

  • side 参数:在父容器内,设置小构件放置在下一个小构件上边或下边或左边或右边,命名常量 LEFT、RIGHT、TOP(默认)、BOTTOM
  • fill 参数:设置当前小构件垂直或水平占满它在父容器的所在位置,命名常量 X、Y、BOTH 填充水平、垂直、两个方向。使用 fill 参数后,小构件的大小会跟着窗口自动变大或者变小
  • expand 参数:会自动将整个父容器多出的空间(如果有的话),自动平均分配给所有 expand 值为非零的所有构件。默认情况下 expand 值为 0,意思就是小构件框本身是多大就占多大。
from tkinter import *


class PackManagerDemo:
    def __init__(self):
        window = Tk()
        window.geometry("600x400+0+0")
        window.title("Pack Manager Demo 1")
        Label(window, text="Blue", bg="blue").pack(side=LEFT)
        Label(window, text="Red", bg="red").pack(side=LEFT, fill=BOTH, expand=1)
        Label(window, text="Green", bg="green").pack(fill=Y)
        window.mainloop()
 
 
PackManagerDemo()

效果

位置管理器

位置管理器将小构件放在绝对位置上,绝对位置是指像素点
位置管理器不能兼容所有计算机,跟分辨率挂钩,所以要少用
from tkinter import *


class PlaceManagerDemo:
    def __init__(self):
        window = Tk()
        window.title("Place Manager Demo")
        Label(window, text="Blue", bg="blue").place(x=20, y=20)
        Label(window, text="Red", bg="red").place(x=50, y=50)
        Label(window, text="Green", bg="green").place(x=80, y=80)
        window.mainloop()


PlaceManagerDemo()

效果

参数 anchor 决定标签的某一方位点的坐标,默认 NW
import tkinter

win = tkinter.Tk()
label1 = tkinter.Label(win, text="AA1", bg="blue")
label2 = tkinter.Label(win, text="A12", bg="yellow")
label3 = tkinter.Label(win, text="123", bg="red")
label1.place(x=10, y=10, anchor=tkinter.NW)  # 等价于:label1.place(x=10, y=10)
label2.place(x=100, y=150, anchor=tkinter.E)  # 设置东所在的位置为x=100, y=150
label3.place(x=10, y=150, anchor=tkinter.SW)

win.mainloop()

效果

效果用 anchor 将标签放在父容器的特定方位
import tkinter

win = tkinter.Tk()
win.geometry("500x500+300+0")

label = tkinter.Label(win, text="hello python")
label.pack()

label2 = tkinter.Label(win,
            anchor=tkinter.E,  # 位置 n, ne, e, se, s, sw, w, nw, center
            bg="blue",
            fg="yellow",
            text="gogogo",
            width=20,
            height=10)
label2.pack()


win.mainloop()  # 进入消息循环

效果

4.显示图像

  • 创建图像时图像来源必须是 GIF 格式
  • 创建图像,photoName = PhotoImage(file = imagefilename)
  • 可以向标签、按钮、多选按钮、单选按钮添加图像
  • 添加的画像相当于原来 text 的作用。也就是可以用文字 text,也可以用图像 image
  • 还可以在画布里添加图像,使用 create_image(x = 90, y = 50, image = chinaImage)

示例代码

from tkinter import *


class ImageDemo:
    def __init__(self):
        window = Tk()
        window.title("Image Demo")
        
        caImage = PhotoImage(file="image/ca.gif")
        chinaImage = PhotoImage(file="image/china.gif")
        leftImage = PhotoImage(file="image/left.gif")
        rightImage = PhotoImage(file="image/right.gif")
        usImage = PhotoImage(file="image/usIcon.gif")
        ukImage = PhotoImage(file="image/ukIcon.gif")
        crossImage = PhotoImage(file="image/x.gif")
        circleImage = PhotoImage(file="image/o.gif")
        
        frame1 = Frame(window)
        frame1.pack()
        Label(frame1, image=caImage).pack(side=LEFT)
        canvas = Canvas(frame1)
        canvas.create_image(90, 50, image=chinaImage)
        canvas["width"] = 200
        canvas["height"] = 100
        canvas.pack(side=LEFT)
        
        frame2 = Frame(window)
        frame2.pack()
        Button(frame2, image=leftImage).pack(side=LEFT)
        Button(frame2, image=rightImage).pack(side=LEFT)
        Checkbutton(frame2, image=usImage).pack(side=LEFT)
        Checkbutton(frame2, image=ukImage).pack(side=LEFT)
        Radiobutton(frame2, image=crossImage).pack(side=LEFT)
        Radiobutton(frame2, image=circleImage).pack(side=LEFT)
        
        window.mainloop()


ImageDemo()

5.菜单

下拉菜单

小构件顺序:菜单栏 => 菜单 => 菜单条目
  • 创建菜单栏:menubar = Menu(window)
  • 放置菜单栏:window.config(menu = menubar)
  • 创建菜单:menuName = Menu(menubar, tearoff = 0)
  • 设置菜单名字:menubar.add_cascade(label = “菜单名字”, menu = menuName)
  • 给菜单添加条目:menuName.add_command(label = “条目名字”, command = self.xxxx)
from tkinter import *


class MenuDemo:
    def __init__(self):
        window = Tk()
        window.title("Menu Demo")

        # 创建菜单栏、菜单、菜单条目==================================
        menubar = Menu(window)
        window.config(menu=menubar)

        operationMenu = Menu(menubar, tearoff=0)
        # tearoff 被设置为 0,它表明菜单不能移出窗口
        # 如果没有设置这个选项,可以将菜单复制多个单独从窗口中移出来在电脑屏幕任何地方使用
        menubar.add_cascade(label="Operation", menu=operationMenu)

        operationMenu.add_command(label="Add", command=self.add)
        operationMenu.add_command(label="Subtract", command=self.subtract)

        operationMenu.add_separator()
        # 在不同的菜单栏条目之间画一根分割线
        operationMenu.add_command(label="Multiply", command=self.multiply)
        operationMenu.add_command(label="Divide", command=self.divide)

        exitmenu = Menu(menubar, tearoff=0)
        menubar.add_cascade(label="Exit", menu=exitmenu)
        exitmenu.add_command(label="Quit", command=window.quit)

        # 给 frame0 添加图像按钮=======================================
        frame0 = Frame(window)
        frame0.grid(row=1, column=1, sticky=W)
        # 创建图像
        plusImage = PhotoImage(file="image/plus.gif")
        minusImage = PhotoImage(file="image/minus.gif")
        timesImage = PhotoImage(file="image/times.gif")
        divideImage = PhotoImage(file="image/divide.gif")
        # 创建图像按钮
        Button(frame0, image=plusImage, command=self.add).grid(row=1, column=1, sticky=W)
        Button(frame0, image=minusImage, command=self.subtract).grid(row=1, column=2, sticky=W)
        Button(frame0, image=timesImage, command=self.multiply).grid(row=1, column=3, sticky=W)
        Button(frame0, image=divideImage, command=self.divide).grid(row=1, column=4, sticky=W)

        # 给 frame1 添加标签和文本输入框============================
        frame1 = Frame(window)
        frame1.grid(row=2,column=1, pady=10)
        Label(frame1, text="Number 1:").pack(side=LEFT)
        self.v1 = StringVar()
        Entry(frame1, width=20, textvariable=self.v1, justify=RIGHT).pack(side=LEFT)
        # width 设置的是文本输入框的宽度
        Label(frame1, text="Number 2:").pack(side=LEFT)
        self.v2 = StringVar()
        
        Entry(frame1, width=5, textvariable=self.v2, justify=RIGHT).pack(side=LEFT)
        Label(frame1, text="Number 3:").pack(side=LEFT)
        self.v3 = StringVar()
        Entry(frame1, width=5, textvariable=self.v3, justify=RIGHT).pack(side=LEFT)

        # 给 frame2 添加按钮 =====================================
        frame2 = Frame(window)
        frame2.grid(row=3, column=1, pady=10, sticky=E)
        Button(frame2, text="Add", command=self.add).pack(side=LEFT)
        Button(frame2, text="Subtract", command=self.subtract).pack(side=LEFT)
        Button(frame2, text="Multiply", command=self.multiply).pack(side=LEFT)
        Button(frame2, text="Divide", command=self.divide).pack(side=LEFT)

        mainloop()

    def add(self):
        self.v3.set(eval(self.v1.get()) + eval(self.v2.get()))

    def subtract(self):
        self.v3.set(eval(self.v1.get()) - eval(self.v2.get()))

    def multiply(self):
        self.v3.set(eval(self.v1.get()) * eval(self.v2.get()))

    def divide(self):
        self.v3.set(eval(self.v1.get()) / eval(self.v2.get()))


MenuDemo()

弹出菜单

弹出菜单,就是在窗口内鼠标右键点击可以出现的一个菜单
与一般的菜单相比,它没有菜单栏而且能浮现在屏幕任何一个地方
设置过程:
  • 创建一个 Menu 的实例
  • 向它添加条目
  • 将一个小构件和一个事件绑定以弹出菜单

示例代码

from tkinter import *


class PopupMenuDemo:
    def __init__(self):
        window = Tk()
        window.title("Popup Menu Demo")

        # 创建一个弹出菜单======
        self.menu = Menu(window, tearoff=0)
        self.menu.add_command(label="Draw a line", command=self.displayLine)
        self.menu.add_command(label="Draw a oval", command=self.displayOval)
        self.menu.add_command(label="Draw a rectangle", command=self.displayRect)
        self.menu.add_command(label="Clear", command=self.clearCanvas)

        # 在 window 中放置一个画布
        self.canvas=Canvas(window, width=200, height=100, bg="white")
        self.canvas.pack()

        # 将弹出菜单绑定到画布======
        self.canvas.bind("<Button-3>", self.popup)

        window.mainloop()

    def displayRect(self):
        self.canvas.create_rectangle(10, 10, 190, 90, tags="rect")

    def displayOval(self):
        self.canvas.create_oval(10, 10, 190, 90, tags="oval")

    def displayLine(self):
        self.canvas.create_line(10, 10, 190, 90, tags="line")
        self.canvas.create_line(10, 90, 190, 10, tags="line")

    def clearCanvas(self):
        self.canvas.delete("rect", "oval", "line")

    # 展示弹出菜单======
    def popup(self, event):
        self.menu.post(event.x_root, event.y_root)


PopupMenuDemo()

运行后

鼠标右键效果

6.鼠标、按键事件和绑定

使用小构件的 bind 方法将鼠标事件 event 和回调处理器 handler 绑定
语法,widget.bind(event, handler)
事件
事件属性
每一个处理器都将一个事件作为作为它的参数
def handler(event): # 注意有 event 这个关键字
    menu.post(event.x_root, event.y_root) # 调用事件属性进行某些操作

例子1:鼠标和键盘事件响应

示例代码
from tkinter import *


class MouseKeyEventDemo:
    def __init__(self):
        window = Tk()
        window.title("Mouse and Key Event Demo")
        canvas = Canvas(window, bg="white", width=200, height=100)
        canvas.pack(side=LEFT)
        canvas2 = Canvas(window, bg="red", width=200, height=100)
        canvas2.pack(side=LEFT)
        canvas.bind("<Button-1>", self.processMouseEvent)
        canvas2.bind("<Button-1>", self.processMouseEvent)

        canvas.bind("<Key>", self.processKeyEvent)
        canvas.focus_set()  # 在画布上获取焦点,以便于从键盘上获取输入,配合 key 事件
        window.mainloop()

    def processMouseEvent(self,event):
        print("-----Mouse Info-----")
        print("Clicked at", event.x_root, event.y_root)
        print("Position in the screen", event.x_root, event.y_root)
        print("Which button is clicked ?", event.num)

    def processKeyEvent(self,event):
        print("-----Key Info-----")
        print("keysym ?", event.keysym)
        print("char ?", event.char)
        print("keycode ?", event.keycode)


MouseKeyEventDemo()

运行效果

例子2:自动获取鼠标位置
import tkinter


def call(event):
    print(event.x, event.y)


win = tkinter.Tk()
frame = tkinter.Frame(win, width=200, height=200)
frame.bind("<Motion>", call)
frame.pack()
win.mainloop()

运行后,随着鼠标不断移动,会不断显示鼠标所在的位置

例子3:两圆相交

import math
from tkinter import *


class Circle2D:
    def __init__(self, x = 0, y = 0, radius = 0):
        self.x = x
        self.y = y
        self.radius = radius

    def getX(self):
        return self.x

    def getY(self):
        return self.y

    def getRadius(self):
        return self.radius

    def setX(self, x):
        self.x = x

    def setY(self, y):
        self.y = y

    def setRadius(self, radius):
        self.radius = radius

    def getPerimeter(self):
        return 2 * math.pi * self.radius

    def getArea(self):
        return math.pi * self.radius * self.radius

    def containsPoint(self, x, y):
        d = distance(x, y, self.x, self.y)
        return d <= self.radius

    def containsCircle(self, circle):
        d = distance(circle.x, circle.y, self.x, self.y)
        return d + circle.radius <= self.radius

    def overlapsCircle(self, circle):
        return distance(self.x, self.y, circle.x, circle.y) <= self.radius + circle.radius


# 两点之间距离的函数
def distance(x1, y1, x2, y2):
    d2 = (x1 - x2) ** 2 + (y1 - y2) ** 2
    return math.sqrt(d2)


# 画圆的函数
def displayCircle(c, text):
    canvas.delete(text)
    canvas.create_oval(c.x - c.radius, c.y - c.radius, c.x + c.radius, c.y + c.radius, tags=text)
    canvas.create_text(c.x, c.y, text=text, tags=text)


# 鼠标绑定事件
def mouseMoved(event):
    if c1.containsPoint(event.x, event.y):
        c1.setX(event.x)
        c1.setY(event.y)
        displayCircle(c1, "c1")

        if c1.overlapsCircle(c2):
            label["text"] = "两圆相交"
        else:
            label["text"] = "两圆不相交"
    elif c2.containsPoint(event.x, event.y):
        c2.setX(event.x)
        c2.setY(event.y)
        displayCircle(c2, "c2")
        if c1.overlapsCircle(c2):
            label["text"] = "两圆相交"
        else:
            label["text"] = "两圆不相交"


window = Tk()
window.title("两个圆之间的相互关系")

label = Label(window, text="两圆相交")
label.pack()

width = 300
height = 100
canvas = Canvas(window, width=width, height=height, bg="white")
canvas.pack()

canvas.bind("<B1-Motion>", mouseMoved)
c1 = Circle2D(50, 50, 30)
c2 = Circle2D(30, 40, 20)
displayCircle(c1, "c1")
displayCircle(c2, "c2")

window.mainloop()

效果

拖动图上的圆

7.动画

原理

通过显示一些移动的图画来创建动画
Canvas 类也可以被用来开发动画
可以在画布上显示图片和文本,并使用 move(tags, dx, dy)方法移动图片 ‘’
dx 为正,向右移动 dx 个像素,dx 为负向左移动 dx 个像素
dy 为正,向下移动 dy 个像素,dx 为负向上移动 dy 个像素
添加工具比如按钮来控制动画的速度,停止动画和重新开始动画等

示例1:滑动窗口

from tkinter import *


class ControlAnimation:
    def __init__(self):
        window = Tk()
        window.title("Control Animation Demo")

        self.width = 250
        self.canvas = Canvas(window, width=self.width, height=50, bg="white")
        self.canvas.pack()
        
        frame = Frame(window)
        frame.pack()

        btStop = Button(frame, text="Stop", command=self.stop)
        btStop.pack(side=LEFT)

        btResume = Button(frame, text="Resume", command=self.resume)
        btResume.pack(side=LEFT)

        btFaster = Button(frame, text="Faster", command=self.faster)
        btFaster.pack(side=LEFT)

        btSlower = Button(frame, text="Slower", command=self.slower)
        btSlower.pack(side=LEFT)

        self.x = 0  # 设置起点和要变化的方向
        self.sleepTime = 100  # 睡眠时间,用来控制速度
        self.canvas.create_text(self.x, 30, text="Message Moving", tags="text")

        self.dx = 3
        self.isStopped = False
        self.animate()  # 主函数,设置动画主要运动规则

        window.mainloop()

    def stop(self):
        self.isStopped = True

    def resume(self):
        self.isStopped = False
        self.animate()

    def faster(self):
        if self.sleepTime > 5:
            self.sleepTime -= 20
            print("当前的间隔时间是 ",self.sleepTime," 毫秒")

    def slower(self):
        if self.sleepTime > 5:
            self.sleepTime += 20
            print("当前的间隔时间是 ", self.sleepTime, " 毫秒")

    def animate(self):
        while not self.isStopped:
            self.canvas.move("text", self.dx, 0)
            self.canvas.after(self.sleepTime)
            self.canvas.update()
            if self.x < self.width:
                self.x += self.dx
            else:
                self.x = 0
                self.canvas.delete("text")
                self.canvas.create_text(self.x, 30, text="Message Moving", tags="text")


ControlAnimation()

示例2:四辆汽车比赛

from tkinter import *


class RaceCar(Canvas):
    def __init__(self, master, width, height):
        super().__init__(master, width=width, height=height)
        self["bg"] = "white"
        self.sleepTime = 100
        self.x = 10
        self.y = 50
        self.displayCar()

    def displayCar(self):
        self.delete("car")
        self.create_oval(self.x + 10, self.y - 10, self.x + 20, self.y, fill="black", tags="car")
        self.create_oval(self.x + 30, self.y - 10, self.x + 40, self.y, fill="black", tags="car")
        self.create_rectangle(self.x, self.y - 20, self.x + 50, self.y - 10, fill="green", tags="car")
        self.create_polygon(self.x + 10, self.y - 20,
                            self.x + 20, self.y - 30,
                            self.x + 30, self.y - 30,
                            self.x + 40, self.y - 20,
                            fill="red", tags="car")


def run():
    while True:
        for car in cars:
            if car.x < int(car["width"]):
                car.displayCar()
                car.x += 2
            else:
                car.x = 0

            car.after(car.sleepTime)
            car.update()


window = Tk()
window.title("比赛的汽车")

width = 250
height = 48

cars = []
for i in range(4):
    cars.append(RaceCar(window, width=width, height=height))
    cars[i].pack()

run()

window.mainloop()

示例3:转动的时钟

示例代码
import time
import math
from tkinter import *


class Clock(Canvas):
    def __init__(self, master, side):
        super().__init__(master, width=side, height=side)

        self.side = side
        self["bg"] = "white"

        # 画外圈圆
        self.drawSideCircle()

        # 画刻度
        self.drawCalibration()

        # 画针和下面的时间信息
        self.displayTime()

    def drawSideCircle(self):
        # 画外圈圆
        self.create_oval(self.side * 0.1, self.side * 0.1, self.side * 0.9, self.side * 0.9, tags="oval")

    def drawCalibration(self):
        # 画刻度
        r = self.side * 0.4
        d_angle = 360 / 60
        angle = 0
        hour = 3
        while angle < 360:
            linecolor = "black"
            if angle % (5 * d_angle) == 0:
                linecolor = "red"
            dx = r * math.cos(angle * math.pi / 180)
            dy = r * math.sin(angle * math.pi / 180)
            x1, y1 = self.side / 2 + dx, self.side / 2 - dy
            x2, y2 = self.side / 2 + dx * 0.9, self.side / 2 - dy * 0.9
            self.create_line(x1, y1, x2, y2, fill=linecolor, tags="line")
            if angle % (5 * d_angle) == 0:
                x3, y3 = self.side / 2 + dx * 0.8, self.side / 2 - dy * 0.8
                self.create_text(x3, y3, text=str(hour), tags="line")
                hour -= 1
                if hour == 0:
                    hour = 12
            angle += d_angle

    def displayTime(self):
        # 获取当前的小时、分钟、秒数
        timenow = time.localtime()
        current_hour = timenow.tm_hour
        current_minute = timenow.tm_min
        current_second = timenow.tm_sec

        if current_second >= 60:
            current_minute += 1
            current_second -= 60
        if current_minute == 60:
            current_minute = 0
            current_hour += 1
        if current_hour == 24:
            current_hour = 0

        # 画秒针
        self.delete("second")
        r = self.side * 0.4
        x0, y0 = self.side / 2, self.side / 2
        angle_second = 90 - 6 * current_second
        dx_second = r * math.cos(angle_second * math.pi / 180)
        dy_second = r * math.sin(angle_second * math.pi / 180)
        x4, y4 = self.side / 2 + dx_second * 0.7, self.side / 2 - dy_second * 0.7
        self.create_line(x0, y0, x4, y4, fill="red", arrow="last", tags="second")

        # 画分针
        self.delete("minute")
        r = self.side * 0.4
        x0, y0 = self.side / 2, self.side / 2
        angle_minute = 90 - 6 * current_minute
        dx_minute = r * math.cos(angle_minute * math.pi / 180)
        dy_minute = r * math.sin(angle_minute * math.pi / 180)
        x5, y5 = self.side / 2 + dx_minute * 0.5, self.side / 2 - dy_minute * 0.5
        self.create_line(x0, y0, x5, y5, fill="blue", arrow="last", tags="minute")

        # 画时针
        self.delete("hour")
        r = self.side * 0.4
        x0, y0 = self.side / 2, self.side / 2
        angle_hour = 90 - 30 * current_hour
        dx_hour = r * math.cos(angle_hour * math.pi / 180)
        dy_hour = r * math.sin(angle_hour * math.pi / 180)
        x6, y6 = self.side / 2 + dx_hour * 0.3, self.side / 2 - dy_hour * 0.3
        self.create_line(x0, y0, x6, y6, fill="black", arrow="last", tags="line")

        # 在最下面显示时间
        self.delete("timedown")
        second_down = str(0) + str(current_second) if current_second < 10 else str(current_second)
        time_down = str(current_hour) + ":" + str(current_minute) + ":" + second_down
        self.create_text(self.side / 2, self.side * 0.95, text = time_down, tags = "timedown")


def clockDynamicChange():
    while True:
        for clock in clocks:
            clock.displayTime()
            clock.after(1000)
            clock.update()


window = Tk()
window.title("四个时钟")

clocks = []
for i in range(4):
    clocks.append(Clock(window, 300))
    clocks[i].pack(side=LEFT)
clockDynamicChange()
window.mainloop()


运行效果

8.滚动条

滚动条类 Scrollbar
在水平方向 or 垂直方向展开 Text,Canvas,Listbox 小构件里的内容
示例代码
from tkinter import *


class ScrollText:
    def __init__(self):
        self.window = Tk()
        self.window.title("Scroll Text Demo")

        frame1 = Frame(self.window)
        frame1.pack()

        # 创建纵向滚动条
        scrollbarY = Scrollbar(frame1)
        scrollbarY.grid(row=1, column=2, sticky=NS)

        # 创建横向滚动条
        scrollbarX = Scrollbar(frame1, orient=HORIZONTAL, name="hbar")  # 默认 vertical
        scrollbarX.grid(row=2, column=1, rowspan=1, columnspan=2, sticky=EW)

        # 创建需要绑定的小构件,以及绑定滚动条
        # 若创建 Text 小构件时可加一个参数,wrap = WORD
        text = Listbox(frame1, width=10, height=10, yscrollcommand=scrollbarY.set, xscrollcommand=
        scrollbarX.set)

        # text = Listbox(frame1, width = 10, height = 10)
        # text.configure(yscrollcommand = scrollbarY.set, xscrollcommand = scrollbarX.set) ##滚动条关联
        # text['yscrollcommand'] = scrollbarY.set
        # text['xscrollcommand'] = scrollbarX.set

        text.grid(row=1, column=1)

        # 设置滚动条使得小构件移动的方式(垂直 or 左右)
        # 注意只有当垂直滚动条的长度小于窗口高度时,或者横向滚动条的长度小于窗口宽度时,才可滑动
        scrollbarY.config(command=text.yview)
        scrollbarX.config(command=text.xview)
        # scrollbarY['command'] = text.yview
        # scrollbarX['command'] = text.xview
        
        for i in range(100):
            # text.delete('1.0', tkinter.END) # 小构件为 Text 时可用,清除文本框里的所有内容
            text.insert(END, str(i) + " stringsssssssssssssssssss " + str(i))  # 文本自动插入内容显示

    def show(self):
        self.window.mainloop()


ScrollText = ScrollText()
ScrollText.show()

效果

9.选择或者输入对话框

用途:

  • 显示特定消息警告
  • 提示用户输入数字和字符串
import tkinter.messagebox
import tkinter.simpledialog
import tkinter.colorchooser

# 显示特定信息或警告
tkinter.messagebox.showinfo("对话框名字,它在对话框左上角", "对话框中间的内容")
tkinter.messagebox.showwarning("showwarning", "This is a warning")
tkinter.messagebox.showerror("showerror", "This is an error")

# 提示用户进行选择,并获取值
isYes = tkinter.messagebox.askyesno("askyesno", "Contiue ?")
print(isYes)  # True 或者 False
isOk = tkinter.messagebox.askokcancel("askokcancel", "OK ?")
print(isOk)   # True 或者 False
isYesNoCancel = tkinter.messagebox.askyesnocancel("askyesnocancel", "Yes, No, Cancel ?")
print(isYesNoCancel)  # True 或者 False 或者 None

# 提示用户进行输入,并获取值
name = tkinter.simpledialog.askstring("askstring", "Enter your name")
print(name)   # OK返回输入的内容,取消返回None,
age = tkinter.simpledialog.askinteger("askinteger", "Enter your age")
print(age)    # OK返回输入的内容,输入非整数会报错,会让其重新输入,取消返回None,
weight = tkinter.simpledialog.askfloat("askfloat", "Enter your weight")
print(weight)

10.作业练习

简单的文本编辑器

from tkinter import *
from tkinter.filedialog import askopenfilename
from tkinter.filedialog import asksaveasfilename


class FileEditor:
    def __init__(self):
        window = Tk()
        window.title("简单的文本编辑器")

        # 创建一个菜单栏
        menubar = Menu(window)
        window.config(menu=menubar)

        # 创建一个下拉菜单栏,并将它添加到这个菜单栏
        operationMenu = Menu(menubar, tearoff=0)
        menubar.add_cascade(label="File", menu=operationMenu)

        # 给这个下拉菜单添加元素
        operationMenu.add_command(label="open", command=self.openFile)
        operationMenu.add_command(label="save", command=self.saveFile)

        # 添加第一个工具栏框架
        frame0 = Frame(window)
        frame0.grid(row=1, column=1, sticky=W)

        # 创建图像
        openImage = PhotoImage(file="image/open.gif")
        saveImage = PhotoImage(file="image/save.gif")
        Button(frame0, image=openImage, command=self.openFile).grid(row=1, column=1, sticky=W)
        Button(frame0, image=saveImage, command=self.saveFile).grid(row=1, column=2)

        # 添加第二个工具栏框架:
        frame1 = Frame(window)
        frame1.grid(row=2, column = 1)

        # 在右方创建一个滚动条
        scrollbar = Scrollbar(frame1)
        scrollbar.pack(side=RIGHT, fill=Y)
        self.text = Text(frame1, width=40, height=20, wrap=WORD, yscrollcommand=scrollbar.set)
        self.text.pack()
        scrollbar.config(command=self.text.yview)

        window.mainloop()

    def openFile(self):
        filenameforReading = askopenfilename()
        infile = open(filenameforReading, "r")
        self.text.insert(END, infile.read())
        infile.close()

    def saveFile(self):
        filenameforWriting = asksaveasfilename()
        outfile = open(filenameforWriting, "w")
        outfile.write(self.text.get(1.0, END))
        outfile.close()

FileEditor()

贷款计算器

from tkinter import *


class LoanCalculator:
    def __init__(self):
        window = Tk()
        window.title("贷款计算器")

        Label(window, text="Annual Interest Rate").grid(row=1, column=1, sticky=W)
        Label(window, text="Number of Years").grid(row=2, column=1, sticky=W)
        Label(window, text="Loan Amount").grid(row=3, column=1, sticky=W)
        Label(window, text="Monthly Paymant").grid(row=4, column=1, sticky=W)
        Label(window, text="Total Payment").grid(row=5, column=1, sticky=W)

        self.annualInterestRateVar = StringVar()
        Entry(window, textvariable=self.annualInterestRateVar, justify=RIGHT).grid(row=1, column=2)

        self.numberOfYearVar = StringVar()
        Entry(window, textvariable=self.numberOfYearVar, justify=RIGHT).grid(row=2, column=2)

        self.loanAmountVar = StringVar()
        Entry(window, textvariable=self.loanAmountVar, justify=RIGHT).grid(row=3, column=2)

        #########################
        self.monthlyPaymentVar = StringVar()
        lblMonthlyPayment = Label(window, textvariable=self.monthlyPaymentVar, justify=RIGHT).grid(row=4, column=2, sticky=E)
        self.totalPaymentVar = StringVar()
        lblTotalPayment = Label(window, textvariable=self.totalPaymentVar, justify=RIGHT).grid(row=5, column=2, sticky=E)
        btComputePayment = Button(window, text="Compute Payment", command=self.computePayment).grid(row=6, column=2,sticky=E)

        window.mainloop()

    def computePayment(self):
        monthlyPayment = self.getMonthlyPayment(
                                                float(self.loanAmountVar.get()),
                                                float(self.annualInterestRateVar.get()) / 1200,
                                                int(self.numberOfYearVar.get()))
        # 用 set 方法改变命名变量的值
        self.monthlyPaymentVar.set(format(monthlyPayment, "10.2f"))

        # 用 get 方法获取命名变量的值
        totalPayment = float(self.monthlyPaymentVar.get()) * 12 * int(self.numberOfYearVar.get())
        self.totalPaymentVar.set(format(totalPayment, "10.2f"))

    def getMonthlyPayment(self,
                                loanAmount,
                                monthlyInterestRate,
                                numberOfYears):
        monthlyPayment = loanAmount * monthlyInterestRate / (1 - 1 / ( 1 + monthlyInterestRate) ** (numberOfYears * 12))

        return monthlyPayment

LoanCalculator()

效果

查找四个连续相同的数字

示例代码
from tkinter import *
from random import randint


class FourSame:
    def __init__(self):
        window = Tk()
        window.title("四个连续相同的数字")
        window.geometry("600x400+0+0")

        frame = Frame(window)
        frame.pack()

        self.entryDict = {}  # 用来存储 6*7=42 个输入框对象
        self.varDict = {}  # 用来存储 42 个变量

        for i in range(1, 7):
            for j in range(1, 8):
                self.varDict[(i, j)] = StringVar()
                print(self.varDict[(i, j)])
                entry_tmp = Entry(frame, text=1, width=5, textvariable = self.varDict[(i, j)])
                entry_tmp.grid(row=i, column=j)
                self.entryDict[(i, j)] = entry_tmp
                print(self.entryDict[(i, j)])
                print('------')

        solveButton = Button(window, text = "查找", command = self.solve)
        solveButton.pack()

        self.initial()

        window.mainloop()


    def solve(self):
        for i in range(1, 7):
            for j in range(1, 8):
                # 判断横向
                if j + 3 <= 7:
                    if self.varDict[(i, j)].get() == self.varDict[(i, j + 1)].get() == \
                            self.varDict[(i, j + 2)].get() == self.varDict[(i, j + 3)].get():
                        self.entryDict[(i, j)]["bg"] = "red"
                        self.entryDict[(i, j + 1)]["bg"] = "red"
                        self.entryDict[(i, j + 2)]["bg"] = "red"
                        self.entryDict[(i, j + 3)]["bg"] = "red"

                # 判断纵向
                if i + 3 <= 6:
                    if self.varDict[(i, j)].get() == self.varDict[(i + 1, j)].get() == \
                            self.varDict[(i + 2, j)].get() == self.varDict[(i + 3, j)].get():
                        self.entryDict[(i, j)]["bg"] = "yellow"
                        self.entryDict[(i + 1, j)]["bg"] = "yellow"
                        self.entryDict[(i + 2, j)]["bg"] = "yellow"
                        self.entryDict[(i + 3, j)]["bg"] = "yellow"

                # 判断主对角线
                if i + 3 <= 6 and j + 3 <= 7:
                    if self.varDict[(i, j)].get() == self.varDict[(i + 1, j + 1)].get() == \
                            self.varDict[(i + 2, j + 2)].get() == self.varDict[(i + 3, j + 3)].get():
                        self.entryDict[(i, j)]["bg"] = "green"
                        self.entryDict[(i + 1, j + 1)]["bg"] = "green"
                        self.entryDict[(i + 2, j + 2)]["bg"] = "green"
                        self.entryDict[(i + 3, j + 3)]["bg"] = "green"

                # 判断次对角线
                if i + 3 <= 6 and j - 3 >= 1:
                    if self.varDict[(i, j)].get() == self.varDict[(i + 1, j - 1)].get() == \
                            self.varDict[(i + 2, j - 2)].get() == self.varDict[(i + 3, j - 3)].get():
                        self.entryDict[(i, j)]["bg"] = "blue"
                        self.entryDict[(i + 1, j - 1)]["bg"] = "blue"
                        self.entryDict[(i + 2, j - 2)]["bg"] = "blue"
                        self.entryDict[(i + 3, j - 3)]["bg"] = "blue"

    def initial(self):
        for i in range(1, 7):
            for j in range(1, 8):
                self.varDict[(i, j)].set(randint(1, 3))


FourSame()


效果

井字游戏

from tkinter import *

window = Tk()
window.title("井字游戏")
window.geometry("600x400+0+0")

frame1 = Frame(window)
frame1.pack()

# 创建三种图像
emptyImage = PhotoImage(file=r"./image/empty.gif")
xImage = PhotoImage(file=r"./image/x.gif")
oImage = PhotoImage(file=r"./image/o.gif")

# 创建九个标签
labelDict = {} # 用来存储九个标签对象
varDict = {} # 用来存储标签的变量
for i in range(1, 4):
    for j in range(1, 4):
        var_tmp = StringVar()
        label_tmp = Label(frame1, image = emptyImage, textvariable = var_tmp)
        label_tmp.grid(row = i, column = j)
        labelDict[(i, j)] = label_tmp
        varDict[(i, j)] = var_tmp
        print(labelDict[(i, j)])
        print(varDict[(i, j)].get() == "")

# 展示进程和结果的标签
labelResult = Label(window, text = "点击方框开始游戏")
labelResult.pack()

# 统计 x 图像和 o 图像的个数,以此来判断下一个图像为 x 还是 o
def countXO():
    print("此函数被调用")
    n1 = 0 # x 图像
    n2 = 0 # o 图像
    for i in range(1, 4):
        for j in range(1, 4):
            if varDict[(i, j)].get() == "x":
                n1 += 1
            elif varDict[(i, j)].get() == "o":
                n2 += 1
    return n1, n2


# 判断是否出现三同
def getThree():
    for i in range(1, 4):
        for j in range(1, 4):
            # 横向
            if j + 2 <= 3:
                if varDict[(i, j)].get() == varDict[(i, j + 1)].get() == varDict[(i, j + 2)].get():
                    if varDict[(i, j)].get() != "":
                        labelResult["text"] = str(varDict[(i, j)].get()) + "获胜"
            # 纵向
            if i + 2 <= 3:
                if varDict[(i, j)].get() == varDict[(i + 1, j)].get() == varDict[(i + 2, j)].get():
                    if varDict[(i, j)].get() != "":
                        labelResult["text"] = str(varDict[(i, j)].get()) + "获胜"
            # 主对角线
            if i + 2 <= 3 and j + 2 <= 3:
                if varDict[(i, j)].get() == varDict[(i + 1, j + 1)].get() == varDict[(i + 2, j + 2)].get():
                    if varDict[(i, j)].get() != "":
                        labelResult["text"] = str(varDict[(i, j)].get()) + "获胜"
            # 次对角线
            if i + 2 <= 3 and j - 2 >= 1:
                if varDict[(i, j)].get() == varDict[(i + 1, j - 1)].get() == varDict[(i + 2, j - 2)].get():
                    if varDict[(i, j)].get() != "":
                        labelResult["text"] = str(varDict[(i, j)].get()) + "获胜"


def getResult(n1, n2):
    if n1 + n2 < 9:
        getThree()
        if labelResult["text"].find("获胜") == -1:
            labelResult["text"] = "继续游戏"
    elif n1 + n2 == 9:
        getThree()
        if labelResult["text"].find("获胜") == -1:
            labelResult["text"] = "平局"


def changeLabelImage(event):
    print(event.widget["image"])
    n1, n2 = countXO()
    for key in labelDict:
        if labelDict[key] == event.widget:
            if n1 == n2 and varDict[key].get() == "":
                event.widget["image"] = xImage
                varDict[key].set("x")
                n1 += 1
            elif n1 - n2 == 1 and varDict[key].get() == "":
                event.widget["image"] = oImage
                varDict[key].set("o")
                n2 += 1
            break

    getResult(n1, n2)  # 调用胜负结果的函数


# 将所有标签与鼠标左键绑定
for i in range(1, 4):
    for j in range(1, 4):
        labelDict[(i, j)].bind("<Button-1>", changeLabelImage)
window.mainloop()

拖动小构件

from tkinter import *


# 定义距离函数
def distance(x1, y1, x2, y2):
    return ((x1 - x2) ** 2 + (y1 - y2) ** 2) ** 0.5


# 定义何为邻近的函数
def isNearby(x1, y1, x2, y2):
    radius = 10
    return distance(x1, y1, x2, y2) < radius + 10


class twoCircle:
    def __init__(self):
        self.x1, self.y1 = 20, 20
        self.x2, self.y2 = 120, 50
        self.radius = 20

        window = Tk()
        window.title("两个可移动的圆")

        self.canvas = Canvas(window, width=800, height=800, bg="white")
        self.canvas.pack()


        self.drawNew()  # 画初始圆和线段

        self.canvas.bind("<B1-Motion>", self.mousemoved)

        window.mainloop()

    def mousemoved(self, event):
        if isNearby(self.x1, self.y1, event.x, event.y):
            self.canvas["cursor"] = "plus"
            self.x1 = event.x
            self.y1 = event.y
            self.drawNew()
        elif isNearby(self.x2, self.y2, event.x, event.y):
            self.canvas["cursor"] = "plus"
            self.x2 = event.x
            self.y2 = event.y
            self.drawNew()
        else:
            self.canvas["cursor"] = "arrow"

    def drawNew(self):
        self.canvas.delete("twoCircle")

        self.canvas.create_oval(self.x1 - self.radius, self.y1 - self.radius,
                             self.x1 + self.radius, self.y1 + self.radius,
                             fill="black", tags="twoCircle")
        self.canvas.create_oval(self.x2 - self.radius, self.y2 - self.radius,
                             self.x2 + self.radius, self.y2 + self.radius,
                             fill="black", tags="twoCircle")
        self.canvas.create_line(self.x1, self.y1, self.x2, self.y2, tags="twoCircle")
        self.d = format(distance(self.x1, self.y1, self.x2, self.y2), ".2f")
        self.canvas.create_text((self.x1 + self.x2) / 2, (self.y1 + self.y2) / 2, text=str(self.d), tags="twoCircle")


twoCircle()


效果

显示 n 个颜色随机的球




from tkinter import *
from random import randint

width = 600  # 画布宽度
height = 600  # 画布高度
radius = 4  # 小球半径
number = 20  # 小球个数


# 将数转换为 16 进制的函数
def toHexChar(hexValue):
    if 0 <= hexValue <= 9:
        return str(hexValue)
    elif 10 <= hexValue <= 15:
        return chr(hexValue - 10 + ord('A'))


# 生成随机颜色的函数
def randomColor():
    color = "#"
    for j in range(6):
        color += toHexChar(randint(0, 15))
    return color


class RandomColorCircle:
    def __init__(self):
        window = Tk()
        window.title("颜色随机的球")

        self.canvas = Canvas(window, width=width, height=height, bg="white")
        self.canvas.pack()

        textname = "显示" + str(number) + "个颜色随机的球"
        btDisplay = Button(window, text=textname, command=self.display)
        btDisplay.pack()

        window.mainloop()

    def display(self):
        self.canvas.delete("oval")

        for i in range(number):
            fillColor = randomColor()
            x = randint(radius, width - radius)
            y = randint(radius, height - radius)
            self.canvas.create_oval(x - radius, y - radius,
                                    x + radius, y + radius,
                                fill=fillColor, tags="oval")


RandomColorCircle()

效果

n 个随机运动的球

from tkinter import *
from random import randint


width = 600  # 画布宽度
height = 600  # 画布高度


# 返回随机颜色的函数
def getRandomColor():
    color = "#"
    for i in range(6):
        color += toHexChar(randint(0,15))
    return color


# 单个数转换为 16 进制的函数
def toHexChar(hexvalue):
    if 0 <= hexvalue <= 9:
        return str(hexvalue)
    elif 10 <= hexvalue <= 15:
        return chr(hexvalue - 10 + ord('A'))


# 定义一个球类
class Ball:
    def __init__(self):
        self.x = randint(0, width)  # 圆心
        self.y = randint(0, height)
        self.dx = randint(2, 10)  # 运动幅度
        self.dy = randint(2, 10)
        self.radius = randint(3, 5)  # 半径
        self.color = getRandomColor()  # 颜色


class BounceBalls:
    def __init__(self):
        self.ballList = []

        window = Tk()
        window.geometry("600x400+0+0")
        window.title("Bouncing Balls")

        self.width = 400
        self.height = 300
        self.canvas = Canvas(window, width = self.width, height = self.height, bg = "white")
        self.canvas.pack()

        frame = Frame(window)
        frame.pack()

        Button(frame, text="Stop", command=self.stop).pack(side=LEFT)
        Button(frame, text="Resume", command=self.resume).pack(side=LEFT)
        Button(frame, text=" + ", command=self.add).pack(side=LEFT)
        Button(frame, text=" - ", command=self.remove).pack(side=LEFT)

        self.sleepTime = 100  # 100 毫秒
        self.isStopped = False
        self.animate()  # 开始动画

        window.mainloop()

    def stop(self):
        self.isStopped = True

    def resume(self):
        self.isStopped = False
        self.animate()

    def add(self):
        self.ballList.append(Ball())

    def remove(self):
        self.ballList.pop()

    def animate(self):
        while not self.isStopped:
            self.canvas.after(self.sleepTime)
            self.canvas.update()
            self.canvas.delete("ball")

            for ball in self.ballList:
                self.redisplayBall(ball)

    def redisplayBall(self, ball):
        if ball.x > self.width or ball.x < 0:
            ball.dx = - ball.dx  # 超出边界后,反向移动

        if ball.y > self.height or ball.y < 0:
            ball.dy = - ball.dy
            ball.x += ball.dx

        ball.y += ball.dy
        self.canvas.create_oval(ball.x - ball.radius, ball.y - ball.radius,
                                ball.x + ball.radius, ball.y + ball.radius,
                                fill = ball.color, tags = "ball")


BounceBalls()

效果

画函数图像

示例代码

import math
from tkinter import *

# 定义一个函数
def f(x):
    return -50 * math.sin((x / 100.0) * 2 * math.pi)

# 主程序
class DrawFunc:
    def __init__(self):
        window = Tk()
        window.geometry("900x600+0+0")
        window.title("函数图像")

        width = 600
        height = 400
        canvas = Canvas(window, width=width, height=height)
        canvas.pack()

        # 生成很多点
        p = []
        xside = int(width * 0.4)
        for x in range(- xside, xside + 1):
            p.append([x + width / 2, f(x) + height / 2])

        # 将点连线
        for i in range(len(p) - 1):
            canvas.create_line(p[i], p[i + 1])

        # 画 X 轴
        canvas.create_line(10, height / 2, width - 10, height / 2)
        canvas.create_text(width / 2 + 100, height / 2 + 10, text="2\u03c0")
        canvas.create_text(width / 2 - 100, height / 2 + 10, text="-2\u03c0")
        canvas.create_line(width - 10 - 10, height / 2 + 10, width - 10, height / 2)
        canvas.create_line(width - 10 - 10, height / 2 - 10, width - 10, height / 2)

        # 画 Y 轴
        canvas.create_line(width / 2, 10, width / 2, height - 10)
        canvas.create_line(width / 2, 10, width / 2 - 10, 10 + 10)
        canvas.create_line(width / 2, 10, width / 2 + 10, 10 + 10)
        window.mainloop()


DrawFunc()


效果


end

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/648508.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

月薪5万是怎样谈的?

知识星球&#xff08;星球名&#xff1a;芯片制造与封测技术社区&#xff0c;星球号&#xff1a;63559049&#xff09;里的学员问&#xff1a;目前是晶圆厂的PE&#xff0c;但是想跳槽谈了几次薪水&#xff0c;都没法有大幅度的增长&#xff0c;该怎么办&#xff1f;“学得文武…

JavaWeb 请求响应路径调试

在使用mvc时&#xff0c;或许会遇到请求的页面响应不了&#xff0c;这种情况要对站下径。 站点根目录 启动服务器时&#xff0c;通常要知道哪个是站点根目录。相应在网页端的url的跟站点通常为http://localhost:8080/ &#xff0c;前端解析时用的是站点根目录。 <form act…

RT-Thread更改msh串口波特率

修改rt-thread文件下components下dirvers下serial.h文件里 #define RT_SERIAL_CONFIG_DEFAULT 里的默认波特率即可

这方法真牛B!论文降重从81%直降1.9%

目录 一、万字论文&#xff0c;从0到1&#xff0c;只需1小时二、获取途径三、论文从81&#xff05;降到1.9&#xff05;四、内容是别人的&#xff0c;话是自己的五、AI工具 --> 中文论文降重六、论文降重小技巧 一、万字论文&#xff0c;从0到1&#xff0c;只需1小时 通过O…

ROS2入门21讲__第20讲__RQT:模块化可视化工具

目录 前言 rqt介绍 日志显示 图像显示 发布话题数据/调用服务请求 绘制数据曲线 数据包管理 节点可视化 前言 ROS中的Rviz功能已经很强大了&#xff0c;不过有些场景下&#xff0c;我们可能更需要一些简单的模块化的可视化工具&#xff0c;比如只显示一个摄像头的图像…

INTERCONNECT模块中的 Circuit Layout Editor

INTERCONNECT模块中的 Circuit Layout Editor 正文 正文 打开 INTERCONNECT 模块后的工作界面如下&#xff1a; 我们可以通过 View->Windows 选取我们需要的工具窗口。 当然&#xff0c;用户也可以自己手动重新规划各个窗口的位置&#xff0c;但是此处&#xff0c;我们保…

反射获取方法的参数类型和参数名

如何获取方法的参数类型和参数名 示例&#xff0c;要获取的方法 获取参数类型和名称 Testpublic void testGetParamsName() throws Exception {LocalVariableTableParameterNameDiscoverer parameterNameDiscoverer new LocalVariableTableParameterNameDiscoverer();Method[…

抖音IP地址频繁变动:背后的原因与解读

在抖音这个短视频平台的日常使用中&#xff0c;不少用户可能注意到了自己的IP地址有时会频繁变动。这种现象不仅引起了用户的好奇&#xff0c;也引发了关于个人隐私、账号安全以及平台政策的一系列讨论。那么&#xff0c;抖音IP地址换来换去什么意思&#xff1f;这背后又隐藏着…

langchain进阶一:特殊的chain,轻松实现对话,与数据库操作,抽取数据,以及基于本地知识库的问答

特殊的chain langchain中的Chain有很多,能够轻松实现部分需求,极致简化代码,但是实现效果与模型智慧程度有关 会话链 效果与LLMChain大致相同 javascript 复制代码 from langchain.chains import ConversationChain from langchain_community.llms import OpenAI conversat…

从零构建vue3+ts+vite项目打包及项目依赖配置

❗️❗️❗️❗️ 写在最前: 本文是根据B站作者 月光分层 视频vuets 工程化配置以及作者笔记稍作整理 &#x1f496;&#x1f496;作者B站地址https://space.bilibili.com/14110850 &#x1f496;&#x1f496;视频教程地址vuets 工程化配置 &#x1f496;&#x1f496;作者微信…

Nacos 2.x 系列【10】配置管理

文章目录 1. 概述2. 配置管理2.1 CRUD2.2 版本管理2.3 灰度管理2.4 监听管理2.5 推送轨迹2.6 示例代码2.6 聚合数据 1. 概述 在Nacos的架构图中&#xff0c;配置管理包含了配置CRUD、版本管理、灰度管理、监听管理、推送轨迹、聚合数据等功能。 在上篇文档中&#xff0c;我们…

shell脚本编译成二进制文件shc

文章目录 1. 安装shc2. 使用shc编译Shell脚本3. 执行二进制文件4. 编译后执行效率 将Shell脚本转换为二进制执行文件&#xff0c;可以使用 shc工具。 shc是一个Shell编译器&#xff0c;它可以将Shell脚本编译成二进制文件。以下是详细步骤&#xff1a; 1. 安装shc 在大多数L…

[Algorihm][简单多状态DP问题][买卖股票的最佳时机含冷冻期][买卖股票的最佳时机含手续费]详细讲解

目录 1.买卖股票的最佳时机含冷冻期1.题目链接买卖股票的最佳时机含冷冻期2.算法原理详解3.代码实现 2.买卖股票的最佳时机含手续费1.题目链接2.算法原理详解3.代码实现 1.买卖股票的最佳时机含冷冻期 1.题目链接 买卖股票的最佳时机含冷冻期 2.算法原理详解 思路&#xff…

【Python】 跨平台获取用户主目录的Python方法

基本原理 在编程中&#xff0c;获取用户的主目录是一个常见的需求。不同的操作系统&#xff08;如Windows、macOS和Linux&#xff09;有不同的路径表示方法。例如&#xff0c;在Windows上&#xff0c;用户的主目录通常在C:\Users\用户名&#xff0c;而在Linux和macOS上&#x…

实现顺序表各种基本运算的算法

实验一&#xff1a;实现顺序表各种基本运算的算法 一、实验目的与要求 目的: 领会顺序表存储结构和掌握顺序表中各种基本运算算法设计。 内容: 编写一个程序sqlist.cpp,实现顺序表的各种基本运算和整体建表算法(假设顺序表的元素类型ElemType为char),并在此基础上设计一个…

RocketMq源码解析四:生产者Producer启动

一、主要接口和类 生产者服务核心接口和类的关系如下图所示&#xff1a; MQProducer是生产者解耦&#xff0c;这里找几个有代表性的方法 // 同步发送消息 SendResult send(final Message msg) throws MQClientException, RemotingException, MQBrokerException,InterruptedExce…

qt 布局学习笔记

目录 qt下载地址&#xff1a; widget 宽高 管理信息列表源码 c版&#xff1a; pro文件&#xff1a; qt 设置水平布局&#xff0c;里面有两个按钮&#xff0c;每个按钮就变的很宽&#xff0c;怎么设置按钮的精确位置 设置固定大小&#xff1a; 使用弹性空间&#xff08;…

【网络安全】勒索软件ShrinkLocker使用 windows系统安全工具BitLocker实施攻击

文章目录 威胁无不不在BitLocker 概述如何利用BitLocker进行攻击如何降低影响Win11 24H2 装机默认开启 BitLocker推荐阅读 威胁无不不在 网络攻击的形式不断发展&#xff0c;即便是合法的 Windows 安全功能也会成为黑客的攻击工具。 卡巴斯基实验室专家 发现 使用BitLocker的…

C++质数的那些事(判断指数、区间筛质数、互质等等)

质数的定义&#xff1a;若一个正整数除了1和它自身之外不能被任何自然数整除&#xff0c;则该数称为质数&#xff0c;也叫素数。否则为合数。 质数的性质&#xff1a;质数的分布较为稀疏&#xff0c;对于一个足够大的数S&#xff0c;不超过S的质数大约有个&#xff0c;也就是说…

渗透测试的测试流程与注意事项

软件测试流程 渗透测试是一种重要的软件测试技术&#xff0c;通过对系统进行模拟攻击和漏洞评估&#xff0c;帮助组织发现和修复潜在的安全风险&#xff0c;提高系统的安全性和稳定性。在进行渗透测试时&#xff0c;需要注意合法授权、技术能力、安全意识和报告质量等方面的问…