PyScript詳解:如何在瀏覽器上直接執行Python程式碼

PyScript詳解:如何在瀏覽器上直接執行Python程式碼

文章目录

  • 什麼是PyScript
  • 如何在Web應用中使用PyScript?
  • 步驟 1:訪問官方網站
  • 步驟 2:設定基本HTML檔案
  • 步驟 3:在瀏覽器中開啟HTML檔案
  • PyScript動手實踐
  • 步驟 1:更新main.py
  • 步驟 2:建立CSS檔案
  • 步驟 3:更新index.html
  • 步驟 4:更新pyscript.toml
  • 小結

PyScript詳解:如何在瀏覽器上直接執行Python程式碼
近年來,Python 已成為最廣泛使用的程式語言之一。然而,直到現在,Python 在 Web 開發領域才真正發揮了重要作用。PyScript 的出現改變了這一現狀。它是一個全新的框架,允許您僅使用 HTML 和 Python 程式碼即可在 Web 瀏覽器上直接執行 Python 程式碼。無論您的經驗水平如何,使用 PyScript 開發互動式 Web 應用都非常便捷,無需瞭解 JavaScript。在本教程中,您將瞭解 PyScript 的概念、工作原理以及如何使用它建立您的第一個基於瀏覽器的 Python 應用。

什麼是PyScript

PyScript 是一個開源框架,它彌合了 Python 和 Web 之間的差距。它允許您直接在 Web 瀏覽器中執行 Python 程式碼。它允許您編寫完全在客戶端執行的互動式 Python 應用程式,而無需後端伺服器。使用 PyScript 就像使用 Python 而不是 JavaScript 編寫 Web 應用一樣。您可以使用 Python 構建簡單的互動式 Web 工具、儀表盤等。

PyScript 的主要功能

  1. 瀏覽器中的 Python:您可以在 HTML 檔案中的 <py-script> 標籤內編寫 Python 程式碼
  2. 無需環境設定:無需安裝任何其他庫或工具。它在瀏覽器中執行。
  3. 與 HTML 互動:輕鬆將 Python 與 HTML、CSS 和 JavaScript 整合。
  4. 基於 WebAssembly:使用 Pyodide(將 Python 編譯為 WebAssembly)在瀏覽器中執行 Python。

如何在Web應用中使用PyScript?

步驟 1:訪問官方網站

訪問官方網站。在這裡,您可以瀏覽演示、文件並親自嘗試。

PyScript官網

Source: PyScript

PyScript專案

步驟 2:設定基本HTML檔案

要執行 PyScript,您需要一個包含所需框架的簡單 HTML 檔案。

示例程式碼:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>My First PyScript App</title>
    <link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
    <script defer src="https://pyscript.net/latest/pyscript.js"></script>
  </head>
  <body>
    <h1>Hello from PyScript!</h1>
    <py-script>
      name = "PyScript"
      print(f"Hello, {name}! You are running Python in the browser.")
    </py-script>
  </body>
</html>

步驟 3:在瀏覽器中開啟HTML檔案

預設情況下,該檔案包含 3 個檔案:

main.py:

Your Python code.

Index.html:

The main web page that includes PyScript.

pyscript.toml:

A configuration file listing any extra Python packages youwant to use.

使用適當的程式碼更新程式碼檔案並開始實驗:

在瀏覽器中開啟HTML檔案

您可以在 PyScript 示例中嘗試 PyScript Playground 以直接在瀏覽器中測試程式碼片段。

測試程式碼片段

PyScript動手實踐

現在您已經熟悉了 PyScript 介面的工作原理,讓我們來實際操作一下。

我們將構建一個雙人井字棋遊戲。

步驟 1:更新main.py

在 main.py 檔案中新增 TicTacToe 類,該類包含遊戲邏輯、使用者互動和 UI 更新。它將使用 PyWeb 將 Python 與 HTML 連線起來,使遊戲在瀏覽器中完全可互動。

程式碼:

from pyweb import pydom
class TicTacToe:
    def __init__(self):
        self.board = pydom["table#board"]
        self.status = pydom["h2#status"]
        self.console = pydom["script#console"][0]
        self.init_cells()
        self.init_winning_combos()
        self.new_game(...)
    def set_status(self, text):
        self.status.html = text
    def init_cells(self):
        self.cells = []
        for i in (0, 1, 2):
            row = []
            for j in (0, 1, 2):
                cell = pydom[f"div#cell{i}{j}"][0]
                assert cell
                row.append(cell)
            self.cells.append(row)
    def init_winning_combos(self):
        self.winning_combos = []
        # winning columns
        for i in (0, 1, 2):
            combo = []
            for j in (0, 1, 2):
                combo.append((i, j))
            self.winning_combos.append(combo)
        # winning rows
        for j in (0, 1, 2):
            combo = []
            for i in (0, 1, 2):
                combo.append((i, j))
            self.winning_combos.append(combo)
        # winning diagonals
        self.winning_combos.append([(0, 0), (1, 1), (2, 2)])
        self.winning_combos.append([(0, 2), (1, 1), (2, 0)])
    def new_game(self, event):
        self.clear_terminal()
        print('=================')
        print('NEW GAME STARTING')
        print()
        for i in (0, 1, 2):
            for j in (0, 1, 2):
                self.set_cell(i, j, "")
        self.current_player = "x"
experimenting        self.set_status(f'{self.current_player} playing...')
    def next_turn(self):
        winner = self.check_winner()
        if winner == "tie":
            self.set_status("It's a tie!")
            self.current_player = "" # i.e., game ended
            return
        elif winner is not None:
            self.set_status(f'{winner} wins')
            self.current_player = "" # i.e., game ended
            return
        if self.current_player == "x":
            self.current_player = "o"
        else:
            self.current_player = "x"
        self.set_status(f'{self.current_player} playing...')
    def check_winner(self):
        """
        Check whether the game as any winner.
        Return "x", "o", "tie" or None. None means that the game is still playing.
        """
        # check whether we have a winner
        for combo in self.winning_combos:
            winner = self.get_winner(combo)
            if winner:
                # highlight the winning cells
                for i, j in combo:
                    self.cells[i][j].add_class("win")
                return winner
        # check whether it's a tie
        for i in (0, 1, 2):
            for j in (0, 1, 2):
                if self.get_cell(i, j) == "":
                    # there is at least an empty cell, it's not a tie
                    return None # game still playing
        return "tie"
    def get_winner(self, combo):
        """
        If all the cells at the given points have the same value, return it.
        Else return "".
        Each point is a tuple of (i, j) coordinates.
        Example:
            self.get_winner([(0, 0), (1, 1), (2, 2)])
        """
        assert len(combo) == 3
        values = [self.get_cell(i, j) for i, j in combo]
        if values[0] == values[1] == values[2] and values[0] != "":
            return values[0]
        return ""
    def set_cell(self, i, j, value):
        assert value in ("", "x", "o")
        cell = self.cells[i][j]
        cell.html = value
        if "x" in cell.classes:
            cell.remove_class("x")
        if "o" in cell.classes:
            cell.remove_class("o")
        if "win" in cell.classes:
            cell.remove_class("win")
        if value != "":
            cell.add_class(value)
    def get_cell(self, i, j):
        cell = self.cells[i][j]
        value = cell.html
        assert value in ("", "x", "o")
        return value
    def click(self, event):
        i = int(event.target.getAttribute('data-x'))
        j = int(event.target.getAttribute('data-y'))
        print(f'Cell {i}, {j} clicked: ', end='')
        if self.current_player == "":
            print('game ended, nothing to do')
            return
        #
        value = self.get_cell(i, j)
        if value == "":
            print('cell empty, setting it')
            self.set_cell(i, j, self.current_player)
            self.next_turn()
        else:
            print(f'cell already full, cannot set it')
    def clear_terminal(self):
        self.console._js.terminal.clear()
    def toggle_terminal(self, event):
        hidden = self.console.parent._js.getAttribute("hidden")
        if hidden:
            self.console.parent._js.removeAttribute("hidden")
        else:
            self.console.parent._js.setAttribute("hidden", "hidden")
GAME = TicTacToe()

步驟 2:建立CSS檔案

在新建的 assets 資料夾中建立一個 style.css 檔案,用於定義井字遊戲的佈局和樣式。這將處理棋盤、單元格以及所有狀態訊息的樣式。

程式碼:

h1, h2 {
    font-family: 'Indie Flower', 'Comic Sans', cursive;
    text-align: center;
}
#board {
    font-family: 'Indie Flower', 'Comic Sans', cursive;
    position: relative;
    font-size: 120px;
    margin: 1% auto;
    border-collapse: collapse;
}
#board td {
    border: 4px solid rgb(60, 60, 60);
    width: 90px;
    height: 90px;
    vertical-align: middle;
    text-align: center;
    cursor: pointer;
}
#board td div {
    width: 90px;
    height: 90px;
    line-height: 90px;
    display: block;
    overflow: hidden;
    cursor: pointer;
}
.x {
    color: darksalmon;
    position: relative;
    font-size: 1.2em;
    cursor: default;
}
.o {
    color: aquamarine;
    position: relative;
    font-size: 1.0em;
    cursor: default;
}
.win {
    background-color: beige;
}

