[Python] GUI examples (Tkinter Tutorial)

這個 Tkinter 很早就想學了,今天來練習看看。

教學網站:

Create your first GUI application

First, we will import Tkinter package and create a window and set its title:

from tkinter import *
window = Tk()
window.title("Welcome to Max app")
window.mainloop()

The result will be like this:

請服用下面的 code 讓程式在 python2 & python3 都可以run:

try:
# for Python2
from Tkinter import *
import ttk
import tkMessageBox as messagebox
except ImportError:
# for Python3
from tkinter import *
from tkinter import ttk
from tkinter import messagebox

Create a label widget

下面的範例是按了button 去修改 label 裡的值。

!/usr/bin/env python
encoding=utf-8
from tkinter import *
window = None
lbl = None
btn = None
def btn_clicked():
global lbl
lbl.config(text="Button was clicked !!")
def MainMenu(root):
global lbl
lbl = Label(root, text="Hello World", font=("Arial Bold", 50))
lbl.grid(column=0, row=0)
btn = Button(root, text="Click Me", command=btn_clicked)btn.grid(column=1, row=0)
def main():
global root
root = Tk()
root.title("Welcome to Max app")
GUI = MainMenu(root)
root.geometry('450x400')
root.mainloop()
if name == "main":
main()

執行畫面:

說明: 使用 .grid() 可以把元件加入到 layout 裡, 使用 .grid_forget() 可以 hide。

Tkinter: how to show and hide Frame class:

f will be a class, not an instance. Since self.frames is a dictionary with the actual frames being the values, you want to loop over the values:

for f in self.frames.values():
    f.grid_forget()

Get input using Entry class (Tkinter textbox)

sample code:

txt = Entry(window,width=10)

放入預設值,參考這一篇:
https://stackoverflow.max-everyday.com/2018/12/python-how-to-set-default-text-for-a-tkinter-entry-widget/

取值出來:

if entryBox.get().strip()=="":
tkMessageBox.showerror("Error", "Please enter text")
else:
print entryBox.get().strip()

Add a Checkbutton widget (Tkinter checkbox)

範例:

chk_state = BooleanVar()
chk_state.set(True) #set check state
chk = Checkbutton(window, text='Choose', variable=chk_state)
chk.grid(column=0, row=0)

Set check state of a Checkbutton

chk_state = IntVar()
chk_state.set(0) #uncheck
chk_state.set(1) #check

值得注意的是 variable 的 IntVar() or BooleanVar() 有 variable scope 的問題,所以如果沒有設對的話,會變成 always fasle.

Getting Tkinter Check Box State

var = Tkinter.IntVar()
chk = Tkinter.Checkbutton(root, text='foo', variable=var)
var.get()

如果是要 bind checkbox 事件:

def callBackFunc():
    print "Oh. I'm clicked"
    
app = tk.Tk() 
app.geometry('150x100')

chkValue = tk.BooleanVar() 
chkValue.set(True)
 
chkExample = tk.Checkbutton(app, text='Check Box', 
                            var=chkValue, command=callBackFunc) 

Add a combobox

官方文件說明:
https://docs.python.org/3.1/library/tkinter.ttk.html

只使用 tkinter 沒有 combo 可以使用,To start using Ttk, import its module: (方案1號)

from tkinter import ttk

To override the basic Tk widgets, the import should follow the Tk import: (方案2號)

from tkinter import *
from tkinter.ttk import *

That code causes several tkinter.ttk widgets (Button, Checkbutton, Entry, Frame, Label, LabelFrame, Menubutton, PanedWindow, Radiobutton, Scale and Scrollbar) to automatically replace the Tk widgets.

說明:這2個方案,各有優缺點,下去試看看就知道。偷懶的人建議使用方案1就好了,用方案2號優點就是畫面比較有「一致性」,主要差在 backgound 會不同。


sample code:

combo = Combobox(window)
combo['values']= (1, 2, 3, 4, 5, "Text")
combo.current(1) #set the selected item index
combo.grid(column=0, row=0)

