Tkinter

講師 堇姬

堇姬Naup(網管/美宣)

成電二年級/幽夜工作室繪師

CKCSC36

DC : naup_sumire_hime

IG : ckcsc36th_naup

涉獵C++、C、python、遊戲(tkinter、pygame)、資安(Web、Crypto)、AI、flask、html/css/js、 PHP、DC bot。

喜歡看輕小說、動畫、Vtuber、打音遊,也喜歡看百合,就是一個長年混跡ACG的宅女。

夢想是可以成為很電的駭客跟繪師,也想自己寫出一個AI老婆。

IG:HSVI_111

FB

Twitter

官網

Windows娘圖鑑

這頁在搞

WIN10

WIN11

WIN95

WIN8

WIN7

Windows Me

Windows 2000

Windows XP

Windows XP HOME

Windows Vista

 Windows CE

Windows Server 2003

Windows NT4.0

WINDOWS 3.1

for-while

各種資料結構

是由一堆元素所組成的資料結構

  • 串列(List)->[]

     可排序、可修改、可重複

  • 元組(Tuple)->()

     可排序、不可修改、可重複

  • 字典(Dictionary)->{ "key" : "value" }

    不可排序、可修改、不可重複

  • 集合(Set)->{}

     不可排序、無索引、不可重複

一維陣列

索引是從0開始

a=['張三', '李四' , 123] #List
b=('張三', '李四', 123)  #Tuple
c={1:'一月', 2:'二月', 3:'三月'} #Dictionary 
d={'張三', '李四', 123, 123} #Set

二維陣列

row

column

map=[[1,2,3,4,5],
     [6,7,8,9,10],
     [11,12,13,14,15]]

print(map[1][3])
#輸出9
  • append:在最後面附加一個元素
  • clear:清空串列裡面的所有元素
  • copy:複製一個串列,例如 list2 = list1.copy
  • count:傳回某個數在串列中出現的次數,如list.count(60)
  • index:傳回某個數在串列中第一次出現的索引值,如list.index(60)
  • insert:在串列指定位置插入元素
  • remove:移除第一個找到的元素,例如list.remove("a")
  • reverse:反轉串列,如 list.reverse()
  • sort:排序串列,如list.sort(), list.sort(reverse=True)
  • max:找出串列裡面的最大值,如 print(max(score))
  • min:找出串列裡面的最小值,如 print(min(score))

常用函式

函式(Function)

 

輸入資料給它就會產生結果的東西

abc

輸入(input)

輸出(output)

函式

def abc(a):
    return a+1
print(abc(2))

輸出3

函式名字

參數

回傳

呼叫函式

import

Python 擁有許多強大的模組,數據分析用的 numpy 、機器學習用的 keras、以及本次要教的tkinter也是一種 

有些並非內建的模組那會需要用pip install下來

pip install [module]

module名字

成功在環境當中新增可使用的模組以後,使用時透過 import 語法來讓程式取得我們所 import 的程式碼並執行。

import [module] (as [name])

方法一

將我們所要引入的 module 裡面的所有程式碼 import 進來,因此可以在 import 以後使用該模組裡的所有變數、函式。

如果覺得模組名太長就可以使用as來改他的名字

import random
print(random.randint(1, 100))

Text

使用 random 模組裡的randint 函式

import random as rm
print(rm.randint(1, 100))

引入 random 模組時,同時將其重新命名為 rm ,然後再繼續使用

方法二

from [module] import [variable/function]

導入特定的變數或是函式來使用,然後指定這些變數或是函式的來源模組。

from random import randint
print(randint(1, 100))

註:不需要透過 module. 來取用了,可以直接使用

那可以自己寫模組來用嗎?

今天寫了如下的程式碼,並且命名為 mymath.py

使用

CUI

命令列介面(CLI),通常不支援滑鼠,使用者通過鍵盤輸入指令,電腦接收到指令後執行。也稱為文字使用者介面( CUI)。

看起來很不友善吧

有人有曾經做遊戲,做這樣的嗎?

綜上所述是不是覺得使用CUI與使用者互動很不友善呢?

所以採用圖形方式顯示的電腦操作使用者介面