步驟 3:更新index.html

修改 index.html 檔案,使其引用 PyScript 設定,載入 main.py 檔案,定義遊戲棋盤結構,並指向 style.css 檔案(位於 assets 資料夾中)進行樣式設定。

程式碼:

<!doctype html>
<html>
    <head>
        <!-- Recommended meta tags -->
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width,initial-scale=1.0">
        <!-- PyScript CSS -->
        <link rel="stylesheet" href="https://pyscript.net/releases/2024.1.1/core.css">
        <!-- CSS for examples -->
        <link rel="stylesheet" href="./assets/css/examples.css" />
        <!-- This script tag bootstraps PyScript -->
        <script type="module" src="https://pyscript.net/releases/2024.1.1/core.js"></script>
        <!-- Custom CSS -->
        <link href="https://fonts.googleapis.com/css?family=Indie+Flower" rel="stylesheet">
        <link rel="stylesheet" href="./assets/css/tictactoe.css" />
        <!-- for splashscreen -->
        <style>
            #loading { outline: none; border: none; background: transparent }
        </style>
        <script type="module">
            const loading = document.getElementById('loading');
            addEventListener('py:ready', () => loading.close());
            loading.showModal();
        </script>
        <title>Tic Tac Toe</title>
        <link rel="icon" type="image/png" href="./assets/favicon.png" />
    </head>
    <body>
        <dialog id="loading">
            <h1>Loading...</h1>
        </dialog>
        <nav class="navbar" style="background-color: #000000">
            <div class="app-header">
                <a href="/">
                    <img src="./assets/logo.png" class="logo" />
                </a>
                <a class="title" href="" style="color: #f0ab3c">Tic Tac Toe</a>
            </div>
        </nav>
        <section class="pyscript">
            <h1>Tic-Tac-Toe</h1>
            <script type="py" src="./main.py" config="./pyscript.toml"></script>
            <table id="board">
                <tr>
                    <td><div id="cell00" data-x="0" data-y="0" class="cell" py-click="GAME.click"></div></td>
                    <td><div id="cell01" data-x="0" data-y="1" class="cell" py-click="GAME.click"></div></td>
                    <td><div id="cell02" data-x="0" data-y="2" class="cell" py-click="GAME.click"></div></td>
                <tr>
                    <td><div id="cell10" data-x="1" data-y="0" class="cell" py-click="GAME.click"></div></td>
                    <td><div id="cell11" data-x="1" data-y="1" class="cell" py-click="GAME.click"></div></td>
                    <td><div id="cell12" data-x="1" data-y="2" class="cell" py-click="GAME.click"></div></td>
                </tr>
                <tr>
                    <td><div id="cell20" data-x="2" data-y="0" class="cell" py-click="GAME.click"></div></td>
                    <td><div id="cell21" data-x="2" data-y="1" class="cell" py-click="GAME.click"></div></td>
                    <td><div id="cell22" data-x="2" data-y="2" class="cell" py-click="GAME.click"></div></td>
                </tr>
            </table>
            <h2 id="status"></h2>
            <button id="btn-new-game" py-click="GAME.new_game">New game</button>
            <button id="btn-toggle-terminal" py-click="GAME.toggle_terminal">Hide/show terminal</button>
            <div id="terminal" hidden="hidden">
                <script id="console" type="py" terminal></script>
            </div>
        </section>
    </body>
</html>

步驟 4:更新pyscript.toml

使用應用所需的必要配置(包括依賴項、檔案路徑等)更新 pyscript.toml 檔案。這可確保 PyScript 知道如何正確載入和執行 Python 程式碼。以下是我們的井字遊戲應用的 pyscript.toml 檔案內容:

配置:

name = "Tic Tac Toe"
description = "A Tic-Tac-Toe game written in PyScript that allows people to take turns."

輸出:

這是您在 PScript 上的第一個專案。

小結

Python 在資料科學、人工智慧、自動化和教育領域的應用前所未有。然而,迄今為止,Python 在 Web 上尚無原生平臺。PyScript 應運而生,它將 Python 的簡潔性與 Web 的易用性完美融合。它仍在不斷完善,但已經為開發者、教育工作者和學習者創造了大量機會。

評論留言