Python GUI——tkinter菜鳥編程(下)


19. Progressbar

進度條,主要用來當做一個工作進度指針,在這個控件中會有一個指針,由此指針可以了解工作進度。

Progressbar(父對象,options,…)

  • length:進度條的長度,默認100像素;
  • mode:兩種模式;
    • determinate:默認,一個指針會從起點移至終點,通常當我們知道所需工作時間時,可以使用此模式;
    • indeterminate:一個指針會在起點和終點之間來回移動,通常當我們不知道工作所需時間時,可以使用此模式;
  • maximum:進度條的最大值,默認是100;
  • name:進度條的名稱,供程序參考引用;
  • orient:進度條的方向;
  • value:進度條的當前值;
  • variable:記錄進度條當前值的變量。

使用update方法更新value值,可以達到動畫效果。

from tkinter import *
from tkinter.ttk import *
import time
def running():
    for i in range(100):
        pb["value"] = i+1
        root.update()
        time.sleep(0.05)
root = Tk()
root.title("Ex")

pb = Progressbar(root, length = 200, mode = "determinate", \
                 orient = HORIZONTAL)
pb.pack(padx = 10, pady = 10)
pb["maximum"] = 100
pb["value"] = 0

btn = Button(root, text = "Running", command = running)
btn.pack(pady = 10)

root.mainloop()

image

Progressbar的方法:

  1. start(interval):每隔interval時間移動一次指針,默認50ms,每次指針移動時調用一次step(delta);
  2. step(delta):每次增加delta,默認值1.0;
  3. stop():停止start()的運行。

20. Menu

Menu(父對象,options,…)

  • bd,bg,cursor,font,fg;
  • activebackground:當光標移至此菜單列表上時的背景色彩;
  • activeborderwidth:當光標移至此菜單列表上時的前景色彩;
  • disabledforeground:菜單列表是DISABLED時的色彩;
  • image:菜單的圖標;
  • tearoff:菜單上方的分割線,True或False;
  • add_cascade():建立分層菜單,同時讓此子功能列表與父菜單建立鏈接;
  • add_command():增加菜單列表;
  • add_separator():增加菜單列表的分割線。
def hello():
    messagebox.showinfo("Hello", "歡迎使用菜單")
root = Tk()
root.title("Ex")
root.geometry("300x160")

menubar = Menu(root)
menubar.add_command(label = "Hello", command = hello)
menubar.add_command(label = "Exit", command = root.destroy)
root.config(menu = menubar)

root.mainloop()

image

20.1 下拉菜單

def File():
    messagebox.showinfo("File", "New File")
root = Tk()
root.title("Ex")
root.geometry("300x160")

menubar = Menu(root)
filemenu = Menu(menubar, tearoff = False)
menubar.add_cascade(label = "File", menu = filemenu)
filemenu.add_command(label = "New File", command = File)
filemenu.add_command(label = "Exit", command = root.destroy)
root.config(menu = menubar)

root.mainloop()

image

分割線:在add_command()中間穿插使用filemenu.add_separator()即可。

20.2 Alt快捷鍵

快捷鍵是在某個菜單類別或列表指定的英文字符串內為單一字母增加下划線,然后可以用Alt鍵先啟動根菜單,在菜單中可以直接按字母鍵啟動相應功能。

設計方法是在add_command()和add_cascade()中使用underline參數。

menubar.add_cascade(label = "File", menu = filemenu, underline = 0)
filemenu.add_command(label = "New File", command = File, underline = 0)
filemenu.add_command(label = "Exit", command = root.destroy, underline = 0)

20.3 Ctrl快捷鍵

filemenu.add_command(label = "New File", command = File, underline = 0, \
                      accelerator = "Ctrl+N")


root.bind("<Control-n>", \
           lambda event:messagebox.showinfo("File", "New File"))

20.4 彈出式菜單

右鍵快捷菜單。

建立Menu對象后直接與右鍵綁定即可。

def minimizeIcon():
    root.iconify()
def showPopupMenu(event):
    popupmenu.post(event.x_root, event.y_root)
root = Tk()
root.title("Ex")
root.geometry("300x160")

popupmenu = Menu(root, tearoff = False)
popupmenu.add_command(label = "Minimize", command = minimizeIcon)
popupmenu.add_command(label = "Exit", command = root.destroy)
root.bind("<Button-3>", showPopupMenu)