說明: current 裡放的是 index 值,不是文字。如果要放文字可以使用 .set(“”

To get the select item, you can use the get function like this:

combo.get()

sample 2 號:

Old example from GitHub: combobox-get-selection

#!/usr/bin/env python3

import tkinter as tk
import tkinter.ttk as ttk

# --- functions ---

def on_select(event=None):
    print('----------------------------')

    if event: # <-- this works only with bind because `command=` doesn't send event
        print("event.widget:", event.widget.get())

    for i, x in enumerate(all_comboboxes):
        print("all_comboboxes[%d]: %s" % (i, x.get()))

# --- main ---

root = tk.Tk()

all_comboboxes = []

cb = ttk.Combobox(root, values=("1", "2", "3", "4", "5"))
cb.set("1")
cb.pack()
cb.bind('<<ComboboxSelected>>', on_select)

all_comboboxes.append(cb)

cb = ttk.Combobox(root, values=("A", "B", "C", "D", "E"))
cb.set("A")
cb.pack()
cb.bind('<<ComboboxSelected>>', on_select)

all_comboboxes.append(cb)

b = tk.Button(root, text="Show all selections", command=on_select)
b.pack()

root.mainloop()

Add radio buttons

sample code:

rad1 = Radiobutton(window,text='First', value=1)
rad2 = Radiobutton(window,text='Second', value=2)
rad3 = Radiobutton(window,text='Third', value=3)
rad1.grid(column=0, row=0)
rad2.grid(column=1, row=0)
rad3.grid(column=2, row=0)

Get radio button value

sample code:

selected = IntVar()
rad1 = Radiobutton(window,text='First', value=1, variable=selected)
rad2 = Radiobutton(window,text='Second', value=2, variable=selected)
rad3 = Radiobutton(window,text='Third', value=3, variable=selected)
def clicked():
print(selected.get())
btn = Button(window, text="Click Me", command=clicked)
rad1.grid(column=0, row=0)
rad2.grid(column=1, row=0)
rad3.grid(column=2, row=0)
btn.grid(column=3, row=0)

Theme 佈景主題

服用下面的範例切換主題:

import tkinter
from tkinter import ttk
root = tkinter.Tk()
style = ttk.Style(root)
style.theme_use('classic')
style.configure('Test.TLabel', background= 'red')
text = ttk.Label(root, text= 'Hello', style= 'Test.TLabel')
text.grid()
root.mainloop()

內建主題有 “classic”, “default”, “aqua” 可以試看看,自己建立套用主題參考:

Apparently, the order you set the properties of the new style is important to determine if a certain property of the new style will be applied or not. For example, if I set first the background instead of selectbackground, then the color of selection will not be changed, but just the mini button color with the arrow (to list down the options).

I noted also that depending on the value of parent, which I suppose is the parent stylefrom which the new style is derived, some of the new settings and properties of the new style might not be applied. For example, if I try to change the fieldbackground property when parent is set to aqua, it does not work, but if parent is set to alt, it works. (I hope more expert users can help and contribute to improve this answer, which could be helpful also for future users of ttk and tkinter).

This is my solution, where I created a complete new style:

import tkinter as tk
from tkinter import ttk

root = tk.Tk()

combostyle = ttk.Style()

combostyle.theme_create('combostyle', parent='alt',
                         settings = {'TCombobox':
                                     {'configure':
                                      {'selectbackground': 'blue',
                                       'fieldbackground': 'red',
                                       'background': 'green'
                                       }}}
                         )
# ATTENTION: this applies the new style 'combostyle' to all ttk.Combobox
combostyle.theme_use('combostyle') 

# show the current styles
# print(combostyle.theme_names())

combo = ttk.Combobox(root, values=['1', '2', '3'])
combo['state'] = 'readonly'
combo.pack()

entry = tk.Entry(root)
entry.pack()

root.mainloop()

Since I am not an expert on ttk, I was not able to apply a new theme just to a certain instance of type ttk.Combobox, but I applied the theme to all instances of future possible ttk.Combobox. If someone can improve this answer, I would really appreciate the gesture!

For more information on how to create and set new styles, see here or here.

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *