為了520能夠博得女友歡心,我設計了一個用來表白的程序,主要用到了tkinter的畫布和各種鼠標事件,今天就來分享給大家。(借鑒要謹慎,可不要只用它來糊弄女盆友哦)

程序的設計是用tkinter模擬手機操作,實現(xiàn)手機桌面的展示、滑動和下拉,然后點擊指定位置,可以彈出新的內(nèi)容。其原理是用canvas顯示圖片,所謂的滑動和點擊等等,其實就是圖片的更換;下拉則是利用鼠標事件來移動圖片,實現(xiàn)和手機下拉相同的效果(不到半屏自動回位,超過半屏自動落下,落下后拖動底端回位)。

桌面是犯罪嫌疑人——偷心賊

下拉的效果:

部分效果如上所示,點擊信息、撥號和相冊,可以分別彈出新的圖片(把表白的內(nèi)容放在這里);點擊home鍵則是返回(home鍵是一個顯示了圖片的button按鈕);桌面還可以滑動,滑動后切換成另一張背景圖(我本人),然后對應綁定了不同的鼠標事件,點擊后會顯示與之前不同的圖片。(所有圖片都是我用自己的手機截圖的)

程序邏輯里,最重要的部分就是幾個模式的區(qū)分,我用flag來定義,具體如下:flag=0是初始狀態(tài)、點擊屏幕其他位置和下拉未到半屏回位后的狀態(tài);flag=1是點擊屏幕頂端下拉;flag=2是下拉超過半屏后自動落下,和此時再點擊其他位置;flag=3是落下后點擊屏幕底端回位;flag=5是切換屏幕。

程序中創(chuàng)建了兩個canvas,一個用來顯示外殼(固定不動),另一個用來顯示屏幕(觸發(fā)各種事件)。在這兩個畫布中,獲取鼠標位置event.x和event.y是分開計的,所以我們只需要用到顯示屏幕的畫布中的位置。根據(jù)信息、撥號和相冊的位置,在點擊時設置對應的flag值,觸發(fā)各自的事件(其實就是顯示新的圖片)。

另外,所有屏幕圖片的大小都是400*700,所以桌面的初始位置在(200,350)附近,而下拉圖片的初始位置是(200,-350),此時是在畫布外的,不會顯示出來。觸發(fā)下拉事件時,它就會跟隨鼠標移動。(這也是創(chuàng)建兩個canvas的原因,如果只用一個,由于屏幕不是在畫布的頂端,因此下拉圖片不會被隱藏起來;而如果它屬于其中一個畫布,但初始位置在畫布外,那么就會隱藏)

下面是完整代碼和簡單注釋:

from tkinter import *
import tkinter as tk
from PIL  import Image, ImageTk
import time
import cv2

flag=0  #狀態(tài)標志
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,當前事件結(jié)束后返回前一個狀態(tài)
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鍵,結(jié)束計時,超過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來區(qū)分
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
    
#釋放左鍵,觸發(fā)下拉的動態(tài)效果(自動下落或回位)以及兩個桌面的切換      
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.代碼中用到的各種位置都經(jīng)過了微調(diào),達到最佳效果。

在這篇文章里,我們演示了如何用tkinter實現(xiàn)復雜的鼠標事件,將不同組件的鼠標事件結(jié)合起來。趕緊學會這一手,回去哄女朋友吧!