GUI

圖形使用者介面(GUI)是指採用圖形方式顯示的電腦操作使用者介面。

GUI

只是想放WIN11娘

透過 GUI 可以透過比較直覺的方式來和程式互動(畢竟要讓一般的使用者使用指令來操作程式是挺難的)

究竟怎麼實現GUI呢?

Tkinter簡介

製作GUI可以使用tkinter模組

Tkinter是可以將Python程式碼變成圖形化介面的套件庫

  1. 簡單易學

  2. 可以使用少量的程式碼產生功能強大的 GUI 介面

  3. 內置於Python 裡面的,不需要我們單獨去安裝

  1. 缺點就是功能過於簡單,這個框架提供的功能太少了,很多功能還是需要手動實現

  2. 相對於c++或其他的套件來說,它的效率仍不算太好

優勢

缺點

repl.it

選擇tkinter

給專案命名

創建

碼程式碼的地方

你視窗顯示的地方

Tkinter實作

視窗建置篇

建構視窗

視窗

300

200

import tkinter  #載入tkinter模組
window=tkinter.Tk()  #製作視窗物件
window.title("HELLO")  #指定視窗標題
window.geometry("300x200")  #視窗大小
window.resizable(False, False)#設定為兩個False代表長寬都無法被放大縮小
window.mainloop()   #顯示視窗

Tkinter實作

元件篇

元件

所有GUI元件

通用參數

 

  • height : 高度
  • width : 寬度
  • fg : 文字顏色
  • bg : 背景顏色
  • command : 指令,利用對應的自定義函式讓物件動作時執行某些動作。
import tkinter  #載入tkinter模組

def plus():#函式
    print("你點擊了按鈕")

window=tkinter.Tk()  #製作視窗物件
window.title("HELLO")  #指定視窗標題
window.geometry("300x200")  #視窗大小
test=tkinter.Button(text="這是Button",command=plus)#按鈕
test.pack()
window.mainloop()   #顯示視窗

label

label_1=tkinter.Label(windows,text="堇姬")
label_1.place(x=150,y=100)

100

150

*以左上角座標為基準

(0,0)

X軸

y軸

Button

import tkinter  #載入tkinter模組
window=tkinter.Tk()  #製作視窗物件
window.title("HELLO")  #指定視窗標題
window.geometry("300x200")  #視窗大小
window.resizable(False, False)#設定為兩個False代表長寬都無法被放大縮小
tkinter.Button(window, text='button').place(x=200, y=100)#按鈕
window.mainloop()   #顯示視窗

Checkbutton

import tkinter  #載入tkinter模組

#勾選時值設為1 未勾選則設為0 每次按下皆顯示目前狀態
def check():
    print(var.get())

window=tkinter.Tk()  #製作視窗物件
window.title("HELLO")  #指定視窗標題
window.geometry("300x200")  #視窗大小
window.resizable(False, False)#設定為兩個False代表長寬都無法被放大縮小

var = tkinter.IntVar()#設定變數 Int 型別儲存目前內容
#儲存的資料位置為 var

tkinter.Checkbutton(windows, variable=var, text='Check', onvalue=1, offvalue=0, command=check).pack()
window.mainloop()   #顯示視窗

Entry

import tkinter  #載入tkinter模組

def show():
    password=test.get()#.get得到entry輸入的值
    print(password)
window=tkinter.Tk()  #製作視窗物件
window.title("HELLO")  #指定視窗標題
window.geometry("300x200")  #視窗大小
window.resizable(False, False)#設定為兩個False代表長寬都無法被放大縮小
test=tkinter.Entry(show="*")#創建entry並將輸入的顯示出*
test.pack()
#建立button並在按下後呼叫show
testButton = tkinter.Button(text="show",command=show)
testButton.pack()
window.mainloop()   #顯示視窗
  • 使用show="",可以隱藏原本輸入的
  • .get可以得到entry輸入的值

 

Canvas

import tkinter  #載入tkinter模組

window=tkinter.Tk()  #製作視窗物件
window.title("HELLO")  #指定視窗標題
window.geometry("300x200")  #視窗大小
window.resizable(False, False)#設定為兩個False代表長寬都無法被放大縮小
canvas = tkinter.Canvas(window, bg='skyblue') #建立畫布
canvas.pack()
window.mainloop()   #顯示視窗

建立畫布,背景用天空藍

更多元件可以參考:

Tkinter實作

布局篇

打包(Pack)

讓各個部件依pack順序由上至下、也可指定左右或特定位置放置

最簡單,但要做複雜的對位及控制,調整對齊的難度高

import tkinter

window = tkinter.Tk()
window.title("Naup")
window.geometry("300x300")

neko = tkinter.Label(window,text="neko",bg="Yellow")
button = tkinter.Button(window,text="Click me!")

neko.pack()
button.pack()

tkinter.mainloop()

流水式排版,利用相對位置的概念去做控件的配置

相關參數

參數並不一定要加,沒有傳入參數時會以預設值排版

定位(place)

直接指定各個部件X及Y軸之絕對或相對位置

最精準,但布局過程及布局後之調整很費心。

import tkinter

window = tkinter.Tk()
window.title("Naup")
window.geometry("300x300")

neko=tkinter.Label(window,text="neko",bg="Yellow")
button=tkinter.Button(window,text="Click me!")

neko.place(x=0,y=20)
button.place(x=200,y=70)

tkinter.mainloop()

使用 place() 方法只能放在主視窗 root 裡,不能放在其他元件 ( 如 Frame 中 )

 x 和 y 參數表示放置的元件在主視窗的絕對位置,視窗左上角為 (0,0),往右為正,往下為正

網格(grid)

建立欄列二維矩陣行列,並運用列row、欄column位置指定各個部件的布局

規則邏輯很清楚很統一,未來也最好調整,撰寫後之的程式也最好理解及佈局最結構化

Tkinter實作

及時處理篇

即時處理

不只有玩家操作才可觸發的事件觸發型事件(如按下按鈕、按鍵盤,將在下篇講解),也有就算玩家不執行動作也會一直跑的即時處理

after()

after(毫秒 , 需要執行函數)

過多久後執行

import tkinter

sec=0
def count_up():
    global sec #宣告成全域變數
    sec+=1
    neko["text"]=sec #label顯示出sec
    window.after(1000,count_up) #過一秒後執行這個函數

window=tkinter.Tk()  #製作視窗物件
window.title("HELLO")  #指定視窗標題
window.geometry("300x200")  #視窗大小
window.resizable(False, False)#設定為兩個False代表長寬都無法被放大縮小

neko=tkinter.Label(window,font=("Times New Roman",100)) #建立label元件
neko.pack()

window.after(1000,count_up) #過一秒後執行這個函數

tkinter.mainloop() #顯示視窗

計時器

第18行:window.after(1000,count_up)

第8行:window.after(1000,count_up)

count_up()

Tkinter實作

事件處理篇

事件

使用者對軟體操作的行為稱為事件

ex: 點選視窗圖片,稱為"對可愛的WIN11娘圖片觸發點選事件"

bind()

bind("<事件>",事件觸發執行的函式)

<事件>

事件內容

<KeyPress>或<Key>

<KeyRelease>

<Motion>

<ButtonPress>或<Button>

按下按鍵

放開按鍵

移動滑鼠游標

點選滑鼠按鍵

import tkinter

key=0
def keydown(e):#按下鍵盤時顯示該鍵的鍵碼
    global key
    key=e.keycode 
    print("KEY:",str(key))

window=tkinter.Tk()  #製作視窗物件
window.title("HELLO")  #指定視窗標題
window.geometry("300x200")  #視窗大小
window.resizable(False, False)#設定為兩個False代表長寬都無法被放大縮小

window.bind("<KeyPress>",keydown)

tkinter.mainloop() #顯示視窗

e用來接收按鍵事件

(<KeyPress event send_event=True state=Mod1 keysym=s keycode=83 char='s' x=882 y=498>)

實戰

素材連結:

目標樣子

首先要先建立地圖