root.mainloop()

20.5 add_checkbutton()

def status():
    if demoStatus.get():
        statusLabel.pack(side = BOTTOM, fill = X)
    else:
        statusLabel.pack_forget()
root = Tk()
root.title("Ex")
root.geometry("300x160")

menubar = Menu(root)
filemenu = Menu(menubar, tearoff = False)
menubar.add_cascade(label = "File", menu = filemenu)
filemenu.add_command(label = "Exit", command = root.destroy)
viewmenu = Menu(menubar, tearoff = False)
menubar.add_cascade(label = "View", menu = viewmenu)

demoStatus = BooleanVar()
demoStatus.set(True)
viewmenu.add_checkbutton(label = "Status", command = status, \
                         variable = demoStatus)
root.config(menu = menubar)
statusVar = StringVar()
statusVar.set("顯示")
statusLabel = Label(root, textvariable = statusVar, relief = "raised")
statusLabel.pack(side = BOTTOM, fill = X)

root.mainloop()

21. Text

Text控件可以看做是Entry的擴充,可以處理多行輸入,也可以在文字中嵌入圖像或提供格式化功能,實際上可以將Text當做文字處理軟件,甚至當做網頁瀏覽器。

Text(父對象,options,…)

  • bg,bd,cursor,fg,font,height,highlightbackground,highlightcolor,highlightthickness,padx,pady,relief,selectbackground,selectborderwidth,selectforeground,width,wrap,xscrollcommand,yscrollcommand;
  • exportselection:執行選擇操作時,所選擇的字符串會自動輸出至剪貼板,設置exportselection=0以避免;
  • insertbackground:插入光標的色彩,默認是黑色;
  • insertborderwidth:圍繞插入游標的3D厚度,默認為0;
  • state:默認NORMAL,DISABLED則是無法編輯;
  • tab:可設置按Tab鍵時,如何定位插入點。

21.1 insert()

使用insert(index, string)可以將字符串插入到指定位置。

root = Tk()
root.title("Ex")

text = Text(root, height = 3, width = 30)
text.pack()
text.insert(END, "Python\nTkinter\n")
text.insert(END, "I like you.")

root.mainloop()

image

21.2 Scrollbar

root = Tk()
root.title("Ex")

yscrollbar = Scrollbar(root)
yscrollbar.pack(side = RIGHT, fill = Y)
text = Text(root, height = 5, width = 30)
text.pack()
yscrollbar.config(command = text.yview)
text.config(yscrollcommand = yscrollbar.set)
string = """Tkinter模塊("Tk 接口")是Python的標准Tk GUI工具包的接口.
Tk和Tkinter可以在大多數的Unix平台下使用,同樣可以應用在Windows和
Macintosh系統里.Tk8.0的后續版本可以實現本地窗口風格,並良好地運行
在絕大多數平台中."""
text.insert(END, string)

root.mainloop()

image

是Text文字區域隨着窗口的擴充而擴充:

text.pack(fill = BOTH, expand = True)

21.3 字形

  • family:Arial(默認)、Times、Courier;
  • weight:norma(默認)、bold;
  • size:字號。
from tkinter import *
from tkinter.ttk import *
from tkinter.font import Font

def familyChanged(event):
    f = Font(family = familyVar.get())
    text.configure(font = f)
def weightChanged(event):
    f = Font(weight = weightVar.get())
    text.configure(font = f)
def sizeSelected(event):
    f = Font(size = sizeVar.get())
    text.config(font = f)

root = Tk()
root.title("Ex")
root.geometry("300x160")

toolbar = Frame(root, relief = RAISED, borderwidth = 1)
toolbar.pack(side = TOP, fill = X, padx = 2, pady = 1)
familyVar = StringVar()
familyFamily = ("", "Arial", "Times", "Courier")
familyVar.set(familyFamily[1])
family = OptionMenu(toolbar, familyVar, *familyFamily, command = familyChanged)
family.pack(side = LEFT, pady = 2)

weightVar = StringVar()
weightFamily = ("", "normal", "bold")
weightVar.set(weightFamily[1])
weight = OptionMenu(toolbar, weightVar, *weightFamily, command = weightChanged)
weight.pack(side = LEFT, pady = 2)

