Hiển thị động các thẻ trong TinML với Python Tkinter

Trong quá trình kết xuất tài liệu bằng ngôn ngữ đánh dấu TinML, một tính năng quan trọng là cho phép người đọc tương tác với nội dung theo trình tự do tác giả sắp đặt. Điều này đạt được nhờ các thẻ hiển thị động, không giống với việc làm mới toàn bộ trình đọc sau mỗi lần kết xuất. Trong phiên bản mới của TinML, có một số thẻ đảm nhiệm chức năng này:

  • <part> – cho phép người đọc quyết định phần nào sẽ được hiển thị (đã được trình bày trong bài trước, không lặp lại ở đây).
  • <wait> – người đọc tự chọn thời điểm bắt đầu đọc phần tiếp theo (cũng đã được đề cập).
  • <pages> – chia nội dung thành nhiều tab, mỗi tab là một trang riêng biệt.
  • <ac> – đánh dấu neo, người đọc có thể nhảy đến vị trí tương ứng khi muốn.

Giải thích và kết xuất các thẻ hiển thị động

Các thẻ trên không thể phân biệt với các thẻ kết xuất thông thường hay thẻ điều khiển luồng trong quá trình phân tích cú pháp. Do đó, việc xử lý chúng hoàn toàn nằm trong bộ giải thích (interpreter) và bộ kết xuất (renderer). Phần này sẽ lấy <pages> làm ví dụ minh họa.

Lưu ý: Thẻ <part> thực chất thuộc nhóm thẻ điều khiển luồng vì nó có thể lồng nhau và người đọc có thể chọn kết xuất nội dung hay không. Trong khi đó, <pages> luôn được kết xuất bất kể người đọc có muốn xem hay không, nhưng họ có thể quyết định xem tab nào đã được kết xuất.

Khi gặp thẻ mở <pages>, bộ giải thích đặt cờ self._pages_active thành True. Trước khi khớp các thẻ và dấu hiệu thông thường, nội dung đa trang sẽ được phân tích. Theo quy tắc ưu tiên của TinML, thẻ điều khiển có độ ưu tiên cao hơn thẻ phần tử, nên việc xác định thẻ <pages> diễn ra sau khi xử lý <part>.

if self._pages_active:
    if unit[1] not in ('</pages>', '</page>'):
        if len(unit) == 3:
            tin_content = ''.join(unit[1:])
        else:
            tin_content = unit[1] + '|'.join(unit[2:])
        self._page_buffer.append(tin_content)
        continue

Mặc dù <pages> không cho phép lồng nhau trong cùng một nguồn văn bản, nó vẫn hỗ trợ phân chia nhiều trang. Khi gặp thẻ đóng </page>, nội dung đã đọc của trang hiện tại được kết xuất, sau đó xóa bộ đệm và tiếp tục đọc:

case '</page>':
    # Thẻ phân cách giữa các trang trong <pages>
    page_tin = '\n'.join(self._page_buffer)
    # Kết xuất trang, nhưng chặn luồng chính
    self._page_widgets[page_num].run_render(page_tin, block=True)
    self._page_buffer.clear()
    page_num += 1

Khi bộ giải thích bắt gặp thẻ kết thúc </pages> (cũng là thẻ đóng của trang cuối cùng), toàn bộ quá trình xử lý thẻ <pages> kết thúc. Sau khi kết xuất trang cuối, tất cả các trang đã hoàn tất:

case '</pages>':
    # Thẻ đóng <pages>
    page_tin = '\n'.join(self._page_buffer)
    self._page_widgets[page_num].run_render(page_tin, block=True)
    self._page_buffer.clear()
    # Lưu danh sách các trang và tên tab
    pages = self._page_widgets.copy()
    self._tinml.add_tag('<pages>', pages=pages, names=page_names)
    # Dọn dẹp
    del page_num
    del page_names
    self._pages_active = False
    self._page_widgets.clear()
    self._page_buffer.clear()

Trong ứng dụng TinText (TinGroup), ví dụ kiểm thử cho thẻ <pages> hiển thị hai trang nội dung bên trong các cửa sổ TinText (TinEngine). Cả hai trang đều được kết xuất bất kể người dùng có chọn xem hay không.

Nhìn chung, các thẻ hiển thị động có cơ chế hoạt động tương tự thẻ điều khiển luồng: bộ giải thích thực hiện nhiều vòng lặp để thu thập các thẻ hiển thị, từ đó đọc, phân tích và kết xuất toàn bộ nội dung của thẻ.

Liên kết dự án

Mã nguồn đầy đủ có tại: Github – TinText (thay thế bằng đường dẫn thực tế).

Thẻ: tinml Tkinter python dynamic-rendering markup-language

Đăng vào ngày 30 tháng 7 lúc 21:24