import tkinter
window = tkinter.Tk()
window.title("顯示迷宮")
canvas = tkinter.Canvas(width=800, height=560, bg="white")
canvas.pack()
map=[
    [1,1,1,1,1,1,1,1,1,1],
    [1,0,0,0,0,0,1,0,0,1],
    [1,0,1,1,0,0,1,0,0,1],
    [1,0,0,1,0,0,0,0,0,1],
    [1,0,0,1,1,1,1,1,0,1],
    [1,0,0,0,0,0,0,0,0,1],
    [1,1,1,1,1,1,1,1,1,1]
    ]
for y in range(7):
    for x in range(10):
        if map[y][x] == 1:
            canvas.create_rectangle(x*80, y*80, x*80+80, y*80+80, fill="skyblue")
window.mainloop()

創建一個二維陣列

0->空
1->邊

加入角色
#注意圖片看的座標式圖片中心點

import tkinter
window = tkinter.Tk()
window.title("顯示角色")
canvas = tkinter.Canvas(width=800, height=560, bg="white")
canvas.pack()
map=[
    [1,1,1,1,1,1,1,1,1,1],
    [1,0,0,0,0,0,1,0,0,1],
    [1,0,1,1,0,0,1,0,0,1],
    [1,0,0,1,0,0,0,0,0,1],
    [1,0,0,1,1,1,1,1,0,1],
    [1,0,0,0,0,0,0,0,0,1],
    [1,1,1,1,1,1,1,1,1,1]
    ]
minix = 1 #角色所在格子x
miniy = 1 #角色所在格子y
for y in range(7):
    for x in range(10):
        if map[y][x] == 1:
            canvas.create_rectangle(x*80, y*80, x*80+80, y*80+80, fill="skyblue")

img = tkinter.PhotoImage(file="mimi_s.png") #建立圖片
canvas.create_image(minix*80+40, miniy*80+40, image=img, tag="MYCHR") #顯示圖片
window.mainloop()

讓貓貓動起來

import tkinter


key = ""
def key_down(e):  #處理按下
    global key   
    key = e.keysym  #存取當前按下的鍵

def key_up(e):  #處理放開
    global key
    key = ""    #清空當前的按下狀態

window = tkinter.Tk()
window.title("顯示角色")
window.bind("<KeyPress>", key_down) #接收按下事件
window.bind("<KeyRelease>", key_up) #接收放開事件
canvas = tkinter.Canvas(width=800, height=560, bg="white")
canvas.pack()
map=[
    [1,1,1,1,1,1,1,1,1,1],
    [1,0,0,0,0,0,1,0,0,1],
    [1,0,1,1,0,0,1,0,0,1],
    [1,0,0,1,0,0,0,0,0,1],
    [1,0,0,1,1,1,1,1,0,1],
    [1,0,0,0,0,0,0,0,0,1],
    [1,1,1,1,1,1,1,1,1,1]
    ]
minix = 1 
miniy = 1 
def main_proc():
    global minix, miniy
    if key == "Up" and map[miniy-1][minix] == 0:
        miniy = miniy - 1
    if key == "Down" and map[miniy+1][minix] == 0:
        miniy = miniy + 1
    if key == "Left" and map[miniy][minix-1] == 0:
        minix = minix - 1
    if key == "Right" and map[miniy][minix+1] == 0:
        minix = minix + 1
    canvas.coords("MYCHR", minix*80+40, miniy*80+40)#移動更新
    window.after(100, main_proc) #每100毫秒偵測一次移動

for y in range(7):
    for x in range(10):
        if map[y][x] == 1:
            canvas.create_rectangle(x*80, y*80, x*80+80, y*80+80, fill="skyblue")

img = tkinter.PhotoImage(file="mimi_s.png") 
canvas.create_image(minix*80+40, miniy*80+40, image=img, tag="MYCHR") 

main_proc()  #第一次呼叫主要循環函式
window.mainloop()
import tkinter


key = ""
def key_down(e):
    global key   
    key = e.keysym  

def key_up(e):  
    global key
    key = "" 