sizeVar = IntVar()
size = Combobox(toolbar, textvariable = sizeVar)
sizeFamily = [x for x in range(8, 31)]
size["value"] = sizeFamily
size.current(4)
size.bind("<<ComboboxSelected>>", sizeSelected)
size.pack(side = LEFT, pady = 2)

text = Text(root)
text.pack(fill = BOTH, expand = True, padx = 2, pady = 2)
text.focus_set()

root.mainloop()

image

21.4 選取文字

在使用Text文字區域時,如果有選取文字的操作發生時,Text對象會將所選文字的起始索引放在SEL_FIRST,結束索引放在SEL_LAST,將它們當做get()的參數,就可以獲得目前所選的文字。

def selectedText():
    try:
        selText = text.get(SEL_FIRST, SEL_LAST)
        print("選取文字:", selText)
    except TclError:
        print("沒有選取文字!")

root = Tk()
root.title("Ex")
root.geometry("300x160")

btn = Button(root, text = "Print selection", command = selectedText)
btn.pack(pady = 3)

text = Text(root)
text.pack(fill = BOTH, expand = True, padx = 3, pady = 3)
text.insert(END, "Love You Like A Love Song")

root.mainloop()

image

image

認識Text的索引:

Text對象的索引並不是單一數字,而是一個字符串,索引的目的是讓Text控件處理更進一步的文件操作。

  • line/column("line.column"):計數方式line從1開始。column從0開始,中間用句點分隔;
  • INSERT:目前插入點的位置;
  • CURRENT:光標目前為止相對於字符的位置;
  • END:緩沖區最后一個字符的位置;
  • Expression:索引使用表達式;
    • "+count chars":count是數字,例如"+2c"索引往后移動兩個字符;
    • "-count chars":count是數字,例如"-2c"索引往前移動兩個字符。

21.5 書簽

在編輯文件時,可以在文件特殊位置建立書簽,方便查詢。書簽是無法顯示的,但會在編輯系統內被記錄,如果書簽內容被刪除,則此書簽也將自動被刪除。

  • index(mark):傳回指定書簽的line和column;
  • mark_names():傳回這個Text對象所有的書簽;
  • mark_set(mark, index):在指定的index位置設置書簽;
  • mark_unset(mark):取消指定書簽。

text.mark_set("mark1", "1.9")

text.mark_set("mark2", "1.13")

print(text.get("mark1", "mark2"))

image

21.6 標簽

標簽是一個文字區域,我們可以為這個區域去一個名字,有了標簽后,我們可以針對此標簽做更進一步的工作,例如,將字形、色彩等應用在此標簽上。

  • tag_add(tagname, startindex[, endindex] …):將startindex和endindex之間的文字命名為tagname標簽;
  • tag_config(tagname, options, …):可以為標簽執行特定的編輯,或動作綁定;
    • background,borderwidth,font,foreground,justify;
    • overstrike:如果為True,加上刪除線;
    • underline:如果為True,加上下划線;
    • wrap:當使用wrap模式時,可以使用NONE、CHAR、WORD;
  • tag_delete(tagname):刪除此標簽,同時會移除此標簽特殊的編輯或綁定;
  • tag_remove(tagname[, startindex[, endindex]] …):刪除標簽,但是不移除此標簽特殊的編輯或綁定。

除了可以使用tag_add()自行定義標簽外,系統還有一個內建標簽SEL,代表所選取的區間。

text.tag_add("tag1", "mark1", "mark2")
text.tag_config("tag1", foreground = "blue", background = "pink")

image

21.7 Cut/Copy/Paste

編輯文件時剪切/復制/粘貼(Cut/Copy/Paste)是很常用的功能,這些功能已經被內建在tkinter中了。

設計彈出菜單。

def cutJob():
    copyJob()
    text.delete(SEL_FIRST, SEL_LAST)
def copyJob():
    try:
        text.clipboard_clear() #清除剪切板
        copyText = text.get(SEL_FIRST, SEL_LAST)
        text.clipboard_append(copyText)
    except TclError:
        print("沒有選取")
def pasteJob():
    try:
        copyText = text.clipboard_get()
        text.insert(INSERT, copyText)
    except:
        print("剪貼板沒有數據")
def showPopupMenu(event):
    popupmenu.post(event.x_root, event.y_root)

root = Tk()
root.title("Bouncing Ball")
root.geometry("300x180")

