教學 2 - 讓它變得有趣

教程 1 中,我們產生了一個能夠運行的存根項目,但我們自己沒有寫任何程式碼。讓我們看看為我們產生了什麼。

生成了什麼

在 src/helloworld 目錄中,您應該會看到 3 個檔案:__init__.py、__main__.py 和 app.py。

__init__.py 將``helloworld`` 目錄標記為可匯入的Python 模組。這是一個空文件;它的存在告訴 Python 解釋器``helloworld``目錄定義了一個模組。

__main__.py 將``helloworld`` 模組標記為一種特殊類型的模組 - 可執行模組。如果您嘗試使用``python -m helloworld``來執行``helloworld``模組,則``__main__.py``檔案是Python將開始執行的位置。 __main__.py 的內容比較簡單:

from helloworld.app import main

if __name__ == "__main__":
    main().main_loop()

也就是說 - 它從``helloworld``應用程式導入``main``方法;如果它是作為入口點執行,則呼叫 main() 方法,並啟動應用程式的主循環。主循環是 GUI 應用程式偵聽使用者輸入(如滑鼠點擊和鍵盤按下)的方式。

更有趣的檔案是``app.py``——它包含創建我們的應用程式視窗的邏輯:

import toga
from toga.style import Pack
from toga.style.pack import COLUMN, ROW

class HelloWorld(toga.App):
    def startup(self):
        main_box = toga.Box()

        self.main_window = toga.MainWindow(title=self.formal_name)
        self.main_window.content = main_box
        self.main_window.show()

def main():
    return HelloWorld()

讓我們逐行瀏覽一下:

import toga
from toga.style import Pack
from toga.style.pack import COLUMN, ROW

首先,我們匯入``toga``小工具工具包,以及一些與樣式相關的實用程式類別和常數。我們的程式碼尚未使用它們 - 但我們很快就會使用它們。

然後,我們定義一個類別::

class HelloWorld(toga.App):

每個 Toga 應用程式都有一個``toga.App``實例,代表應用程式的運作實體。該應用程式最終可能會管理多個視窗;但對於簡單的應用程序,將有一個主視窗。

接下來,我們定義一個``startup()``方法:

def startup(self):
    main_box = toga.Box()

啟動方法所做的第一件事是定義一個主框。 Toga 的佈局方案的行為與 HTML 類似。您可以透過建立一組框來建立應用程序,每個框都包含其他框或實際的小部件。然後,您可以將樣式套用到這些方塊來定義它們將如何使用可用的視窗空間。

在此應用程式中,我們定義了一個框,但沒有在其中放入任何內容。

接下來,我們定義一個窗口,可以將這個空框放入其中:

self.main_window = toga.MainWindow(title=self.formal_name)

這將建立一個``toga.MainWindow``實例,它將具有與應用程式名稱相符的標題。主視窗是 Toga 中的一種特殊視窗 - 它是與應用程式的生命週期密切相關的視窗。當主視窗關閉時,應用程式退出。主視窗也是具有應用程式選單的視窗(如果您使用的是 Windows 等平台,其中功能表列是視窗的一部分)

然後,我們添加空框作為主視窗的內容,並指示應用程式顯示我們的視窗:

self.main_window.content = main_box
self.main_window.show()

最後,我們定義一個``main()``方法。這就是創建我們的應用程式實例的原因:

def main():
    return HelloWorld()

這個``main()``方法是由``__main__.py``導入和呼叫的方法。它創建並傳回我們的``HelloWorld``應用程式的實例。

這是最簡單的 Toga 應用程式。讓我們將一些我們自己的內容放入應用程式中,並使應用程式做一些有趣的事情。

添加一些我們自己的內容

修改``src/helloworld/app.py``中的``HelloWorld``類,使其看起來像這樣:

class HelloWorld(toga.App):
    def startup(self):
        main_box = toga.Box(style=Pack(direction=COLUMN))

        name_label = toga.Label(
            "Your name: ",
            style=Pack(padding=(0, 5)),
        )
        self.name_input = toga.TextInput(style=Pack(flex=1))

        name_box = toga.Box(style=Pack(direction=ROW, padding=5))
        name_box.add(name_label)
        name_box.add(self.name_input)

        button = toga.Button(
            "Say Hello!",
            on_press=self.say_hello,
            style=Pack(padding=5),
        )

        main_box.add(name_box)
        main_box.add(button)

        self.main_window = toga.MainWindow(title=self.formal_name)
        self.main_window.content = main_box
        self.main_window.show()

    def say_hello(self, widget):
        print(f"Hello, {self.name_input.value}")

