tkinter 的拓展包:tkinterx

我在 GitHub 放置一個名為 xinetzone / pychaos 的項目,該項目以 tkinter 為基礎(chǔ)研究如何使用 Python 開發(fā) GUI 接口。該項目已經(jīng)提供了 PyPI 接口,您可以使用如下命令進(jìn)行安裝:

pip install tkinterx

下面我們逐步展開此庫的使用細(xì)節(jié)。

1 更加友好的畫圖操作

tkinterx 提供了 CanvasMeta 來代替 tkinter 的 Canvas 進(jìn)行畫圖。

def test_Meta():
    from tkinter import Tk
    from tkinterx.graph.canvas import CanvasMeta
    root = Tk()
    root.columnconfigure(0, weight=1)
    root.rowconfigure(0, weight=1)
    self = CanvasMeta(root)
    kw = {
        'color': 'purple',
        'dash': 2,
        'width': 3,
    }
    self.create_graph('line', [20, 20, 100, 200], **kw)
    self.create_graph('oval', [50, 80, 100, 200], fill='red', **kw)
    self.create_graph('rectangle', [170, 80, 220, 200], fill='yellow', **kw)
    self.create_graph('arc', [180, 100, 250, 260],
                      tags='test',
                      fill='lightblue', style='chord', **kw)
    self.create_graph('polygon', [(270, 80), (220, 170), (230, 90)], fill='blue', **kw)
    self.create_graph('polygon', ((70, 80), (20, 70), (30, 90)), fill='purple', **kw)
    self.grid(row=0, column=0)
    print((self.gettags(1)))
    print((self.find_withtag('test')))
    root.mainloop()

CanvasMeta 提供了一個統(tǒng)一的 2D 畫圖接口,create_graph(graph_type, directions, color='blue', width=1, tags=None, **kwargs)。

  • graph_type:指定畫圖的類型,有 'rectangle', 'oval', 'line', 'arc', 'polygon'。
  • directions:指定您要畫圖形的對角線的方向向量 d = (x_0, y_0, x_1, y_1),其中 (x_0, y_0)(x_1, y_1) 分別表示圖形的左上角與右下角坐標(biāo)。graph_type 為 'polygon' 的圖形則可以 *points 形式指定 directions 的值。
  • color:表示圖形的顏色。
  • width:表示圖形的寬度。
  • tags:表示圖對象的標(biāo)識 id 綁定的標(biāo)簽信息。比如 ['line', 'graph'],('test', 'graph'), 'line''line graph'。需要注意的是,像 '1'、'1 2 2' 這種純數(shù)字的標(biāo)簽是無效的。如果 tagsNone 則默認(rèn)添加標(biāo)簽 [graph_type, 'graph']
  • fill:表示圖形的填充顏色,由于 'line' 無法填充,所以此參數(shù)對于 graph_type'line' 的圖形無效。

下圖1 展示了圖形效果:

圖1 畫出幾個不同的圖形

2 可傳遞值的窗體

先看一個例子:

import json
from tkinter import Tk, StringVar, ttk
from tkinterx.meta import WindowMeta, ask_window, askokcancel, showwarning


class Window(WindowMeta):
    def __init__(self, master=None, cnf={}, **kw):
        super().__init__(master, cnf, **kw)

    def create_widget(self):
        self.add_row('Please enter your name:', 'name')
        self.add_row('Please enter your age:', 'age')
        self.add_row('Enter your information saving path:', 'save_path')

    def save(self, path):
        table = self.table.todict()
        with open(path, 'w') as fp:
            json.dump(table, fp)

    def run(self):
        self.withdraw()
        name = self.table['name']
        age = self.table['age']
        save_path = str(self.table['save_path'])
        if '' in [name, age, save_path]:
            showwarning(self)
        else:
            self.save(save_path)
            askokcancel(self)