popupmenu = Menu(root, tearoff = False)
popupmenu.add_command(label = "Cut", command = cutJob)
popupmenu.add_command(label = "Copy", command = copyJob)
popupmenu.add_command(label = "Paste", command = pasteJob)
root.bind("<Button-3>", showPopupMenu)

text = Text(root)
text.pack(fill = BOTH, expand = True, padx = 3, pady = 2)
text.insert(END, "Five Hundred Miles\n")
text.insert(END, "If you miss the train I'm on,\n")
text.insert(END, "You will know that I am gone.\n")
text.insert(END, "You can hear the whistle blow\n")
text.insert(END, "A hundred miles.")

root.mainloop()

由於沒有選取文字或剪貼板沒有數據就進行讀取會發生TclError,所以設計時增加了try… except設計。

直接引用tkinter的虛擬事件:

def cutJob():
     text.event_generate("<<Cut>>")
def copyJob():
     text.event_generate("<<Copy>>")
def pasteJob():
     text.event_generate("<<Paste>>")

21.8 undo/redo

def undoJob():
     try:
         text.edit_undo()
     except:
         print("先前沒有動作")
def redoJob():
     try:
         text.edit_redo()
     except:
         print("先前沒有動作")

toolbar = Frame(root, relief = RAISED, borderwidth = 1)
toolbar.pack(side = TOP, fill = X, padx = 2, pady = 1)
undoBtn = Button(toolbar, text = "Undo", command = undoJob).pack(side = LEFT)
redoBtn = Button(toolbar, text = "Redo", command = redoJob).pack(side = LEFT)

text = Text(root, undo = True)

21.9 查找文字

在Text控件內可以使用search()方法查找特定的字符串。

pos = text.search(key, startindex, endindex)

  • pos:傳回所到的字符串的索引位置,如果查找失敗則傳回空字符串;
  • key:所查找的字符串;
  • startindex:查找起始位置;
  • endindex:查找結束位置,如果查找到文檔末尾可以使用END。
def mySearch():
    text.tag_remove("found", "1.0", END)
    start = "1.0"
    key = entry.get()
    if len(key.strip()) == 0:
        return
    while True:
        pos = text.search(key, start, END)
        if pos == "":
            break
        text.tag_add("found", pos, "%s+%dc" % (pos, len(key)))
        start = "%s+%dc" % (pos, len(key))

root = Tk()
root.title("Bouncing Ball")
root.geometry("300x180")

root.rowconfigure(1, weight = 1)
root.columnconfigure(0, weight = 1)

btn = Button(root, text = "查找", command = mySearch)
btn.grid(row = 0, column = 1, padx = 5, pady = 5)

entry = Entry()
entry.grid(row = 0, column = 0, padx = 5, pady = 5, \
           sticky = W+E)

text = Text(root, undo = True)
text.grid(row = 1, column = 0, columnspan = 2, padx = 3, pady = 5, \
          sticky = N+S+W+E)
text.insert(END, "Five Hundred Miles\n")
text.insert(END, "If you miss the train I'm on,\n")
text.insert(END, "You will know that I am gone.\n")
text.insert(END, "You can hear the whistle blow\n")
text.insert(END, "A hundred miles.")
text.tag_configure("found", background = "yellow")

root.mainloop()

image

21.10 存儲Text控件內容

使用tkinter.filedialog模塊中的asksaveasfilename可以啟動“另存為”對話框,然后選擇文檔存儲的位置和文件名。

from tkinter.filedialog import asksaveasfilename
def saveAsFile():
    global filename
    textContent = text.get("1.0", END)
    filename = asksaveasfilename()
    if filename == "":
        return
    with open(filename, "w") as output:
        output.write(textContent)
        root.title(filename)

root = Tk()
root.title("Ex")
root.geometry("300x180")

menubar = Menu(root)
filemenu = Menu(menubar, tearoff = False)
menubar.add_cascade(label = "File", menu = filemenu)
filemenu.add_command(label = "Save", command = saveAsFile)
filemenu.add_command(label = "Exit", command = root.destroy)
root.config(menu = menubar)

text = Text(root, undo = True)
text.pack(fill = BOTH, expand = True)
text.insert(END, "Five Hundred Miles\n")
text.insert(END, "If you miss the train I'm on,\n")
text.insert(END, "You will know that I am gone.\n")
text.insert(END, "You can hear the whistle blow\n")
text.insert(END, "A hundred miles.")
root.mainloop()

image