備註

不要刪除文件頂部的導入或底部的``main()``。您只需要更新``HelloWorld``類別。

讓我們詳細看看發生了什麼變化。

我們仍在創建一個主盒子;然而,我們現在正在應用一種風格:

main_box = toga.Box(style=Pack(direction=COLUMN))

Toga 的內建佈局系統稱為``Pack``。它的行為很像 CSS。您可以在層次結構中定義物件 - 在 HTML 中,物件是``<div>``、`` <span>`` 和其他 DOM 元素;在 Toga 中,它們是小部件和盒子。然後,您可以為各個元素指定樣式。在這種情況下,我們表明這是一個``COLUMN``框 - 也就是說,它是一個將消耗所有可用寬度的框,並且會在添加內容時擴展其高度,但它會嘗試盡可能短。</span>

接下來,我們定義幾個小工具::

name_label = toga.Label(
    "Your name: ",
    style=Pack(padding=(0, 5)),
)
self.name_input = toga.TextInput(style=Pack(flex=1))

在這裡,我們定義了一個 Label 和一個 TextInput。這兩個小部件都有與之相關的樣式;標籤的左側和右側將有 5 個像素的內邊距,頂部和底部將沒有內邊距。 TextInput 被標記為靈活的 - 也就是說,它將吸收其佈局軸中的所有可用空間。

TextInput 被指定為該類別的實例變數。這使我們可以輕鬆存取小部件實例 - 我們稍後將使用它。

接下來,我們定義一個盒子來容納這兩個小工具:

name_box = toga.Box(style=Pack(direction=ROW, padding=5))
name_box.add(name_label)
name_box.add(self.name_input)

name_box 是一個像主盒子一樣的盒子;然而,這一次,它是一個``ROW``盒子。這意味著內容將水平添加,並且它將嘗試使其寬度盡可能窄。盒子還有一些內邊距 - 所有邊都是 5px。

現在我們定義一個按鈕:

button = toga.Button(
    "Say Hello!",
    on_press=self.say_hello,
    style=Pack(padding=5),
)

此按鈕的所有邊都有 5 像素的內邊距。我們也定義了一個*處理程序* - 按下按鈕時呼叫的方法。

然後,我們將名稱框和按鈕新增到主框:

main_box.add(name_box)
main_box.add(button)

這樣就完成了我們的佈局;啟動方法的其餘部分與之前一樣 - 定義一個 MainWindow,並將主框指定為視窗的內容:

self.main_window = toga.MainWindow(title=self.formal_name)
self.main_window.content = main_box
self.main_window.show()

我們需要做的最後一件事是定義按鈕的處理程序。處理程序可以是任何方法、生成器或非同步協程;它接受生成事件的小部件作為參數,並且每當按下按鈕時就會調用:

def say_hello(self, widget):
    print(f"Hello, {self.name_input.value}")

該方法的主體是一個簡單的列印語句 - 但是,它將詢問名稱輸入的當前值,並使用該內容作為列印的文字。

現在我們已經進行了這些更改,我們可以透過再次啟動應用程式來查看它們的外觀。和以前一樣,我們將使用開發者模式:

(beeware-venv) $ briefcase dev

[helloworld] Starting in dev mode...
===========================================================================

您會注意到,這一次,它``不``安裝依賴項。公文包可以檢測到該應用程式之前已經運行過,為了節省時間,只會運行該應用程式。如果您為應用程式新增的依賴項,則可以在執行``briefcase dev``時傳入``-r``選項來確保它們已安裝。

這應該打開一個 GUI 視窗:

Hello World 教學 2 窗口,在 macOS 上

如果您在文字方塊中輸入名稱,然後按 GUI 按鈕,您應該會看到啟動應用程式的控制台中出現輸出。

下一步

我們現在有了一個可以做一些更有趣的事情的應用程式。但它只能在我們自己的計算機上運行。讓我們打包這個應用程式以進行分發。在 教程 3 中,我們將把我們的應用程式打包為一個獨立的安裝程序,我們可以將其發送給朋友、客戶,或上傳到應用程式商店。