# Author: Acer Zhang # Datetime: 2021/9/14 # Copyright belongs to the author. # Please indicate the source for reprinting. import sys import webbrowser import time from typing import List import tkinter from tkinter import ttk from tkinter.scrolledtext import ScrolledText from cube_qgui.manager import BLACK, FONT from cube_qgui.banner_tools import BaseBarTool from cube_qgui.third_party.collapsing_frame import CollapsingFrame from cube_qgui.notebook_tools import BaseNotebookTool from cube_qgui.os_tools import StdOutWrapper, DataCache from cube_qgui.base_tools import ArgInfo TITLE_BG_COLOR = BLACK # ToDo 主题部分可考虑通过增加warmup来解决 class _Backbone: """ 整个界面的基础,存放共有的变量 """ def __init__(self, f_style="primary"): """ 请务必检查self.frame是否做了pack等定位操作,无操作将不会被显示 :param f_style: """ # 统一用place self.style = f_style # 全局变量 self.global_info = ArgInfo() def build(self, master, global_info): self.frame = ttk.Frame(master, style=self.style + ".TFrame") self.global_info = global_info class BaseNavigation(_Backbone): """ 左侧导航栏基本框架 """ def __init__(self, style="primary"): super(BaseNavigation, self).__init__(f_style=style) self.tabs = dict() def add_about(self, author: str = "未知作者", version: str = "0.0.1", github_url: str = None, bilibili_url: str = None, blog_url: str = None, other_info: List[str] = None): bus_cf = CollapsingFrame(self.frame) bus_cf.pack(fill='x', pady=0) bus_frm = ttk.Frame(bus_cf, padding=5) bus_frm.columnconfigure(1, weight=1) bus_cf.add(bus_frm, title="Info", style='secondary.TButton') ttk.Label(bus_frm, text=f"Author:\t{author}", style="TLabel", justify="left", wraplength=160).pack(anchor="nw") ttk.Label(bus_frm, text=f"Version:\t{version}", style="TLabel", justify="left", wraplength=160).pack(anchor="nw") if other_info: for line in other_info: ttk.Label(bus_frm, text=line, style="TLabel").pack(anchor="nw") if github_url: def github_callback(event): webbrowser.open_new(github_url) github_label = ttk.Label(bus_frm, text=f"> View on GitHub", style="info.TLabel", justify="left") github_label.pack(anchor="nw") github_label.bind("", github_callback) if bilibili_url: def bilibili_callback(event): webbrowser.open_new(bilibili_url) bilibili_label = ttk.Label(bus_frm, text=f"> View on bilibili", style="info.TLabel", justify="left") bilibili_label.pack(anchor="nw") bilibili_label.bind("", bilibili_callback) if blog_url: def blog_callback(event): webbrowser.open_new(blog_url) blog_label = ttk.Label(bus_frm, text=f"> View on blog", style="info.TLabel", justify="left") blog_label.pack(anchor="nw") blog_label.bind("", blog_callback) def add_info(self, title: str, info: str): bus_cf = CollapsingFrame(self.frame) bus_cf.pack(fill='x', pady=0) bus_frm = ttk.Frame(bus_cf, padding=5) bus_frm.columnconfigure(1, weight=1) bus_cf.add(bus_frm, title=title, style='secondary.TButton', justify="left") ttk.Label(bus_frm, text=info, style="TLabel", wraplength=160).pack(anchor="nw") # def add_homepage(self, tool): # btn = ttk.Button(self.frame, # text=tool.name, # image=tool.name, # compound='left', # command=tool.bind_func) # btn.pack(side='left', ipadx=5, ipady=5, padx=0, pady=1) def build(self, master, global_info): super(BaseNavigation, self).build(master, global_info) self.frame.place(x=0, y=50, width=180, height=470) class BaseNoteBook(_Backbone): """ 中间Notebook部分框架 """ def __init__(self, style="primary", tab_names: List[str] = None, stdout=None): super(BaseNoteBook, self).__init__(f_style=style) self.tab_names = tab_names self.nb_frames = list() # 初始化总输出行数 self.line_len = 2 if not stdout: stdout = sys.stdout self.stdout = stdout sys.stdout = StdOutWrapper(self.stdout, callback=self._write_log_callback) sys.stderr = StdOutWrapper(self.stdout, callback=self._write_log_callback) self.image_cache = DataCache() def add_tool(self, tool: BaseNotebookTool, to_notebook=True): if tool.tab_index >= len(self.nb_frames): raise ValueError(f"设置的index大小越界,当前页面数量为{len(self.nb_frames)},分别为:{self.nb_frames},而" f"您设置的index为{tool.tab_index},超过了当前页面数量。") if to_notebook: frame = self.nb_frames[tool.tab_index] tool_frame = tool.build(master=frame, global_info=self.global_info) else: tool_frame = tool.build(global_info=self.global_info) tool_info = tool.get_arg_info() self.global_info += tool_info return tool_frame def build(self, master, global_info): super(BaseNoteBook, self).build(master, global_info) self.frame.place(x=182, y=55, width=750, height=460) self.nb = ttk.Notebook(self.frame) self.nb.pack(side="top", fill="both") if self.tab_names: for tab_name in self.tab_names: sub_frame = ttk.Frame(self.nb) sub_frame.pack(anchor="nw", expand="yes") self.nb_frames.append(sub_frame) self.nb.add(sub_frame, text=tab_name) else: sub_frame = ttk.Frame(self.nb) sub_frame.pack(anchor="nw", expand="yes") self.nb_frames.append(sub_frame) self.nb.add(sub_frame, text="Generate") self.global_info += ArgInfo(name="QGUI-BaseNoteBook", set_func=self._select_notebook_callback, get_func=lambda: print("BaseNoteBook不支持get")) # 增加OutPut self.console_frame = ttk.Frame(self.frame, style=self.style + ".TFrame") self.console_frame.pack(side="top", fill='both', expand="yes") # 标题 self.title = ttk.Label(self.console_frame, font=(FONT, 15), style=self.style + ".Inverse.TLabel", text="Console Log", justify="left") self.title.pack(side="top", fill="x", padx=10, pady=5) # 文本 self.text_area = ScrolledText(self.console_frame, highlightcolor=master.style.colors.primary, highlightbackground=master.style.colors.border, highlightthickness=1) self.text_area.pack(fill="both", expand="yes") self.text_area.insert("end", "Console Connected\n") self.text_area.configure(state="disable") def print_tool(self, tool: BaseNotebookTool): self.text_area.configure(state="normal") self.text_area.window_create("end", window=self.add_tool(tool, to_notebook=False)) self.text_area.configure(state="disable") print("") def print_image(self, image): from PIL import Image, ImageTk if isinstance(image, str): image = Image.open(image) w, h = image.size scale = 128 / max(w, h) w *= scale h *= scale image = image.resize((int(w), int(h))) image = ImageTk.PhotoImage(image) self.image_cache += image self.text_area.configure(state="normal") self.text_area.image_create("end", image=image) self.text_area.configure(state="disable") print("") def _select_notebook_callback(self, index): self.nb.select(index) def _write_log_callback(self, text): self.text_area.configure(state="normal") # 对print形式的进度条进行适配 if "\r" in text: self.text_area.delete(str(self.line_len) + ".0", str(self.line_len) + ".end") self.line_len -= 1 text = text[text.index("\r") + 1:] + " " if len(text) > 0 and text != "\n": text = time.strftime("%H:%M:%S", time.localtime()) + "\t" + text self.text_area.insert("end", text) self.line_len += 1 self.text_area.configure(state="disable") self.text_area.see("end") class BaseBanner(_Backbone): def __init__(self, title: str = "QGUI Tesing Program", style="primary"): super(BaseBanner, self).__init__(f_style=style) self.img_info = dict() self.title = title def add_tool(self, tool: BaseBarTool): """ 添加小工具组件 :param """ tool.build(master=self.frame, global_info=self.global_info) tool_info = tool.get_arg_info() self.global_info += tool_info def build(self, master, global_info): super(BaseBanner, self).build(master, global_info) self.frame.place(x=0, y=0, width=940, height=50) # 占位标题 black = tkinter.Frame(self.frame, height=10, bg=TITLE_BG_COLOR) black.pack(side="right", anchor="se") # 主标题 title = ttk.Label(self.frame, font=(FONT, 22), text=self.title, style=self.style + ".Inverse.TLabel") title.pack(side="right", anchor="se", padx=5, pady=3) if __name__ == '__main__': pass