21.11 新建文檔

def newFile():
     text.delete("1.0", END)
     root.title("Untitled")

filemenu.add_command(label = "New", command = newFile)

21.12 打開文檔

from tkinter.filedialog import asksaveasfilename, askopenfilename
def openFile():
     global filename
     filename = askopenfilename()
     if filename == "":
         return
     with open(filename, "r") as fileObj:
         content = fileObj.read()
     text.delete("1.0", END)
     text.insert(END, content)
     root.title(filename)

filemenu.add_command(label = "Open", command = openFile)

21.13 默認滾動條

from tkinter.scrolledtext import ScrolledText

text = ScrolledText(root, undo = True)

21.14 插入圖像

Text控件是允許插入圖像文件的,所插入的圖像文件會被視為一個字符,所呈現的大小是圖像文件的實際大小。

from PIL import Image, ImageTk

root = Tk()
root.title("Ex")
root.geometry("300x160")

img = Image.open("mystar.jpg")
pic = ImageTk.PhotoImage(img)
text = Text()
text.image_create(END, image = pic)
text.insert(END, "\n")
text.insert(END, "A yellow star")
text.pack(fill = BOTH, expand = True)
root.mainloop()

image

22. Treeview

tkinter.ttk中的控件,用於提供多欄的顯示功能,可以成為樹狀表格數據。在設計時也可以在左邊欄設計成樹狀結構(層次結構),用戶可以隱藏任何部分。

Treeview(父對象,options,…)

  • cursor,height;
  • columns:欄位的字符串。其中,第一個欄位的圖標欄,這是默認的,不在此進行設置,如果設置columns = ("Name", "Age"),則控件有3欄,其中,第一個欄位是最左欄的圖標欄,可以進行展開(expand)和隱藏(collapse)操作,另兩欄是Name和Age;
  • displaycolumns:可以設置欄位顯示順序;
    • 如果參數是"#all"表示顯示所有欄,依照建立順序顯示;
    • 如果設置columns = ("Name", "Age", "Date"),使用insert()插入元素時需要依次插入元素,同樣狀況如果使用columns(2, 0),則圖標欄在最前面,緊跟着是Date欄,然后是Name欄;
  • padding:可以用1~4個參數設置內容與控件框的間距,規則如下,

    image

  • selectmode:用戶可以使用鼠標選擇項目的方式;
    • selectmode = BROWSE:一次選擇一項(默認);
    • selectmode = EXTENDED:一次可以選擇多項;
    • selectmode = NONE:無法用鼠標執行選擇;
  • show:默認為"tree",顯示圖標欄,設為"headings"以不顯示;
  • takefocus:默認為True,設為False以不被訪問。

from tkinter.ttk import *
root = Tk()
root.title("Ex")

tree = Treeview(root, columns = ("cities"))
tree.heading("#0", text = "State")
tree.heading("#1", text = "Cities")
tree.insert("", index = END, text = "伊利諾", values = "芝加哥")
tree.insert("", index = END, text = "加州", values = "洛杉磯")
tree.insert("", index = END, text = "江蘇", values = "南京")
tree.pack()

root.mainloop()

#0是指最左欄圖標欄位,#1是指第一個欄位,當所建立的欄是最頂層時,父對象id可以使用""處理。

image

tree = Treeview(root, columns = ("cities"), show = "headings")

image

text:圖標欄的內容;

values:一般欄位的內容,有多欄時使用元組輸入,如果所設置的內容數太少時其他欄將是空白,如果所設置的內容太多時多出的內容將被拋棄。

使用列表方法建立欄位內容:

root = Tk()
root.title("Ex")

list1 = ["芝加哥", "800"]
list2 = ["洛杉磯", "1000"]
list3 = ["南京", "900"]
tree = Treeview(root, columns = ("cities", "populations"))
tree.heading("#0", text = "State")
tree.heading("#1", text = "Cities")
tree.heading("#2", text = "Populations")
tree.insert("", index = END, text = "伊利諾", values = list1)
tree.insert("", index = END, text = "加州", values = list2)
tree.insert("", index = END, text = "江蘇", values = list3)
tree.pack()

root.mainloop()

image

22.1 格式化

column(id, options)

id是指出特定欄位,可以用字符串表達,或是用"#index"索引方式,下列options參數:

  • anchor:設置欄位內容參考位置;
  • minwidth:最小欄寬,默認20像素;
  • stretch:默認為1,當控件大小改變時欄寬將隨之改變;
  • width:默認200像素。