window = tkinter.Tk()
window.title("顯示角色")
window.bind("<KeyPress>", key_down) 
window.bind("<KeyRelease>", key_up)
canvas = tkinter.Canvas(width=800, height=560, bg="white")
canvas.pack()
map=[
    [1,1,1,1,1,1,1,1,1,1],
    [1,0,0,0,0,0,1,0,0,1],
    [1,0,1,1,0,0,1,0,0,1],
    [1,0,0,1,0,0,0,0,0,1],
    [1,0,0,1,1,1,1,1,0,1],
    [1,0,0,0,0,0,0,0,0,1],
    [1,1,1,1,1,1,1,1,1,1]
    ]
minix = 1 
miniy = 1 
def main_proc():
    global minix, miniy
    if key == "Up" and map[miniy-1][minix] == 0:
        miniy = miniy - 1
    if key == "Down" and map[miniy+1][minix] == 0:
        miniy = miniy + 1
    if key == "Left" and map[miniy][minix-1] == 0:
        minix = minix - 1
    if key == "Right" and map[miniy][minix+1] == 0:
        minix = minix + 1

    if map[miniy][minix] == 0: #如果所在的是空則填滿粉色
        map[miniy][minix] = 2  #將粉色的格子變成2
        #讓他變成粉色
        canvas.create_rectangle(minix*80, miniy*80, minix*80+79, miniy*80+79, fill="pink", width=0)
    #刪除圖片
    canvas.delete("MYCHR")
    #在新位子創建圖片
    canvas.create_image(minix*80+40, miniy*80+40, image=img, tag="MYCHR")
    
    window.after(100, main_proc) 

for y in range(7):
    for x in range(10):
        if map[y][x] == 1:
            canvas.create_rectangle(x*80, y*80, x*80+80, y*80+80, fill="skyblue")

img = tkinter.PhotoImage(file="mimi_s.png") 
canvas.create_image(minix*80+40, miniy*80+40, image=img, tag="MYCHR") 

main_proc()  
window.mainloop()

創建粉色地板

偵測過關

import tkinter
import tkinter.messagebox #引入訊息框模組


key = ""
def key_down(e):
    global key   
    key = e.keysym  

def key_up(e):  
    global key
    key = "" 

window = tkinter.Tk()
window.title("顯示角色")
window.bind("<KeyPress>", key_down) 
window.bind("<KeyRelease>", key_up)
canvas = tkinter.Canvas(width=800, height=560, bg="white")
canvas.pack()
map=[
    [1,1,1,1,1,1,1,1,1,1],
    [1,0,0,0,0,0,1,0,0,1],
    [1,0,1,1,0,0,1,0,0,1],
    [1,0,0,1,0,0,0,0,0,1],
    [1,0,0,1,1,1,1,1,0,1],
    [1,0,0,0,0,0,0,0,0,1],
    [1,1,1,1,1,1,1,1,1,1]
    ]
minix = 1 
miniy = 1 
yuka = 0 #計算有幾個塗色
def main_proc():
    global minix, miniy, yuka
    if key == "Up" and map[miniy-1][minix] == 0:
        miniy = miniy - 1
    if key == "Down" and map[miniy+1][minix] == 0:
        miniy = miniy + 1
    if key == "Left" and map[miniy][minix-1] == 0:
        minix = minix - 1
    if key == "Right" and map[miniy][minix+1] == 0:
        minix = minix + 1

    if map[miniy][minix] == 0: 
        map[miniy][minix] = 2  
        yuka=yuka+1
        canvas.create_rectangle(minix*80, miniy*80, minix*80+79, miniy*80+79, fill="pink", width=0)

    canvas.delete("MYCHR")
    canvas.create_image(minix*80+40, miniy*80+40, image=img, tag="MYCHR")
    if yuka==30:  #全部塗滿過關
        canvas.update()  #更新畫布(若不加可能會有最後格沒有上到色)
        tkinter.messagebox.showinfo("恭喜過關","你獲得WIN11娘")  #跳出過關訊息
    else:  #如果過關就不用繼續了
        window.after(100, main_proc) 

for y in range(7):
    for x in range(10):
        if map[y][x] == 1:
            canvas.create_rectangle(x*80, y*80, x*80+80, y*80+80, fill="skyblue")