class Root(Tk):
    def __init__(self):
        super().__init__()
        self.label_var = StringVar()
        self.create_widgets()
        self.layout()

    def create_buttons(self):
        style = ttk.Style()
        style.configure("C.TButton",
                        foreground="green",
                        background="white",
                        relief='raise',
                        justify='center',
                        font=('YaHei', '10', 'bold'))
        self.table_button = ttk.Button(self, text='Fill in your name and age:',
                                       command=self.ask_table,
                                       style="C.TButton")

    def create_widgets(self):
        self.create_buttons()
        self.label = ttk.Label(self, textvariable=self.label_var)

    def ask_table(self):
        bunch = ask_window(self, Window)
        name, age = bunch['name'], bunch['age']
        self.label_var.set(f"{name}: {age}")

    def layout(self):
        self.table_button.pack()
        self.label.pack()


if __name__ == "__main__":
    root = Root()
    root.geometry('300x200')
    root.mainloop()

輸出的界面為:

圖2 可傳遞值的窗體

該例子使用了可定制的窗體: WindowMeta,該類存在實例方法 add_row(text, key) 可以用于創(chuàng)建“行數(shù)據(jù)”,即 text: key 形式的 ttk 小部件。其中 text、key 分別使用 ttk.Labelttk.Entry 小部件。對于 key 如果設(shè)定為 *path、*dir 則會在您使用鼠標(biāo)點擊其對應(yīng)的 text 時分別打開文件選擇器與文件夾選擇器。

WindowMeta 中用戶傳入的值均被記錄在其 table 屬性字典之中,可以被其他窗體獲取。除此之外,WindowMeta 還有兩個關(guān)鍵的實例方法 run(與 ok 按鈕綁定)與 create_widget(用于創(chuàng)建小部件),需要使用者自行重載,就像 Window 之中的那樣設(shè)計即可。

為了在不同窗體之間傳遞用戶傳入的信息,還需要借助 ask_window 函數(shù),比如 Root 的實例方法 ask_table 中的 bunch = ask_window(self, Window) 操作。

3 按行或者列創(chuàng)建圖形對象

from tkinterx.param import ParamDict
from tkinterx.graph.canvas import CanvasMeta
from tkinter import Tk


class SimpleGraph(CanvasMeta):
    color = ParamDict()
    shape = ParamDict()

    def __init__(self, master, shape, color, cnf={}, **kw):
        '''The base class of all graphics frames.

        :param master: a widget of tkinter or tkinter.ttk.
        '''
        super().__init__(master, cnf, **kw)
        self.color = color
        self.shape = shape

    def draw(self, direction, width=1, tags=None, **kw):
        kw.update({'color': self.color, 'width': width, 'tags': tags})
        return self.create_graph(self.shape, direction, **kw)

    def add_row(self, direction, num, stride=10, width=1, tags=None, **kw):
        x0, y0, x1, y1 = direction
        stride = x1 - x0 + stride
        for k in range(num):
            direction = [x0+stride*k, y0, x1+stride*k, y1]
            self.draw(direction, width=width, tags=tags, **kw)

    def add_column(self, direction, num, stride=5, width=1, tags=None, **kw):
        x0, y0, x1, y1 = direction
        stride = y1 - y0 + stride
        for k in range(num):
            direction = [x0, y0+stride*k, x1, y1+stride*k]
            self.draw(direction, width=width, tags=tags, **kw)


if __name__ == "__main__":
    root = Tk()
    self = SimpleGraph(root, 'rectangle', 'red')
    self.add_row([15, 15, 40, 40], 10)
    self.add_column([15, 45, 40, 80], 5)
    self.grid()
    root.mainloop()

效果圖:

圖3 按行或者列創(chuàng)建圖形對象

注意 add_rowadd_column 的參數(shù)均是: direction, num, stride=10, width=1, tags=None, **kw。其中 num 表示行數(shù)或者列數(shù),stride 表示圖形間隔的像素個數(shù)。其余參數(shù)同 CanvasMetadraw_graph 函數(shù)的參數(shù)。

在 tkinterx 中定制了一個可以修改形狀、顏色、填充、輪廓寬度的工具:

from tkinter import Tk
from tkinterx.graph.canvas_design import SimpleGraph

root = Tk()
self = SimpleGraph(root, 'rectangle', 'yellow', width=1, fill=None, background='pink')
self.add_row([25, 25, 40, 40], 10, 20)
self.fill = 'blue' # 修改填充顏色
self.add_column([40, 80, 100, 100], 5, 30, tags='TY') # 自定義標(biāo)簽
self.grid(row=0, column=0)
root.mainloop()

輸出界面:

圖4 可以修改圖形屬性的工具

也可以畫出規(guī)則的圖形:

from tkinter import Tk
from tkinterx.graph.canvas_design import RegularGraph
root = Tk()
self = RegularGraph(root, 'circle', 'yellow', width=7, fill=None, background='pink')
self.fill = 'red'
self.draw([140, 140], 40, tags='DF', activedash=7,
          activeoutlinestipple='error', activeoutline='red')
self.add_row([75, 45], 20, 10)
self.width = 0
self.fill = 'blue'
self.add_column([40, 80], 20, 5)
self.shape = 'square'
self.width = 5
self.add_column([240, 20], radius=10, num=7, stride=25)
self.grid(row=0, column=0)
root.mainloop()
圖5 畫出正方形與圓形

4 創(chuàng)建幾何畫板

首先,創(chuàng)建了一個用于選擇圖形的形狀和顏色的面板:

from tkinter import Tk
from tkinterx.graph.canvas_design import Selector
root = Tk()
select = Selector(root, background='skyblue')
select.grid()
root.mainloop()

界面圖:

圖6 選擇圖形的形狀和顏色的面板

創(chuàng)建畫圖面板

from tkinterx.graph.painter import GraphMeta
from tkinter import Tk

root = Tk()
selector = Selector(root, background='skyblue')
self = GraphMeta(root, selector)
self.bind_drawing()
selector.grid()
self.grid()
root.mainloop()

效果圖:

圖7 幾何畫板
from tkinter import Tk
from tkinterx.graph.canvas_design import Selector
from tkinterx.graph.painter import GraphPainter

class DrawingWindow(Tk):
    def __init__(self, **win_kw):
        super().__init__(**win_kw)
        self.selector = Selector(self, background='skyblue', width=350, height=90)
        self.painter = GraphPainter(self, self.selector, background='pink')
        self.painter.bind_drawing()
        self.painter.bind_master()

    def layout(self, row=0, column=0):
        self.selector.grid(row=row, column=column)
        self.painter.grid(row=row+1, column=column, sticky='nesw')
        
self = DrawingWindow()
self.layout(row=0, column=0)
self.mainloop()

DrawingWindow 設(shè)定了一些鼠標(biāo)與鍵盤的事件綁定。使用實例變量 record_bbox=[x0, y0, x1, y1] 追蹤畫布上的鼠標(biāo)位置,其中 (x0, y0) 記錄點擊鼠標(biāo)左鍵觸發(fā)的位置,而 (x1, y1) 則記錄鼠標(biāo)移動的實時位置。當(dāng)鼠標(biāo)左鍵釋放后,(x0, y0) 設(shè)定為 ['none']*2。該畫筆支持使用 F1 清空畫布;支持 Ctrl+a 選中畫布全部圖形,然后使用鼠標(biāo)進(jìn)行整體移動;支持使用鼠標(biāo)左鍵選中圖形并拖動到其他位置;支持將鼠標(biāo)移動到圖形內(nèi),使用 Del 按鍵進(jìn)行刪除。

使用鼠標(biāo)作圖的過程中,圖形的邊框是加粗的虛線,離開圖形之后,變成實線??梢酝ㄟ^下方的選擇器切換不同顏色(可自定義)的畫筆以及畫的圖形形狀,也支持使用鍵盤的方向鍵移動鼠標(biāo)指針位置選中的圖形。

圖8 可修改圖形的幾何畫板
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容