設置欄寬和對齊方式:

tree.column("#1", anchor = CENTER, width = 100)

image


如果此方法不含參數,將以字典方式傳回特定欄所有參數的內容。

ret = column(id)

cityDict = tree.column("cities")
print(cityDict)

image

22.2 不同顏色

fag_configure("tagName",options,…)

  • background,font,foreground,image。
root = Tk()
root.title("Ex")

stateCity = {
        "伊利諾":"芝加哥","加州":"洛杉磯",
        "德州":"休斯頓","華盛頓":"西雅圖",
        "江蘇":"南京","山東":"青島",
        "廣東":"廣州","福建":"廈門"
        }
tree = Treeview(root, columns = ("cities"))
tree.heading("#0", text = "State")
tree.heading("cities", text = "City")
tree.column("cities", anchor = CENTER)
tree.tag_configure("evenColor", background = "lightblue")
rowCount = 1
for state in stateCity.keys():
    if rowCount%2 == 1:
        tree.insert("", index = END, text = state, values = stateCity[state])
    else:
        tree.insert("", index = END, text = state, values = stateCity[state], \
                    tags = ("evenColor"))
    rowCount +=1
tree.pack()

root.mainloop()

image

22.3 層級式Treeview

root = Tk()
root.title("Ex")

asia = {"中國":"北京","日本":"東京","泰國":"曼谷","韓國":"首爾"}
euro = {"英國":"倫敦","法國":"巴黎","德國":"柏林","挪威":"奧斯陸"}

tree = Treeview(root, columns = ("capital"))
tree.heading("#0", text = "國家")
tree.heading("capital", text = "首都")
idAsia = tree.insert("", index = END, text = "Asia")
idEuro = tree.insert("", index = END, text = "Europe")
for country in asia.keys():
    tree.insert(idAsia, index = END, text = country, values = asia[country])
for country in euro.keys():
    tree.insert(idEuro, index = END, text = country, values = euro[country])
tree.pack()

root.mainloop()

image

22.4 事件觸發

def treeSelect(event):
     widgetObj = event.widget
     itemselected = widgetObj.selection()[0]
     col1 = widgetObj.item(itemselected, "text")
     col2 = widgetObj.item(itemselected, "value")[0]
     Str = "{0} : {1}".format(col1, col2)
     var.set(Str)

tree.bind("<<TreeviewSelect>>", treeSelect)
var = StringVar()
label = Label(root, textvariable = var, relief = "groove")
label.pack(fill = BOTH, expand = True)

image

22.5 刪除項目

def removeItem():
     ids = tree.selection()
     for iid in ids:
         tree.delete(iid)

tree = Treeview(root, columns = ("cities"), selectmode = EXTENDED)

rmBtn = Button(root, text = "Remove", command = removeItem).pack(pady = 5)

def removeItem():
    ids = tree.selection()
    for iid in ids:
        tree.delete(iid)

root = Tk()
root.title("Ex")

stateCity = {
        "伊利諾":"芝加哥","加州":"洛杉磯",
        "德州":"休斯頓","華盛頓":"西雅圖",
        "江蘇":"南京","山東":"青島",
        "廣東":"廣州","福建":"廈門"
        }
tree = Treeview(root, columns = ("cities"), selectmode = EXTENDED)
tree.heading("#0", text = "State")
tree.heading("cities", text = "City")
for state in stateCity.keys():
    tree.insert("", index = END, text = state, values = stateCity[state])
tree.pack()
rmBtn = Button(root, text = "Remove", command = removeItem).pack(pady = 5)
root.mainloop()

22.6 插入項目

def insertItem():
     state = stateEntry.get()
     city = cityEntry.get()
     if (len(state.strip()) == 0 or len(city.strip()) == 0):
         return
     tree.insert("",END,text=state,values=city)
     stateEntry.delete(0,END)
     cityEntry.delete(0,END)

# 建立上層插入項目
stateLeb = Label(root,text="State:")
stateLeb.grid(row=0,column=0,padx=5,pady=3,sticky=W)
stateEntry = Entry()
stateEntry.grid(row=0,column=1,sticky=W+E,padx=5,pady=3)
cityLab = Label(root,text="City:")
cityLab.grid(row=0,column=3,sticky=E)
cityEntry = Entry()
cityEntry.grid(row=0,column=3,sticky=W+E,padx=5,pady=3)
 