img = tkinter.PhotoImage(file="mimi_s.png") 
canvas.create_image(minix*80+40, miniy*80+40, image=img, tag="MYCHR") 

main_proc()  
window.mainloop()

成發

把python語法熟悉,然後把元件跟事件處理練熟,成發可以做小遊戲之類的。(給不知道要做甚麼的人一些方向。)

講師展示一些用tkinter做的作品

三消:

2048:

感謝聆聽

實戰1

素材連結:

可以讓中間那個可愛的女生上下左右移動

1.建立視窗

window = tkinter.Tk()
window.title("移動角色")

2.創建畫布,把圖片顯示出來

canvas = tkinter.Canvas(width=800, height=600, bg="black")
canvas.pack()
img = tkinter.PhotoImage(file="cute.png")
canvas.create_image(cx, cy, image=img, tag="MYCHR")

#tag參數:取名神器

如何讓他動起來?

1.我們需要達到按著左鍵時向左移動,所以需要偵測兩個,(1)是否按下 (2)是否放開

可以用甚麼來接收是否按下鍵盤?

window.bind("<KeyPress>", key_down)
window.bind("<KeyRelease>", key_up)

用bind()接收

創建兩個函式處理事件

1.key_down:處理按下的事件,需要紀錄現在按下哪顆鍵

2.key_up:處理放開的事件,若放開清空現在按下的鍵

key = ""
def key_down(e):
    global key
    key = e.keysym #得到該鍵的名字
def key_up(e):
    global key
    key = ""  #清空現在的狀態

只差一步了,就是動起來

確認流程

確認是否按下

上、下、左、右

變更到新座標

main_proc()

100毫秒後呼叫

cx = 400 #X座標
cy = 300 #y座標
def main_proc():
    global cx, cy 
    #x,y改變
    if key == "Up": 
        cy = cy - 20
    if key == "Down":
        cy = cy + 20
    if key == "Left":
        cx = cx - 20
    if key == "Right":
        cx = cx + 20
    #更新圖片位子
    canvas.coords("MYCHR", cx, cy)
    #每100毫秒呼叫
    window.after(100, main_proc)

程式碼實現

import tkinter

key = ""
def key_down(e):
    global key
    key = e.keysym
def key_up(e):
    global key
    key = ""

cx = 400 #X座標
cy = 300 #y座標
def main_proc():
    global cx, cy 
    #x,y改變
    if key == "Up": 
        cy = cy - 20
    if key == "Down":
        cy = cy + 20
    if key == "Left":
        cx = cx - 20
    if key == "Right":
        cx = cx + 20
    #更新圖片位子
    canvas.coords("MYCHR", cx, cy)
    #每100毫秒呼叫
    window.after(100, main_proc)

window = tkinter.Tk()
window.title("移動角色")
window.bind("<KeyPress>", key_down)
window.bind("<KeyRelease>", key_up)
canvas = tkinter.Canvas(width=800, height=600, bg="black")
canvas.pack()
img = tkinter.PhotoImage(file="cute.png")
canvas.create_image(cx, cy, image=img, tag="MYCHR")

還差一點,最後一開始需要先呼叫一次函式,並且用window.mainloop()顯示出來

完整程式碼

import tkinter

key = ""
def key_down(e):
    global key
    key = e.keysym
def key_up(e):
    global key
    key = ""

cx = 400 #X座標
cy = 300 #y座標
def main_proc():
    global cx, cy 
    #x,y改變
    if key == "Up": 
        cy = cy - 20
    if key == "Down":
        cy = cy + 20
    if key == "Left":
        cx = cx - 20
    if key == "Right":
        cx = cx + 20
    #更新圖片位子
    canvas.coords("MYCHR", cx, cy)
    #每100毫秒呼叫
    window.after(100, main_proc)

window = tkinter.Tk()
window.title("移動角色")
window.bind("<KeyPress>", key_down)
window.bind("<KeyRelease>", key_up)
canvas = tkinter.Canvas(width=800, height=600, bg="black")
canvas.pack()
img = tkinter.PhotoImage(file="cute.png")
canvas.create_image(cx, cy, image=img, tag="MYCHR")
main_proc()
window.mainloop()

Tkinter

By naup96321