diff --git a/gui_client.py b/gui_client.py index e7416a8..6e32bc2 100644 --- a/gui_client.py +++ b/gui_client.py @@ -1,26 +1,74 @@ import sys import requests +from datetime import datetime, timezone +from zoneinfo import ZoneInfo from PySide6.QtWidgets import ( - QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, - QTableWidget, QTableWidgetItem, QPushButton, QMessageBox, QDialog, - QFormLayout, QLineEdit, QComboBox, QTextEdit, QDialogButtonBox, - QAbstractItemView + QApplication, QWidget, QVBoxLayout, QHBoxLayout, QFormLayout, QAbstractItemView ) -from PySide6.QtCore import Qt, QThread, Signal, QObject -from typing import Dict, Any, Optional +from PySide6.QtCore import ( + Qt, QThread, Signal, QObject, QAbstractTableModel, QModelIndex, QLocale, QAbstractListModel +) +from PySide6.QtGui import QIcon +from qfluentwidgets import ( + MSFluentWindow, setTheme, Theme, TableView, PushButton, MessageBox, MessageBoxBase, + LineEdit, InfoBar, TextEdit, InfoBarIcon, FluentTranslator, TeachingTip, TeachingTipTailPosition, + ModelComboBox, + +) +from qfluentwidgets import FluentIcon as Fi +import resources + +# dict[str, str | int] 就是 +# { +# "ID": "xxx", +# "NAME": "zzz", +# "SAFE": -2, +# "UPDATE_DATA": "aaa", +# "NOTES": "" +# } + # --- API 客户端配置 --- -BASE_URL = "http://127.0.0.1:17701/api/v1/ext" +BASE_URL = "https://safe-marks.oranj.work/api/v1/ext" -# 安全状态与中文的映射 (根据新要求) +SAFE = [1, 0, -1, -2] SAFE_MAP = { - 1: "安全 (safe)", - 0: "未知 (unsure)", - -1: "不安全 (unsafe)", - -2: "不详 (unknown)" + 1: "安全", + 0: "未知", + -1: "不安全", + -2: "未记录" } # 反向映射,用于 QComboBox SAFE_MAP_INV = {v: k for k, v in SAFE_MAP.items()} +SAFE_MAP_ICON = { + 1: InfoBarIcon.SUCCESS.icon(), + 0: InfoBarIcon.WARNING.icon(), + -1: InfoBarIcon.ERROR.icon(), + -2: InfoBarIcon.INFORMATION.icon(), +} + + +class APIException(Exception): + pass + + +def show_quick_tip(widget: QWidget, caption: str, text: str): + mb = MessageBox(caption, text, widget) + mb.cancelButton.setHidden(True) + mb.buttonLayout.insertStretch(0, 1) + mb.buttonLayout.setStretch(1, 0) + mb.yesButton.setMinimumWidth(100) + mb.setClosableOnMaskClicked(True) + mb.exec() + + +def accept_warning(widget: QWidget, condition: bool, + caption: str = "警告", text: str = "你确定要继续吗?") -> bool: + if condition: + mb = MessageBox(caption, text, widget) + if not mb.exec(): + return True + return False # --- API 工作线程 --- @@ -43,9 +91,9 @@ class ApiWorker(QObject): if not response.ok: try: detail = response.json().get("detail", response.text) - raise Exception(f"API 错误 (状态 {response.status_code}): {detail}") + raise APIException(f"API 错误 (状态 {response.status_code}): {detail}") except requests.JSONDecodeError: - raise Exception(f"API 错误 (状态 {response.status_code}): {response.text}") + raise APIException(f"API 错误 (状态 {response.status_code}): {response.text}") return response.json() def do_query_all(self): @@ -57,7 +105,7 @@ class ApiWorker(QObject): except Exception as e: self.error.emit(str(e)) - def do_add_one(self, extension: Dict[str, Any]): + def do_add_one(self, extension: dict[str, str | int]): """执行 add_one 操作 (dict)""" try: response = self.session.post(f"{BASE_URL}/add_one", json=extension) @@ -66,7 +114,7 @@ class ApiWorker(QObject): except Exception as e: self.error.emit(str(e)) - def do_update_one(self, item_id: str, payload: Dict[str, Any]): + def do_update_one(self, item_id: str, payload: dict[str, str | int]): """执行 update_one 操作 (dict)""" try: response = self.session.put(f"{BASE_URL}/update_one/{item_id}", json=payload) @@ -85,127 +133,241 @@ class ApiWorker(QObject): self.error.emit(str(e)) +class SafeStatusListModel(QAbstractListModel): + + def __init__(self, parent: QWidget = None): + super().__init__(parent) + self.safe_status = SAFE + + def rowCount(self, /, parent = ...): + return len(self.safe_status) + + def columnCount(self, parent, /): + return 1 + + def data(self, index: QModelIndex, /, role: int = ...): + row = index.row() + if role == Qt.ItemDataRole.EditRole: + return SAFE_MAP[self.safe_status[row]] + elif role == Qt.ItemDataRole.DecorationRole: + return SAFE_MAP_ICON[self.safe_status[row]] + elif role == Qt.ItemDataRole.UserRole: + return self.safe_status[row] + return None + # --- 添加/编辑 对话框 --- -class ExtensionDialog(QDialog): +class ExtensionDialog(MessageBoxBase): """用于添加或编辑插件条目的对话框。""" - def __init__(self, parent=None, extension_data: Optional[Dict] = None): + def __init__(self, extension_data: dict[str, str | int] = None, parent=None): super().__init__(parent) self.is_edit_mode = extension_data is not None - self.setWindowTitle("编辑插件" if self.is_edit_mode else "添加插件") self.setMinimumWidth(400) # 创建控件 - self.id_edit = QLineEdit() - self.name_edit = QLineEdit() - self.safe_combo = QComboBox() - self.notes_edit = QTextEdit() + self.lne_id = LineEdit(self) + self.lne_name = LineEdit(self) + self.safe_combo = ModelComboBox(self) + self.notes_edit = TextEdit(self) # 填充 SAFE 下拉框 (使用新的 MAP) - for text in SAFE_MAP_INV.keys(): - self.safe_combo.addItem(text) + safe_list_model = SafeStatusListModel(self) + self.safe_combo.setModel(safe_list_model) # 布局 form_layout = QFormLayout() - form_layout.addRow("ID:", self.id_edit) - form_layout.addRow("名称 (NAME):", self.name_edit) - form_layout.addRow("安全状态 (SAFE):", self.safe_combo) - form_layout.addRow("备注 (NOTES):", self.notes_edit) - - self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) - self.button_box.accepted.connect(self.accept) - self.button_box.rejected.connect(self.reject) + form_layout.addRow("ID:", self.lne_id) + form_layout.addRow("名称:", self.lne_name) + form_layout.addRow("安全状态:", self.safe_combo) + form_layout.addRow("备注:", self.notes_edit) + self.cw = QWidget(self) main_layout = QVBoxLayout() main_layout.addLayout(form_layout) - main_layout.addWidget(self.button_box) - self.setLayout(main_layout) + self.cw.setLayout(main_layout) + + self.viewLayout.addWidget(self.cw) if self.is_edit_mode: - self.id_edit.setText(extension_data.get("ID", "")) - self.id_edit.setReadOnly(True) # ID 不允许修改 - self.name_edit.setText(extension_data.get("NAME", "")) - safe_status = extension_data.get("SAFE", 0) - self.safe_combo.setCurrentText(SAFE_MAP.get(safe_status, "未知 (unsure)")) + self.lne_id.setText(extension_data.get("ID", "")) + self.lne_id.setReadOnly(True) # ID 不允许修改 + self.lne_name.setText(extension_data.get("NAME", "")) + safe_status: int = extension_data.get("SAFE", -2) + self.safe_combo.setCurrentIndex(SAFE.index(safe_status)) self.notes_edit.setText(extension_data.get("NOTES", "")) + else: + self.safe_combo.setCurrentIndex(SAFE.index(-2)) - def get_data(self) -> Dict[str, Any]: + def validate(self) -> bool: + if len(self.lne_id.text()) == 0: + TeachingTip.create( + target=self.lne_id, + title="错误", + content="ID 不能为空!", + icon=InfoBarIcon.ERROR, + isClosable=True, + duration=2000, + tailPosition=TeachingTipTailPosition.BOTTOM, + parent=self + ) + return False + if len(self.lne_name.text()) == 0: + TeachingTip.create( + target=self.lne_name, + title="错误", + content="名称不能为空!", + icon=InfoBarIcon.ERROR, + isClosable=True, + duration=2000, + tailPosition=TeachingTipTailPosition.BOTTOM, + parent=self, + ) + return False + return True + + def get_data(self) -> dict[str, str | int]: """获取对话框中用于 'add_one' 的数据""" return { - "ID": self.id_edit.text(), - "NAME": self.name_edit.text(), - "SAFE": SAFE_MAP_INV.get(self.safe_combo.currentText(), 0), + "ID": self.lne_id.text(), + "NAME": self.lne_name.text(), + "SAFE": self.safe_combo.currentData(), "NOTES": self.notes_edit.toPlainText() } - def get_update_payload(self) -> Dict[str, Any]: + def get_update_payload(self) -> dict[str, str | int]: """获取对话框中用于 'update_one' 的数据 (不含ID)""" return { - "NAME": self.name_edit.text(), - "SAFE": SAFE_MAP_INV.get(self.safe_combo.currentText(), 0), + "NAME": self.lne_name.text(), + "SAFE": self.safe_combo.currentData(), "NOTES": self.notes_edit.toPlainText() } +class ExtensionsDataTable(QAbstractTableModel): + + def __init__(self, ext_data: list[dict[str, str | int]], parent=None): + super().__init__(parent) + self.ext_data = ext_data or [] + self.headers = ["ID", "名称", "安全性", "更新日期", "备注"] + self.col_map = { + 0: "ID", + 1: "NAME", + 2: "SAFE", + 3: "UPDATE_DATE", + 4: "NOTES", + } + + def rowCount(self, /, parent = ...): + return len(self.ext_data) + + def columnCount(self, /, parent = ...): + return len(self.headers) + + def headerData(self, section: int, orientation: Qt.Orientation, /, role: int = ...): + if orientation == Qt.Orientation.Horizontal: + if role == Qt.ItemDataRole.DisplayRole: + return self.headers[section] + return None + + def data(self, index: QModelIndex, /, role: int = ...): + row = index.row() + col = index.column() + ext = self.ext_data[row] + col_val = ext[self.col_map[col]] + if role == Qt.ItemDataRole.DisplayRole: + if col == 2: + return SAFE_MAP[col_val] + elif col == 3: + return self.iso2custom(col_val) + else: + return col_val + if role == Qt.ItemDataRole.DecorationRole: + if col == 2: + return SAFE_MAP_ICON[col_val] + # 一行的每一个列都返回所有数据,这样不管双击一行的哪一个位置,都可以获取到所有数据 + if role == Qt.ItemDataRole.UserRole: + return ext + return None + + def update_data(self, ext_data: list[dict[str, str | int]]): + self.beginResetModel() + self.ext_data.clear() + self.ext_data.extend(ext_data) + self.endResetModel() + + @staticmethod + def iso2custom(iso_time: str) -> str: + dt = datetime.fromisoformat(iso_time) + dt = dt.replace(tzinfo=timezone.utc) + dt = dt.astimezone(ZoneInfo("America/New_York")) + custom_format = "%Y-%m-%d %H:%M:%S" + + return dt.strftime(custom_format) + + +class MainInterface(QWidget): + + def __init__(self, ext_data: list[dict[str, str | int]] = None, parent=None): + super().__init__(parent) + self.setObjectName("main") + self.vly_m = QVBoxLayout() + self.setLayout(self.vly_m) + + self.hly_top = QHBoxLayout() + self.pbn_refresh = PushButton(Fi.SYNC, "刷新", self) + self.pbn_add = PushButton(Fi.ADD, "添加", self) + self.pbn_delete = PushButton(Fi.DELETE, "删除", self) + + self.hly_top.addWidget(self.pbn_refresh) + self.hly_top.addWidget(self.pbn_add) + self.hly_top.addWidget(self.pbn_delete) + self.hly_top.addStretch(1) + self.vly_m.addLayout(self.hly_top) + + self.tbv_m = TableView(self) + self.ext_model = ExtensionsDataTable(ext_data, self) + self.tbv_m.setModel(self.ext_model) + self.tbv_m.setEditTriggers(QAbstractItemView.EditTrigger.NoEditTriggers) + self.tbv_m.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows) # 整行选中 + self.tbv_m.setSelectionMode(QAbstractItemView.SelectionMode.SingleSelection) # 只允许单选 + self.tbv_m.verticalHeader().hide() + self.tbv_m.setBorderVisible(True) + self.tbv_m.setBorderRadius(4) + + self.tbv_m.horizontalHeader().setStretchLastSection(True) + self.tbv_m.setColumnWidth(0, 250) + self.tbv_m.setColumnWidth(1, 200) + self.tbv_m.setColumnWidth(3, 180) + + self.vly_m.addWidget(self.tbv_m) + + # --- 主窗口 --- -class MainWindow(QMainWindow): +class MainWindow(MSFluentWindow): + def __init__(self): super().__init__() - self.setWindowTitle("插件管理器") - self.setGeometry(100, 100, 800, 600) + self.setWindowTitle("插件安全标记") + self.setWindowIcon(QIcon(":/logo.png")) + # self.navigationInterface.hide() + self.resize(1000, 760) + desktop = QApplication.screens()[0].availableGeometry() + w, h = desktop.width(), desktop.height() + self.move(w // 2 - self.width() // 2, h // 2 - self.height() // 2) + setTheme(Theme.LIGHT) - self.table_widget = QTableWidget() - self.setup_table() + # --- UI --- + self.main_interface = MainInterface(parent=self) + self.addSubInterface(self.main_interface, Fi.HOME, "主页", Fi.HOME_FILL) - self.refresh_button = QPushButton("刷新 (Query All)") - self.add_button = QPushButton("添加 (Add One)") - self.delete_button = QPushButton("删除选定 (Delete One)") - - button_layout = QHBoxLayout() - button_layout.addWidget(self.refresh_button) - button_layout.addWidget(self.add_button) - button_layout.addWidget(self.delete_button) - button_layout.addStretch() - - main_layout = QVBoxLayout() - main_layout.addLayout(button_layout) - main_layout.addWidget(self.table_widget) - - central_widget = QWidget() - central_widget.setLayout(main_layout) - self.setCentralWidget(central_widget) - - self.setup_worker_thread() - - # 连接信号和槽 - self.refresh_button.clicked.connect(self.worker.do_query_all) - self.add_button.clicked.connect(self.on_add) - self.delete_button.clicked.connect(self.on_delete) - self.table_widget.doubleClicked.connect(self.on_edit) - - # 初始加载数据 - self.worker.do_query_all() - - def setup_table(self): - """初始化表格""" - self.table_widget.setColumnCount(5) - self.table_widget.setHorizontalHeaderLabels( - ["ID", "名称 (NAME)", "安全 (SAFE)", "更新日期 (UPDATE_DATE)", "备注 (NOTES)"]) - self.table_widget.setEditTriggers(QAbstractItemView.NoEditTriggers) - self.table_widget.setSelectionBehavior(QAbstractItemView.SelectRows) # 整行选中 - self.table_widget.setSelectionMode(QAbstractItemView.SingleSelection) # 只允许单选 - self.table_widget.horizontalHeader().setStretchLastSection(True) - self.table_widget.resizeColumnsToContents() - - def setup_worker_thread(self): - """初始化后台工作线程和 QObject""" + # --- 初始化后台工作线程和 QObject --- self.thread = QThread() self.worker = ApiWorker() self.worker.moveToThread(self.thread) # 连接 Worker 的新信号 - self.worker.queryAllFinished.connect(self.populate_table) + self.worker.queryAllFinished.connect(self.main_interface.ext_model.update_data) self.worker.addOneFinished.connect(self.handle_operation_finished) self.worker.updateOneFinished.connect(self.handle_operation_finished) self.worker.deleteOneFinished.connect(self.handle_operation_finished) @@ -213,6 +375,15 @@ class MainWindow(QMainWindow): self.thread.start() + # 连接信号和槽 + self.main_interface.pbn_refresh.clicked.connect(self.worker.do_query_all) + self.main_interface.pbn_add.clicked.connect(self.on_add) + self.main_interface.pbn_delete.clicked.connect(self.on_delete) + self.main_interface.tbv_m.doubleClicked.connect(self.on_edit) + + # 初始加载数据 + self.worker.do_query_all() + def closeEvent(self, event): """关闭窗口时,确保线程也退出""" self.thread.quit() @@ -221,88 +392,61 @@ class MainWindow(QMainWindow): # --- GUI 槽函数 (在主线程中运行) --- - def populate_table(self, data: list): - """使用从 API 获取的数据填充表格""" - self.table_widget.setRowCount(0) - - for row, item in enumerate(data): - self.table_widget.insertRow(row) - - id_item = QTableWidgetItem(item.get("ID")) - name_item = QTableWidgetItem(item.get("NAME")) - safe_val = item.get("SAFE", 0) - safe_item = QTableWidgetItem(SAFE_MAP.get(safe_val, "未知 (unsure)")) # 使用新 MAP - date_item = QTableWidgetItem(item.get("UPDATE_DATE")) - notes_item = QTableWidgetItem(item.get("NOTES")) - - # 存储原始数据到 ID 项中,以便编辑时检索 - id_item.setData(Qt.UserRole, item) - - self.table_widget.setItem(row, 0, id_item) - self.table_widget.setItem(row, 1, name_item) - self.table_widget.setItem(row, 2, safe_item) - self.table_widget.setItem(row, 3, date_item) - self.table_widget.setItem(row, 4, notes_item) - - self.table_widget.resizeColumnsToContents() - def handle_operation_finished(self, result: dict): """添加/更新/删除成功后的通用处理器""" - QMessageBox.information(self, "操作成功", f"操作已成功完成。\n{result}") + InfoBar.success("", "操作已成功完成。", duration=2000, parent=self.window()) self.worker.do_query_all() # 重新加载数据 def handle_error(self, error_message: str): """显示来自工作线程的错误消息""" - QMessageBox.critical(self, "API 错误", f"与服务器通信时发生错误:\n{error_message}") + show_quick_tip(self, "API 错误", f"与服务器通信时发生错误:\n{error_message}") # --- 按钮点击处理 --- def on_add(self): """打开“添加”对话框 (add_one)""" - dialog = ExtensionDialog(self) + dialog = ExtensionDialog(parent=self) if dialog.exec(): data = dialog.get_data() - if not data.get("ID") or not data.get("NAME"): - self.handle_error("ID 和 名称 (NAME) 不能为空。") - return self.worker.do_add_one(data) - def on_edit(self, index): + def on_edit(self, index: QModelIndex): """打开“编辑”对话框(通过双击)(update_one)""" - row = index.row() - item_data = self.table_widget.item(row, 0).data(Qt.UserRole) + item_data = index.data(Qt.ItemDataRole.UserRole) item_id = item_data.get("ID") - dialog = ExtensionDialog(self, extension_data=item_data) + dialog = ExtensionDialog(extension_data=item_data, parent=self) if dialog.exec(): update_payload = dialog.get_update_payload() self.worker.do_update_one(item_id, update_payload) def on_delete(self): """删除当前选定的行 (delete_one)""" - current_row = self.table_widget.currentRow() - if current_row < 0: - QMessageBox.warning(self, "未选择", "请选择一行进行删除。") + # 前面设定了只能选中一行 + ext_data: list[dict] = [index.data(Qt.ItemDataRole.UserRole) + for index in self.main_interface.tbv_m.selectedIndexes() + if index.column() == 0] + if len(ext_data) == 0: + show_quick_tip(self, "警告", "你没有选中任何插件。") return - id_item = self.table_widget.item(current_row, 0) - item_id = id_item.text() + ext_d: dict = ext_data[0] + if accept_warning(self, True, "警告", + f"你确定要删除插件【{ext_d.get('NAME')}】吗?"): + return - reply = QMessageBox.question( - self, - "确认删除", - f"你确定要删除插件 (ID: {item_id}) 吗?", - QMessageBox.Yes | QMessageBox.No, - QMessageBox.No - ) - - if reply == QMessageBox.Yes: - self.worker.do_delete_one(item_id) + self.worker.do_delete_one(ext_d.get("ID")) # --- 运行 GUI --- -if __name__ == "__main__": +def main(): app = QApplication(sys.argv) - window = MainWindow() - window.show() - sys.exit(app.exec()) + translator = FluentTranslator(QLocale(QLocale.Language.Chinese, QLocale.Country.China)) + app.installTranslator(translator) + win = MainWindow() + win.show() + return app.exec() + + +if __name__ == '__main__': + main() diff --git a/logo.ico b/logo.ico new file mode 100644 index 0000000..f996efb Binary files /dev/null and b/logo.ico differ diff --git a/logo.png b/logo.png new file mode 100644 index 0000000..8fe1bdc Binary files /dev/null and b/logo.png differ diff --git a/requirements.txt b/requirements.txt index b043195..1b970fd 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ PySide6-Essentials PySide6-Fluent-Widgets requests +tzdata diff --git a/resources.py b/resources.py new file mode 100644 index 0000000..cc0a7f2 --- /dev/null +++ b/resources.py @@ -0,0 +1,297 @@ +# Resource object code (Python 3) +# Created by: object code +# Created by: The Resource Compiler for Qt version 6.10.0 +# WARNING! All changes made in this file will be lost! + +from PySide6 import QtCore + +qt_resource_data = b"\ +\x00\x00\x10v\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x80\x00\x00\x00\x80\x08\x03\x00\x00\x00\xf4\xe0\x91\xf9\ +\x00\x00\x03\x00PLTEGpL\x04\x06\x03\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00z\xabh\x00\x00\x00\x1b=E\ +\x01\x02\x01\x10!%\x00\x03\x04\x00\x00\x00\x01\x02\x01\x00\ +\x00\x00\x03\x08\x07a\xaf\xc1\x04\x06\x03!\xaf\xe6\x02\x03\ +\x01\x00\x00\x00(O\x1a\x03\x04\x02\x00g\x8f\x00\x00\x00\ +\x05\x09\x04\x00\x00\x00\x00\x00\x00\x04\x08\x03\x02\x05\x02*\ +U\x1b-\x5c\x1c\x05\x09\x03\x00Gb}\xda\xfa\x07@\ +W\x08\x12\x05\x11$\x0b'j\x82\x92\xd4n\x9f\xe5\xfb\ +\xa1\xe6\xfb%j\x82\x1b7\x11\x84\xdb\xf6.t\x8b\x00\ +D^&n\x86E\xa6\x94T\xae\x96}\xd9\xf6k\xd0\ +\xf2#;\x1b\x0a\x9d\xcf\x15J;\x00D]#G\x16\ +#6\x1c2S&Jz8\x01FaV\xb2\x9c/\ +z\x93'q\x8b,v\x90\x00Ie0{\x955\x82\ +\x9dY\x91D\x00E`H\xaa\x9bCk4d\xa3M\ +\x0aCK#\x86\x92z\xaeb7\x87\xa2.\xbf\xf5\x00\ +\xa3\xe1\x00Fb\x02\xa7\xe6\x1cIE\x00\xa1\xdf\x13[\ +u\x00Gb\x00\xa1\xdf\x00\xa2\xe0\x00\xa3\xe2\xab\xdb\x91\ +\xd0\xea\xc2\xcc\xe3\xc29S0\x9d\xd6}\xa0\xde}\x99\ +\xd8s[\x89J\x0b\x15\x08=\x8e\xa9j\xcd\xed+S\ +\x1c-X\x1d.[\x1e\x00=V\x00Fb\x00C]\ +\x00;S\x00E_\x00AZ*P\x1cw\xbb]\x80\ +\xc2h\x00?X,W\x1dz\xbea\x83\xc4l\x00\x93\ +\xccx\xbc_-Z\x1d~\xc0e,V\x1d+U\x1d\ +\x00\x9e\xdb\x00\x8e\xc5\x00\x91\xca*R\x1c\x00Ie\x85\ +\xc5m\x00\x8c\xc2\x00\x9a\xd6)M\x1b\x00B\x5c\x00:\ +P\x00\x9c\xd8\x00\x94\xce\x82\xc3j\x89\xc8r\x00\x97\xd2\ +.]\x1e(K\x1b&F\x1a\x00\x8a\xbf\x00\xa3\xe1\x00\ +\xa0\xdeg\xb0L\x7f\xc1gm\xb5S}\xbfd\x00\x99\ +\xd3/^\x1e\x11\x22\x0c0`\x1e\x00\x8f\xc7t\xba[\ +o\xb6U\x00\xa5\xe4\x00\x88\xbc\x008N{\xbfc)\ +N\x1c/_\x1e\x86\xc6o\x9e\xd7\x890b\x1f%B\ +\x1a\x00\x83\xb5\x00\x85\xb9k\xb3Qq\xb7W'H\x1b\ +i\xb2O\x8e\xccx\x88\xc7p\x00\xa7\xe7\x006L\x9a\ +\xd4\x85s\xb8Yr\xb8X\x92\xce{\x8c\xcau\x94\xd0\ +~\x8a\xc9s\x00\x16\x1e\x97\xd2\x81\x15(\x0e\x00\xa9\xe9\ +\x00\x96\xd0\x00~\xb0\x00\xaa\xec\x18.\x11\x8b\xc9u\x0b\ +\x15\x07%D\x19\x00&3\x00\x1e*\x00\xac\xee\x03\xb0\ +\xf2\x0e\x1c\x0a\x00->\x1c4\x14\x00\xae\xf0>]2\ +P|@\xa4\xedx/`\x1eQ\xc4\xed\x013FO\ +x@\x86\xc6n\x1f9\x16#>\x19PtC \xa9\ +\xdb;U1d\x94O>\xbb\xe9\x01Qo\x00Wy\ +7Z)/G&\x01`\x831c\x1f\x0bCG0\ +T\x22\x10\xa6\xdf\x0aJ]\x10Ne\x92\xca{\x89\xc7\ +d\xa8\xde\x93D\xa0\x89\x1d\xa0\xd1Gn3n\xa1R\ +\x9e\xe2w%\xb8\xf0\x17\x97\xc7\x18Xn$q~?\ +k>3O's\xadU\x0d)\x1d\x0b51c\xc2\ +\xa1\x82\xbea[\x87C\x02p\x96`\x9fs;zY\ +\x09u\x9d\x18J8\x12=7\x1dPF.bJp\ +\xc2y\x00\xff\x00z\x00\x04,;\xc9\x0b\x00\x00\x01\x00\ +tRNS\x00\x17\x11%\x09\x19\x0d\x01\x06\x03K,\ +\x1c3!\x09U\x0e{9\xf9C\xfefq^>\x97\ +\x88\xf9\xf9\xa5\xecq\xfd\xb9\xcc\xef\xfeiW\xdf\xe0D\ +\xf9\xec\xd1\xfb\xf61\x1f\xadd\xf6h\xee\x96\xc1\xdd\xaa\ +\xf7\xc4\xad\xb9\xf7\xf7\xfa\xec\x88\xfb\xcd\xf6\xf5\xfe\xfd\xfd\ +n\xb9\xcft;\xef\xa4\x0f\xdf\xd0\xa2\x8f\x83u2\xab\ +\xbb\xcb\xce\xdc\xfd\x14\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\xff\xfe\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\ +\xff\xff\x09\x193o\xbfu\x00\x00\x0c%IDAT\ +x\xda\xec\x9aiT\x14W\x1a\x86U\x10hDQ\x14\ +\x04\xb7$\xe8`\xc6IF'\x1a5\x893C\xc6h\ +\xd4$\x93\xcc\xbeo(6\xb2)\xa2\x12\x05\x89\xf4\xa8\ +\x11P\xd1\x91\x88#.\x19\x04Y\x04\xc1\xd6F\x81\x06\ +\x8c\xa0\x10C\x0b\xad\x88\xa22\xda\x86(q\xc1}M\ +r\xce|\xf7\xdeZnU\xdd\xc2\xaa\xfe1\xbfx\xe9\ +\xe6\x1c\xff\xf8<\xdf[\xf7\xd6\xad\xd6\xee\xd1\xa3;\xdd\ +\xe9Nw\xba\xd3\x1d.\xee\x8a\xfc\x1f\xe1\xde\xef\xbf\xfb\ +\xee\x0bBF\xbf0\x9a\xcf\x8b\xa3_\xe4\xf3\x03>?\ +\xe2\xf2\x0a\xce? \x03p\xbcUc\x90E9\xde\x80\ +#G\x8e\x1c\x83\x1f\x94\xaf\x8f|Mr\x1a\xe7\x0b\xb5\ +\x9c\xfe\xe24\x97w^B\xf9\x9e\x98\xef\xf3\xf9!\x9f\ +\x1f\xe3\xfc\x14\xe7'8\xbf|\xe3\x8d\xb7$\x02\x98\x0e\ +\x12\x9f\xa1\x94\x97\x97\x1f<\x08\xaf\x83\x07\xf7\xec\xd9\xf3\ +o\x94\xb5k\xd7\xfe\x97\xcfI\xf4#\xe6\xf4\xdfo\x5c\ +\xc7\xb9t\xfd\x12\x97\x8b\x97.\xa2\x18/\x1a\x8dI\xc6\ +\xa4@cRxRRx8\xfc\x12~\xc3\xaf\xa4\x97\ +\x15\x0d\x1c;\xc6\xd1\xcb\x09Z\x84\xaf]\xb7v\xdd\xba\ +u\x1fB\xfe\x09\xf9\x08\xf2/\x94\x8f?>t\xe8\xd0\ +\xc9w\x02\x0b33\x0b\x0b\x0b\xe1m\xca\xc41\x99L\ +a\xa6\xb00\xfc\x0e\x0b%\x89\xe6\x13N$\xe4\x02<\ +\xbf\x9c\xe2\x0bxD\xc7\xf8\x0f9\xfcG\x98\x0e\xf8\x95\ ++W\x22\x01.\x99\xe6o\xde\x0c\x02\xec\xfa\x95\ +\xf3S\xe3+\x05\x9e\xbd\xf8\x84\x9d/\x8e\xbfy\xb3(\ +\xc0\x1a?T\xad~\x94\xe7%\x02N\xb5\x8f\xf8K\xbf\ +z3Z\x13_R?d.-\xa0o\xf1-\x14\xc7\ +_\xba\x94\x13\xe8\x0a\xcf\xa8\x1f\xf8R\x01][\x9f\xc6\ +/MD\x02\xba\xeb\x07\xbeD@\xdf\xd6\xa7\xf8\x89\x89\ +H@\x7f\xfds\x95\x02z\xb6\xbe8>\xe4\xda\x9b\xa1\ +\xfa\xebW\x08\xe8Z|\x9b\xc5\xf1\x13\x13S\xaf\xe1\x06\ +t\xd6/\x17\xd0\xb9\xf5)|*i@S\xfdF\x1a\ +\xaf\x10\xd0\xb3\xf5\xf9\xf6SQ\xae\xbd\x14\xaa\xbf~H\ +\x82D@\xdf\xd6\x17\xc6OM\xdd\xbaU\x14\xd0^\x7f\ +\xf0\xf3?\x97<\x92y\xff\xf6\xbd\xf7~\xf3\xeb_h\ +\xde\xfa\xe2\xf8[)\x81g\xd5\xff2\x82\xbe5\xd1\xf3\ +\x03\x97_\xa1\xa7p\xd6\x93\xf9\x80W\xde\xd7\xb4\xf5\xc5\ +\xf1\x91\xc0\x8dPm\xf5O\xecax\xf6\x87\x83\x09\xaa\ +\xf8\xec\xb2\xb2\xecDI\xfb\x98\x9f\x9dM\x044\xd4?\ +Q\xcb'\x84\x09\xaa[\xdf\xaf\xa1\xb6\xfa\xf0\x81\xc3\xd5\ +\xb5\x85a\xc6\x84\x963\xe7\xfc\x1e<\x06\xa5l$\x10\ +\x0d\x8f\x9f\x85\x0d\x0d\xb5\xb5\xb5\xd5\xd5\xd5&\xd5\xd5\xaf\ +M@\xb5}\xbf\xe8Ld \x0b\x08\x99\x1a\xaa\x0f\xe3\ +T\xe3\xccP\xbd\xf9i\x16`/>\xbf\xe8PS\x83\ +LA\xc4\xd6ri\xa8\x0dT]\xfd\x1a\x05\xd4\xb6>\ +\x08@\xd1`pX\xc4cp\x03\x09y.4\x99\x02\ +Uo>\xda\x04T\xb7\xbe_xh\xa8)\xb3P(\ +\x81\xe0\x1b2\xf1]\x10/>\xe0\x02/P\xed\xec\x99\ +[]\xf3\xb7\x91#\x9f\x1b\xea\xe3\xef\xdf\xdf\xf7\x83?\ +\xf6\xf6\xf6f}J\x9d\xa0\xba\xf5\xfd\xc2\xa1\x82L\xb4\ +\xd6p\x09\xc0\xaf\xad-4E\x1b\xc9\xdf\x9f\x80\xd3\x92\ +\xd0\xd2\x12\xacz\xf3\xab\xae\xacI/\xadhlj\xed\ +wUo\xbc\x94\x002\x80\xf1C\xe7%\ +p\xd4s\xe7\xe0\xc5%\x98Y?\x12\xf8\x06q\xb7o\ +_\xb1\x1dG*\xf0)\x9f\xbbj\xa7~\xaa\x9f\x11\xaf\ +\xc2\x0b\xdf|\xfb\xed\xcd\x9b7\xaf\xc0\xfb\xd1\xa3G7\ + \xe7\xfc\xe8<\x08V;{\x12\x9e$''\xafX\ +!\xf0\xb7K\x05\xbe\xe4\x04\xd4N\xfd\xd4\x99\x0f\x1f>\ +\x8c\x09\xfa\x1c\xd2\xd1\x11\x14t\x19%\x08\xae\x19\xfas\ +\xd0\xfe\xef\xbe{\xd8\xde\xde>s\xe6\xcc\xc7\x8f\x83\x99\ +|\xb4B^\x95\xf0\xd9\x0d\x14\xddU9\xf5SS\xa7\ +\x99\xcd\x16\x8b\xc3a\xb1\x98?-*\xea\xe8\xc0hI\ +\x82\xf6\xcf\xba\xda\x5c\x7fs\x86\x12\x9f@\xd6\xe8\xab\xc9\ +4\x9e)P\x04\x0d\xa8\x1d{\xd3\xf2\xf3\xf3\xf7\xe5[\ +\xf7\xe1\xe4;\x1c\x0e\xab\xb5\xc4\x5cb)\xb2t8f\ +]\xbdo\xaf\xeb\xeclm\xcdM\xa9\xca\x9a\xc1\xac\x1f\ +\x0bH\xf9\xf1\x0a\x81\x22\x18m\x8a\xda\xa9O\x04 \xd6\ +\xfd\xfb\xf7\xaf^]PP\xb0\x13r\xe2\xc4\x89\x82}\ +\xf6\xdc,\x9c*\xf4\xae\x9a\xc1\xac\x9f\x08P\xf8\x88x\ +\x85\x00\xe2\x17MQ;\xf5\x91\x00\xc2[\x11\x9e\xf0O\ +,_\xbe<&&\xa6\xc0\xde\x94\xc5\xa7\x0a\x090\xc7\ +G\x02\xf4\xf8\xf1\xf1!2\x01\xccG\x02\xecS?{\ +\x9a\xc8'\xe3s\xfcU;\xed\x14>%\xe5\x02\xeb\xf2\ +\x8b\x02\x22_*PD\xf8 o\x9f;\xf5\xb3\xc7\ +*\xf9\x08\xbfJ\x14\xc0|N@1>\x11\x10\xf0\xc0\ +\x975\x80\xf1E\x96)\xac\xf6\x11\x1f\x04\x18\xf5\x03?\ +j\xb9\x9d\xc2\x13\x01\xc6\xf8p\x9f~B\x8f/\x13(\ +*\x12\x05\x94\xed\xa3\xec\x1a\xcb\xaa\x1f\xf8\x9c\x00\xcfG\ +\x02\xca\xcb\x0f\xa7D\xc2\xbc'R~\x9cR\xc0b\xb1\ +\x8cc\xb5\x0f\xf8] \xc0\xa8\x1f\xf8Q1v\x9a\x0f\ +\x02r\xfc\xdc\xe8\xc2;wn\x97V\x9c\x0a\xa1\xf0!\ +qJ\x01\xd8\xd3H@\xd9\xfe.\x94\xb1\x22\x9f\x1a?\ +**\x0d\x04D|J\xee\x05\x8ao45\xdc\xb9}\ +;\x1d\xa5\xb4\xf4(\x12\x10\xc6\x8fS\x0aXP\xc6\xb1\ +\xda'\x02\xac\xfa\x81\x0f\x024\x9f\x13\x88\x86G\x97\xdb\ +55\xe9\xf0\x22\xf8\x8a\x8a\xc6\xba\x10\x9a\xbfd\x89T\ +\xc0b\x11\x04\x14\xed\xa3\x94\x8de\xd5\x9f\x96\x96\xb6x\ +\x95\x9d\xc2\xe7\xe6>\x85G\xc4\x03\x95\x95\x955\x955\ +(\xe95\x04\x7f\xf4hS]\x88X?\xf0\xa5\x02\x04\ +o6\x8fc\x8f\xbf\xab\xacl,\xab\xfe\xb4\xc5\x8bA\ +\x80\xe6\xe7\x96\x226\x0e\xe6\xf3\xf8\xc6\xa6,\x10\x10\xea\ +\x07\xfe\xfc\xe1\x94\x00\xcf7\x8fc\x8f\x0fi\x97\xed>\ +\x9e\xbf\x91\x17\xc8\xcd\xe5\x05\x98\xfc\xa6\xdc\xba8j\xfc\ +\xf9\xf3\x95\x02fN@\xba\xf8v\x95\x01\x1f^\xed\xac\ +\xfa\x81\xcf\x0bp\xfc\xe4\xf4J\xd1\x80\xf0\x01\xdf(\x08\ +\xc4\x09\xfc\xd92\x01\xb3\x99\x08P\xedgs\xe3\x93\x06\ +X\xf5\x03\x7f[\x94\x9d\xe6\xf3\x02\xfc\xf8\xa8\x00r\x05\ +Z\xeb\x96H\xf82\x01\xc277?\xbdp\xf1\xfa\xb9\ +\x07e\xd4\xf8\x18\x0f\x02\xac\xfa\x81\xbf-\xcdN\xe1\x93\ +\x93K\x85\xea\x11\x1a\xd8\x00om\xc5\xcf\x82q!\x14\ +^&@\xf0%%\xcdM\x8dG+J\xe1\xe7\xce\x85\ +\x8b- \x22\xa6\x1d\xe3c\xa4\xf5o\xdb\xb6`\x01\x08\ +P\xfc\xe4\x0a\x09;\x8b<\x02\xc3c\xa8l|\x99\x00\ +\xcf/i\xce\xca\xe2\x14P\xd2\xd3o\x0b\x22\xed\xac\xfa\ +\x81\x0f\x024?\xf9h\x85\xd09a\xb7\xb5\x85\x84H\ +W\x1f\xc6\xcf^\xa4\x10(A\x02pS!\x0a\x98O\ +\xee\x22\xe9H\xe4\x11\xab~\xe0/Xl\xa7\xf0\xc9\xc9\ +\x8d\xb8\xf2N\xfc\xf8\xdd\x16\x12\x1fO\xdf\xfb$|\xb9\ +@I\x09/P\xd5\x84\x0c*\x04\x01\x22a\x93\xf3\x09\ +>6\x96\x08\xf0\xfc\xed\xe4r\x9fm\x8b\x90\x1d=\x0a\ +\xbeL\x00\xe3\xf3\xf3I\x03M\x8d\xd8 \x9d\x8e\x8dU\ +?\xf0c7\xdaE<w\xaa\x8f?G*\ +P\xc2\x15\x90\xdf\x9c\x92RU\xd5v\xf6\x0a\x0e4\xd9\ +\xd9\x0a+\x02{\xd8X\xf5\xc7\xc6FFn\xb4+\xf8\ +\xca\xfa\x97P|\x82\x97\x0b\xa0\xf1!\xb3\x9a\xaf\xde\xbb\ +|\xef\xde\xde[W\xaf67\xdf\xbf__\x8f=:\ +;s\xb3\xeae\xbb\x8f\xe7Gn\xb3;3\xbe\xb2\x81\ +|\x12G\x09<\xf2\x9b\x1d%\xfb\x82.\xdf\x8b\xbc\xc5\ +{\xd8\xae\x5cif\xd5\x0f\xfc\xc8\x05\xf6g\x8d\xcf\xe6\ +\xaf\x915\x80\xc3=xZ\xad\xf9\x0e\xab\xc3Q\x02\x1a\ +\x05\xbc\xc7-V\xfd\x90\x0cN\x80=~\x88*~\x8d\ +D@\xc0K>w\x14\xac\xb6ZW;\x1c\xf9\xa8\x8e\ +\xcb\xf7\x98\xfc\x8c\x8c\x8cX\x9b\x13\xf5\x03\x7f\x93B\x80\ +\xfd\xb9#&f\xf9\xce\x82\x9dVk\x01\xab\xfe\x0cN\ +@[\xfd\x8bx>\xc2o\xda4B&\xa0\xf2\xb9\x83\ +\xbb\xf7G\xa5\xb1\xea\x87\xec\x8d\xb5\xad\xd0_?\xf0w\ +H\x05\xf6\xc9\xf8\xb2\xa3O\xa5~\xe0c\x01\xdd\xf5\x03\ +_*\xc0\xae\x7f\x95\xda\xcdG\x18\x7f\xef\xde\xe2H\xdb\ +\x0a)>^\xcb\xf8;v\x1c\xa7\x05\xba\xaa_\xca\x8f\ +\x95\xf2\x8b\x8bwG\xda\x9c\x19\x7f\xc7\xf1O\xa4\x02V\ +\xb5\xcf\x1d]\xd6\x0f|\x22\xa0y\xf3#<\xe1K\x04\ +\xac\xf4\xee\xd3Q?\xf0\xb7d\xd8\xf4\xae>\x8c\xff\xe4\ +?\x12\x01'\xeb\x07>\x12\xd0_?\xf0%\x02\xce\xd6\ +\x0f\xfc-{m*G\x8f*\xfe8\xc2\xcb\x04\xd4\xeb\ +_\xdcU\xfd\x90\x0dD@\xef\xf82\x01\x84/p\xa2\ +~\xe0\x13\x01'\xf8\xe7i\x01g\xeb\x07\xfe\x86b\x9b\ +\xde\xd5\x87\xf1\xe7\xcf\x8fp\xa7\x05\xb4\xd7O\xf17\xa0\ +\x14\xd7;3>\x12\xf00\x88\x02zn~\xf4\xf8\x1b\ +rrv\xd7k:z\xa8\xd5\x87\xf1 \xe0\xe6\xc1u\ +\xe0\xde\x9b\xaf?Fg\xfd\xc0\x07\x01'\xea\xc7\x02=\ +]\x0d\x82\x80\x93\xf5\x03?gK\xbd\x13\xf5#\xfex\ +/7\x22\xe0n\xe8\xa9\xa3\xfeb\xc9\xf899yX\ +@/~\xc4\xa8a\x9e}\xf8\x06\xdc\x0d\xfd\xa8\xfa\xa3\ +\xf4\xd4\x9f\x93\x97\x87\x04t\x8e?i\xd4P\x9f\x00\xcf\ +\xbe\xfdzy\x08\x02\xce\xd6\x9f\x87\x05\x9ey\xf4P\xab\ +\xef\xf8\xa4Q\xcf\x0d\xf3\x0f\xf0\x1c\xd8\xc7\xab\x97\xab\x81\ +[\x84\x06\xafU\xba\xeb\x17\xf8y\x1b\xea\xb5\xdf\xfb\xc6\ +\x8c\x1c:\xccgp\x7f_\xc0\xf7t\xf3\xe0\xf9=\x0c\ +\xae.\xfd'\xffu\x96Z\xfd\x91\xea\xf5\xe7\xe5\xad_\ +\x0f\x02\x9a\xf8\xc39\xba\xe7\x90\xbe.\x80w\x15\xf9=\ +\xdc=\xdcz\xba\x0c\xf4\xf4\x9f<}j\xac\xbe\xfa\x81\ +\x0f\x02]\x1f=\x08\x7f\x9c\xa3\x07x\xc2\xf0\x1c\xde@\ +\xfd\xff\xb9\x01\x0c\xbc\x5c\xfa\x0e\xf1\xec?x\xf2\xf8\xe9\ +o/\xd0\x5c\xffz\x22\xd0\xf5\xf8;&\x8dBt\xff\ +\x004<\xc2\xf7r\x95\xf1\xc1\xc0\xe0\xe1\xea\xd6\xabg\ +?\x97\xbe\x03}\xfb\x07\xf8O~\xed\xf5[]\xd7\x9f\ +\xc3\xe3\x91@W\xfc5\xb0\xe88\xba\xef@\x82wS\ +\xe0\xc9V0x`\x09/\x97>\xb8\x8aa\xe3\xa7O\ +\xdd\x9d\xd1\xf5\xe5\xe7\x05\xd4\xf0\xc3\xc7\x10:\x5cx\xdf\ +!\x22\xde\xc3\xa0\xf6\x95aw\xae\x09\xaf~X\x02W\ +\xf1vW\xf5\x13\x016\x7f\xd0\x98\x91\x84\xce\x0d\x8f\xf0\ +j\xe3+$\xc8\xf5\xe83\xd0\xd73`\xb0\xcfx\xb0\ +P\xa9_\x14\x90\x1e=k&\xd1\xf4!\x18\xdf\xcfK\ +\x0b^&\xc1_\x0f\x7ftA\xf2\x18\xe3\xf3\x02\xf4\xf8\ +s`\xd1\x09t\x19^\x1b\x9f|\xd3[X\x14p=\ +\xd0\xd2\x1c\xec3\xf4\xb5\xd7\xa7\xe6\xc9\xf9X@\xe4\xcf\ +\x1e#\xd2\xd1\xf04\xdeU;^)\x81\xaf\x07\xae\x02\ +.\xc8z\xa9\x80\xf8O\x0e\x02\x9d\x1f\x9e\xc7\xf3\x8bO\ +\xf7\x17\xd6\x91\x03'\x81\x16\x05Y\x9a>C\xe1\x82H\ +\x04\x10}\x10Z\xf2<\x1d\x86W\xe2\x0dN~_\xfe\ +\x7f\xed\x9b1\x0e\x830\x0cEE\x14\xa5\x80P\x07\xa4\ +\x0e\x9dz9\x8e\xc0!{,\xfe\xb7\xdd\x8a\xb4\x88\x81\ +\xa4e\xf1\xbb\xc0{\xb1a\xf4:\xc2>\x8a;F\xf1\ +\xc0B,`\x9am\xed\x9c<\xed:\xfb\x95>\x1e\xd7\ +oG\xe8>0\x0a,\xe4\xa9\x7f\xbb\xad\xbd\xb7\xb7\xd3\ +\x9e\xe9\x8b\xcf\x05^\x0dY\x04F1\x8eo\xf7\x8d\x0f\ +\x87\x1br\xb1\x8b\xbeh\xfa\xfb\x11W\xad\xe8\xeds\x13\ +/\xcdpCN{e\xfdV\x04*\xd4(\x0cBC\ +\x82\xd8U_\xfdX\xc3\x1a\x18\x91R\xf8$\x091\xd2\ +\xdev\x95\x9f\xff\x1d!\x1d9\xad\xa1\xe7\x1c\xbf=\x8c\ +\xe9v\xf9\xdf\xa5\xcc\xe5\xd4\x1b\x1d\xc7q\x1c\xc7)c\ +\x01\xf8\x0eHZ\xe9\xd3K\x9a\x00\x00\x00\x00IEN\ +D\xaeB`\x82\ +" + +qt_resource_name = b"\ +\x00\x08\ +\x05\xe2Y'\ +\x00l\ +\x00o\x00g\x00o\x00.\x00p\x00n\x00g\ +" + +qt_resource_struct = b"\ +\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\ +\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\ +\x00\x00\x01\x99\xf9l\xfe\x95\ +" + +def qInitResources(): + QtCore.qRegisterResourceData(0x03, qt_resource_struct, qt_resource_name, qt_resource_data) + +def qCleanupResources(): + QtCore.qUnregisterResourceData(0x03, qt_resource_struct, qt_resource_name, qt_resource_data) + +qInitResources() diff --git a/resources.qrc b/resources.qrc new file mode 100644 index 0000000..b8d396d --- /dev/null +++ b/resources.qrc @@ -0,0 +1,5 @@ + + + logo.png + +