# 建立Insert按鈕
InBtn = Button(root,text="插入",command=insertItem)
InBtn.grid(row=0,column=4,padx=5,pady=3)

22.7 雙擊項目

def doubleClick(event):
     e = event.widget #取得事件控件
     iid = e.identify("item",event.x,event.y) #取得雙擊項目id
     state = e.item(iid,"text") #取得State
     city = e.item(iid,"values")[0] #取得City
     Str = "{0}:{1}".format(state,city) #格式化
     messagebox.showinfo("Double Clicked",Str) # 輸出

tree.bind("<Double-1>",doubleClick)

image

22.8 綁定滾動條

tree = Treeview(root, columns = ("cities"), selectmode = EXTENDED)
yscrollbar = Scrollbar(root)
yscrollbar.pack(side = RIGHT, fill = Y)
tree.pack()
yscrollbar.config(command = tree.yview)
tree.configure(yscrollcommand = yscrollbar.set)

22.9 排序

單擊欄標題可以排序,再次單擊反向排序。

def treeview_sortSolumn(col):
    global reverseFlag
    lst = [(tree.set(st, col), st) for st in tree.get_children("")]
    #print(lst)
    lst.sort(reverse = reverseFlag)
    #print(lst)
    for index, item in enumerate(lst):
        tree.move(item[1], "", index)
    reverseFlag = not reverseFlag

root = Tk()
root.title("Ex")

states = {
        "伊利諾", "加州", "德州", "華盛頓", "江蘇", "山東", "廣東", "福建"
        }

tree = Treeview(root, columns = ("states"), \
                show = "headings", selectmode = EXTENDED)
tree.pack()
tree.heading("#1", text = "State", \
             command = lambda c = "states": treeview_sortSolumn(c))
for state in states:
    tree.insert("", index = END, values = (state,))
root.mainloop()

23. Canvas

這個模塊可以繪圖,也可以制作動畫,而動畫也是設計游戲的基礎。

23.1 繪圖功能

1. 建立畫布

canvas = Canvas(父對象, width = xx, height = yy)

畫布建立完成后,左上角是坐標(0,0),向右x軸遞增,向下y軸遞增。

2. create_line()

繪制線條。

create_line(x1, y1, x2, y2, …, xn, yn, options)

options:

  • arrow:默認是沒有箭頭,使用arrow = FIRST在起始線末端有箭頭,arrow = LAST在最后一條線末端有箭頭,使用arrow = BOTH在兩端有箭頭;
  • arrowshape:使用元組(d1, d2, d3)代表三角形三邊長度來控制箭頭形狀,默認是(8, 10, 3);

    image

  • capstyle:線條終點的樣式,默認為BUTT,可以選擇PROJECTING,ROUND;
  • image

  • dash:建立虛線,使用元組存儲數字數據, 第一個數字是實線,第二個數字是空白,如此循環完當前所有元組數字又重新開始;
  • dashoffset:與dash一樣產生虛線,但是一開始是空白的寬度;
  • fill:設置線條顏色;
  • joinstyle:線條相交的設置,默認為ROUND,也可以選擇BEVEL、MITER;

    image

  • stipple:繪制位圖線條;
  • width:線條寬度。

import math
root = Tk()
root.title("Ex")

canvas = Canvas(root, width = 640, height = 480)
canvas.pack()
x_center, y_center, r = 320, 240, 100
x, y = [], []
for i in range(12):
    x.append(x_center + r * math.cos(i*math.pi/6))
    y.append(y_center + r * math.sin(i*math.pi/6))
for i in range(12):
    for j in range(12):
        canvas.create_line(x[i], y[i], x[j], y[j])
root.mainloop()

image

3. create_rectangle()

繪制矩形。

create_rectangle(x1, y1, x2, y2, options)

  • fill:為矩陣填充顏色;
  • outline:設置矩陣線條顏色。

4. create_arc()

繪制圓弧。

create_arc(x1, y1, x2, y2, extent = angle, options)

  • extent:1~359,繪制圓弧的角度;
  • start:圓弧起點位置;
  • style:ARC、CHORD、PIESLICE。

繪制方向是逆時針。

5. create_oval()

繪制原或橢圓。

