为了520能够博得女友欢心,我设计了一个用来表白的程序,主要用到了tkinter的画布和各种鼠标事件,今天就来分享给大家。(借鉴要谨慎,可不要只用它来糊弄女盆友哦)
程序的设计是用tkinter模拟手机操作,实现手机桌面的展示、滑动和下拉,然后点击指定位置,可以弹出新的内容。其原理是用canvas显示图片,所谓的滑动和点击等等,其实就是图片的更换;下拉则是利用鼠标事件来移动图片,实现和手机下拉相同的效果(不到半屏自动回位,超过半屏自动落下,落下后拖动底端回位)。
![v2-577a6c6a61a546c1da9337f8cc299339_b.jpg](http://img-01.proxy.5ce.com/view/image?&type=2&guid=9985a0a5-ca2e-eb11-8da9-e4434bdf6706&url=https://pic2.zhimg.com/v2-577a6c6a61a546c1da9337f8cc299339_b.jpg)
下拉的效果:
![v2-d685947fb517a69e405413b7c3cfd217_b.jpg](http://img-03.proxy.5ce.com/view/image?&type=2&guid=9985a0a5-ca2e-eb11-8da9-e4434bdf6706&url=https://pic4.zhimg.com/v2-d685947fb517a69e405413b7c3cfd217_b.jpg)
部分效果如上所示,点击信息、拨号和相册,可以分别弹出新的图片(把表白的内容放在这里);点击home键则是返回(home键是一个显示了图片的button按钮);桌面还可以滑动,滑动后切换成另一张背景图(我本人),然后对应绑定了不同的鼠标事件,点击后会显示与之前不同的图片。(所有图片都是我用自己的手机截图的)
程序逻辑里,最重要的部分就是几个模式的区分,我用flag来定义,具体如下:flag=0是初始状态、点击屏幕其他位置和下拉未到半屏回位后的状态;flag=1是点击屏幕顶端下拉;flag=2是下拉超过半屏后自动落下,和此时再点击其他位置;flag=3是落下后点击屏幕底端回位;flag=5是切换屏幕。
程序中创建了两个canvas,一个用来显示外壳(固定不动),另一个用来显示屏幕(触发各种事件)。在这两个画布中,获取鼠标位置event.x和event.y是分开计的,所以我们只需要用到显示屏幕的画布中的位置。根据信息、拨号和相册的位置,在点击时设置对应的flag值,触发各自的事件(其实就是显示新的图片)。
另外,所有屏幕图片的大小都是400*700,所以桌面的初始位置在(200,350)附近,而下拉图片的初始位置是(200,-350),此时是在画布外的,不会显示出来。触发下拉事件时,它就会跟随鼠标移动。(这也是创建两个canvas的原因,如果只用一个,由于屏幕不是在画布的顶端,因此下拉图片不会被隐藏起来;而如果它属于其中一个画布,但初始位置在画布外,那么就会隐藏)
下面是完整代码和简单注释:
from tkinter import *
import tkinter as tk
from PIL import Image, ImageTk
import time
import cv2
flag=0 #状态标志
root = Tk()
root.title("手机")
root.geometry("450x840+500+0")
root.overrideredirect(True)
canvas2 = Canvas(root,width=450, height=840,bg='white',highlightthickness=0)
canvas = Canvas(root,width=400, height=700,bg='black',highlightthickness=0)
f=0 #记录之前的flag,当前事件结束后返回前一个状态
canvas2.place(x=0,y=0) #手机壳
canvas.place(x=24,y=80) #屏幕
#左键点击home键,清除刚才显示的图片(即返回桌面),同时开始计时
def onLeftButton(event):
global start
start=time.time()
canvas.delete('c')
#鼠标左键抬起home键,结束计时,超过3秒则退出程序(关机),否则退回之前的flag
def ButtonUp(event):
global end,start,flag
end=time.time()
flag=f
if end-start>=3:
root.destroy()
#获得鼠标位置,并设置相应的flag
def callback(event):
global x,y,flag
x = event.x
y = event.y
if flag!=2 and 0<y<10:
flag=1
if flag==2 and y>680:
flag=3
#双击“信息”、“拨号”或“相册”的位置,显示新的图片
#两个桌面对应不同的图片,用flag来区分
def DoubleButton(event):
if flag==5:
if 30<x<90 and 30<y<90:
canvas.create_image(200,350,image=var7,tags="c")
elif 120<x<180 and 600<y<660:
canvas.create_image(200,350,image=var8,tags="c")
else:
if 30<x<90 and 30<y<90:
canvas.create_image(200,350,image=var3,tags="c")
elif 120<x<180 and 600<y<660:
canvas.create_image(200,350,image=var4,tags="c")
elif 220<x<270 and 600<y<660:
canvas.create_image(200,350,image=var5,tags="c")
#按住左键并拖动鼠标,显示下拉图片
def onLeftButtonMove(event):
global a,flag,f
if flag==2:
return
if flag==0:
f=5-flag #这里要么是0,要么是5
return
if flag==5:
f=5-flag
return
#下拉栏落下后,拖动底端回位
if flag==3:
if event.y<=702:
canvas.delete('a')
canvas.create_image(200,event.y-350,image=var1,tags="a")
return
#跟随鼠标下拉
if flag==1 and event.y<=702:
canvas.delete('a')
canvas.create_image(200,event.y-350,image=var1,tags="a")
return
#释放左键,触发下拉的动态效果(自动下落或回位)以及两个桌面的切换
def onLeftButtonUp(event):
global a,flag
if flag==0:
if abs(event.x-x)>10:
canvas.create_image((200,350), image=var6,tags='b')
flag=5
return
else:
return
if flag==5:
if abs(event.x-x)>10:
canvas.create_image((200,350), image=var2,tags='b')
flag=0
return
else:
return
if flag==2:
return
yy=event.y
i=0
if flag==3:
#不到半屏自动回位
while yy-i>=-350:
canvas.delete('a')
canvas.create_image(200,yy-i-350,image=var1,tags="a")
i+=1
root.update_idletasks() #刷新
root.update()
flag=f
return
#超过半屏自动落下
if yy>350:
while yy+i<=702:
canvas.delete('a')
canvas.create_image(200,yy+i-350,image=var1,tags="a")
i+=1
root.update_idletasks() #刷新
root.update()
flag=2
return
#点击底端,自动回位
while yy-i>=-350:
canvas.delete('a')
canvas.create_image(200,yy-i-350,image=var1,tags="a")
i+=1
root.update_idletasks() #刷新
root.update()
flag=f
#存储初始图
load = Image.open("res/0.jpg") #手机壳图片
var= ImageTk.PhotoImage(load)
load = Image.open("res/1.png")
var1= ImageTk.PhotoImage(load)
load = Image.open("res/2.jpg")
var2= ImageTk.PhotoImage(load)
load = Image.open("res/3.jpg")
var3= ImageTk.PhotoImage(load)
load = Image.open("res/4.jpg")
var4= ImageTk.PhotoImage(load)
load = Image.open("res/5.jpg")
var5= ImageTk.PhotoImage(load)
load = Image.open("res/6.jpg")
var6= ImageTk.PhotoImage(load)
load = Image.open("res/7.png")
var7= ImageTk.PhotoImage(load)
load = Image.open("res/8.jpg")
var8= ImageTk.PhotoImage(load)
load = Image.open("res/bt.png")
bt= ImageTk.PhotoImage(load)
canvas.create_image((200,-350), image=var1,tags='a') #下拉图片的初始位置,此时未进入画布当中
canvas2.create_image((225,430), image=var) #手机壳
canvas.create_image((200,352), image=var2,tags='b') #初始桌面
b = Button(root, image = bt, bg='black',activebackground='black')
b.place(x=165,y=800)
b.bind('<Button-1>', onLeftButton)
b.bind('<ButtonRelease-1>', ButtonUp)
canvas.bind('<B1-Motion>', onLeftButtonMove) #按住并移动左键
canvas.bind('<ButtonRelease-1>', onLeftButtonUp) #释放左键
canvas.bind('<Double-Button-1>', DoubleButton) #双击左键
root.bind("<Button-1>",callback)
root.mainloop()
P.S.代码中用到的各种位置都经过了微调,达到最佳效果。
在这篇文章里,我们演示了如何用tkinter实现复杂的鼠标事件,将不同组件的鼠标事件结合起来。赶紧学会这一手,回去哄女朋友吧!效果就不放了,有隐私,不能录视频!