create_oval(x1, y1, x2, y2, options)

6. create_polygon()

繪制多邊形。

create_polygon(x1, y1, x2, y2, …, xn, yn, options)

7. create_text()

輸出文字。

create_text(x, y, text = "字符串", options)

(x, y)是字符串輸出的中心坐標。

  • anchor:默認為CENTER;
  • fill,font,justify,stipple,text,width。

8. 畫布色彩

Canvas(…, bg = "yellow")

9. 插入圖像

create_image(x, y, options)

(x, y)是圖像左上角的位置。

23.2 鼠標拖曳繪制線條

圓的左上角坐標與右下角坐標相同,可以認為是點(極小化的圓)。

def paint(event):
    x, y = (event.x, event.y)
    canvas.create_oval(x, y, x, y, fill = "blue")
def clear():
    canvas.delete("all")
root = Tk()
root.title("Ex")
canvas = Canvas(root, width = 640, height = 300)
canvas.pack()
btn = Button(root, text = "清除", command = clear)
btn.pack(pady = 5)
canvas.bind("<B1-Motion>", paint)
root.mainloop()

image

23.3 動畫設計

1. 基本動畫

動畫設計所使用的的方法是move()。

canvas.move(id, xMove, yMove) #id是對象標號

canvas.update() #重繪畫布

xMove、yMove分別是對象沿x和y軸移動的距離,單位是像素。

import time

root = Tk()
root.title("Ex")

canvas = Canvas(root, width = 500, height = 150)
canvas.pack()
canvas.create_oval(10, 50, 60, 100, fill = "yellow", outline = "blue")
for x in range(0,80):
    canvas.move(1, 5, 0)
    root.update()
    time.sleep(0.05)

root.mainloop()

image

2. 反彈球游戲

from tkinter import *
from random import *
import time

class Ball:
    def __init__(self, canvas, color, winW, winH, racket):
        self.canvas = canvas
        self.racket = racket
        self.id = canvas.create_oval(winW/2-10, winH/2-10, \
                                     winW/2+10, winH/2+10, fill  = color)
        self.x = step
        self.y = -step
        self.notTouchBottom = True
    def hitRacket(self, ballPos):
        racketPos = self.canvas.coords(self.racket.id)
        if ballPos[2] >= racketPos[0] and ballPos[0] <= racketPos[2]:
            if ballPos[3] >= racketPos[1] and ballPos[3] <= racketPos[3]:
                return True
        return False
    def ballMove(self):
        self.canvas.move(self.id, self.x, self.y)
        ballPos = self.canvas.coords(self.id)
        if ballPos[0] <= 0:
            self.x = step
        if ballPos[1] <= 0:
            self.y = step
        if ballPos[2] >= winW:
            self.x = -step
        if ballPos[3] >= winH:
            self.y = -step
        if self.hitRacket(ballPos) == True:
            self.y = -step
        if ballPos[3] >= winH:
            self.notTouchBottom = False

class Racket:
    def __init__(self, canvas, color):
        self.canvas = canvas
        self.id = canvas.create_rectangle(270, 400, 370, 415, fill = color)
        self.x = 0
        self.canvas.bind_all("<KeyPress-Right>", self.moveRight)
        self.canvas.bind_all("<KeyPress-Left>", self.moveLeft)
    def racketMove(self):
        self.canvas.move(self.id, self.x, 0)
        racketPos = self.canvas.coords(self.id)
        if racketPos[0] <= 0:
            self.x = 0
        elif racketPos[2] >= winW:
            self.x = 0
    def moveLeft(self, event):
        self.x = -3
    def moveRight(self, event):
        self.x = 3

#定義畫布尺寸
winW = 640
winH = 480
#定義位移步長
step = 3
#定義移動速度
speed = 100

root = Tk()
root.title("Bouncing Ball")
root.wm_attributes('-topmost', 1) #確保游戲窗口在屏幕最上層
canvas = Canvas(root, width = winW, height = winH) #建立畫布
canvas.pack()
root.update()

racket = Racket(canvas, "purple")
ball = Ball(canvas, "yellow", winW, winH, racket)

while ball.notTouchBottom: #如果球未接觸畫布底端
    try:
        ball.ballMove()
    except:
        print("單擊關閉按鈕終止程序執行")
        break
    racket.racketMove()
    root.update()
    time.sleep(1/speed)

root.mainloop()


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM