modify: h5 插件系统更新
This commit is contained in:
@@ -15,6 +15,7 @@ node_modules/
|
|||||||
|
|
||||||
# python-build files
|
# python-build files
|
||||||
output/
|
output/
|
||||||
|
output.md
|
||||||
|
|
||||||
# lib
|
# lib
|
||||||
lib/
|
lib/
|
||||||
@@ -27,3 +28,4 @@ nuitka-crash-report.xml
|
|||||||
|
|
||||||
# plugin files
|
# plugin files
|
||||||
plugins/
|
plugins/
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
import toml
|
import toml
|
||||||
|
|
||||||
config = toml.load("pyproject.toml")
|
try:
|
||||||
|
config = toml.load("pyproject.toml")
|
||||||
|
except:
|
||||||
|
config = {}
|
||||||
|
|
||||||
|
__all__ = ["config"]
|
||||||
|
|||||||
@@ -9,3 +9,9 @@ def add_student(message_item: MessageItem):
|
|||||||
# time.sleep(3)
|
# time.sleep(3)
|
||||||
print("点击了添加按钮")
|
print("点击了添加按钮")
|
||||||
return {"message": "学生添加成功"}
|
return {"message": "学生添加成功"}
|
||||||
|
|
||||||
|
|
||||||
|
@TaskMounter.task(meta={"task_name": "test"})
|
||||||
|
def test(message_item: MessageItem):
|
||||||
|
# print(f"测试任务: {message_item}")
|
||||||
|
return {"message": "测试成功"}
|
||||||
|
|||||||
@@ -4,5 +4,7 @@ from PySide6.QtCore import Signal, QObject
|
|||||||
class GlobalSignals(QObject):
|
class GlobalSignals(QObject):
|
||||||
|
|
||||||
show_add_dialog_signal = Signal(dict)
|
show_add_dialog_signal = Signal(dict)
|
||||||
|
update_queue_state = Signal(dict, str)
|
||||||
|
|
||||||
|
|
||||||
__all__ = ["GlobalSignals"]
|
__all__ = ["GlobalSignals"]
|
||||||
@@ -5,4 +5,5 @@ class ListenSignals(QObject):
|
|||||||
|
|
||||||
listening_students_signal = Signal(list)
|
listening_students_signal = Signal(list)
|
||||||
|
|
||||||
|
|
||||||
__all__ = ["ListenSignals"]
|
__all__ = ["ListenSignals"]
|
||||||
@@ -15,7 +15,9 @@ QPushButton:pressed{
|
|||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
ADD_BUTTON_STYLE = BUTTON_STYLE + """
|
ADD_BUTTON_STYLE = (
|
||||||
|
BUTTON_STYLE
|
||||||
|
+ """
|
||||||
QPushButton {
|
QPushButton {
|
||||||
background-color: #0d6efd;
|
background-color: #0d6efd;
|
||||||
}
|
}
|
||||||
@@ -26,8 +28,11 @@ QPushButton:pressed{
|
|||||||
background-color: #0a58ca;
|
background-color: #0a58ca;
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
DELETE_BUTTON_STYLE = BUTTON_STYLE + """
|
DELETE_BUTTON_STYLE = (
|
||||||
|
BUTTON_STYLE
|
||||||
|
+ """
|
||||||
QPushButton {
|
QPushButton {
|
||||||
background-color: #dc3545;
|
background-color: #dc3545;
|
||||||
}
|
}
|
||||||
@@ -37,8 +42,11 @@ QPushButton:hover {
|
|||||||
QPushButton:pressed{
|
QPushButton:pressed{
|
||||||
background-color: #b02a37;
|
background-color: #b02a37;
|
||||||
"""
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
BATCH_DELETE_BUTTON_STYLE = BUTTON_STYLE + """
|
BATCH_DELETE_BUTTON_STYLE = (
|
||||||
|
BUTTON_STYLE
|
||||||
|
+ """
|
||||||
QPushButton {
|
QPushButton {
|
||||||
background-color: #fd7e14;
|
background-color: #fd7e14;
|
||||||
}
|
}
|
||||||
@@ -49,8 +57,11 @@ QPushButton:pressed{
|
|||||||
background-color: #dc680f;
|
background-color: #dc680f;
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
UPDATE_BUTTON_STYLE = BUTTON_STYLE + """
|
UPDATE_BUTTON_STYLE = (
|
||||||
|
BUTTON_STYLE
|
||||||
|
+ """
|
||||||
QPushButton {
|
QPushButton {
|
||||||
background-color: #198754;
|
background-color: #198754;
|
||||||
}
|
}
|
||||||
@@ -61,8 +72,11 @@ QPushButton:pressed{
|
|||||||
background-color: #146c43;
|
background-color: #146c43;
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
IMPORT_BUTTON_STYLE = BUTTON_STYLE + """
|
IMPORT_BUTTON_STYLE = (
|
||||||
|
BUTTON_STYLE
|
||||||
|
+ """
|
||||||
QPushButton {
|
QPushButton {
|
||||||
background-color: #6f42c1;
|
background-color: #6f42c1;
|
||||||
}
|
}
|
||||||
@@ -73,8 +87,11 @@ QPushButton:pressed{
|
|||||||
background-color: #4a2d8e;
|
background-color: #4a2d8e;
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
EXPORT_BUTTON_STYLE = BUTTON_STYLE + """
|
EXPORT_BUTTON_STYLE = (
|
||||||
|
BUTTON_STYLE
|
||||||
|
+ """
|
||||||
QPushButton {
|
QPushButton {
|
||||||
background-color: #20c997;
|
background-color: #20c997;
|
||||||
}
|
}
|
||||||
@@ -85,3 +102,4 @@ QPushButton:pressed{
|
|||||||
background-color: #198b6d;
|
background-color: #198b6d;
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
)
|
||||||
|
|||||||
+33
-12
@@ -1,14 +1,30 @@
|
|||||||
# coding:utf-8
|
# coding:utf-8
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
from PySide6.QtCore import QUrl, QSize, QEventLoop, QTimer
|
from PySide6.QtCore import QUrl, QSize, QEventLoop, QTimer
|
||||||
from PySide6.QtGui import QIcon, QDesktopServices
|
from PySide6.QtGui import QIcon, QDesktopServices
|
||||||
from qfluentwidgets import (NavigationItemPosition, MessageBox, NavigationAvatarWidget, SplitFluentWindow, SplashScreen)
|
from PySide6.QtWidgets import QApplication
|
||||||
|
from qfluentwidgets import (
|
||||||
|
NavigationItemPosition,
|
||||||
|
MessageBox,
|
||||||
|
NavigationAvatarWidget,
|
||||||
|
SplitFluentWindow,
|
||||||
|
SplashScreen,
|
||||||
|
)
|
||||||
from qfluentwidgets import FluentIcon as FIF
|
from qfluentwidgets import FluentIcon as FIF
|
||||||
|
|
||||||
from .students.student_interface import StudentInterface
|
from .students.student_interface import StudentInterface
|
||||||
from .video.video_interface import VideoInterface
|
from .video.video_interface import VideoInterface
|
||||||
from .camera.camera_interface import CameraInterface
|
from .camera.camera_interface import CameraInterface
|
||||||
from .web_view.web_view_interface import WebViewInterface
|
from .web_view.web_view_interface import WebViewInterface
|
||||||
from PySide6.QtWidgets import QApplication
|
|
||||||
|
|
||||||
|
def resource_path(relative_path):
|
||||||
|
"""获取资源的绝对路径"""
|
||||||
|
if hasattr(sys, "_MEIPASS"):
|
||||||
|
return os.path.join(sys._MEIPASS, relative_path) # type: ignore
|
||||||
|
return os.path.join(os.path.abspath("."), relative_path)
|
||||||
|
|
||||||
|
|
||||||
class Window(SplitFluentWindow):
|
class Window(SplitFluentWindow):
|
||||||
@@ -21,11 +37,13 @@ class Window(SplitFluentWindow):
|
|||||||
self.web_view_interface = WebViewInterface(self)
|
self.web_view_interface = WebViewInterface(self)
|
||||||
self.initNavigation()
|
self.initNavigation()
|
||||||
self.resize(900, 700)
|
self.resize(900, 700)
|
||||||
self.setWindowIcon(QIcon('./resources/images/logo.png'))
|
|
||||||
|
# 使用资源路径函数
|
||||||
|
self.setWindowIcon(QIcon(resource_path("./resources/images/logo.png")))
|
||||||
|
|
||||||
desktop = QApplication.screens()[0].availableGeometry()
|
desktop = QApplication.screens()[0].availableGeometry()
|
||||||
w, h = desktop.width(), desktop.height()
|
w, h = desktop.width(), desktop.height()
|
||||||
self.move(w//2 - self.width()//2, h//2 - self.height()//2)
|
self.move(w // 2 - self.width() // 2, h // 2 - self.height() // 2)
|
||||||
|
|
||||||
self.splashScreen = SplashScreen(self.windowIcon(), self)
|
self.splashScreen = SplashScreen(self.windowIcon(), self)
|
||||||
self.splashScreen.setIconSize(QSize(102, 102))
|
self.splashScreen.setIconSize(QSize(102, 102))
|
||||||
@@ -44,17 +62,20 @@ class Window(SplitFluentWindow):
|
|||||||
self.addSubInterface(self.camera_interface, FIF.CAMERA, "Camera")
|
self.addSubInterface(self.camera_interface, FIF.CAMERA, "Camera")
|
||||||
self.addSubInterface(self.web_view_interface, FIF.FIT_PAGE, "Web View")
|
self.addSubInterface(self.web_view_interface, FIF.FIT_PAGE, "Web View")
|
||||||
|
|
||||||
|
# 使用资源路径函数
|
||||||
self.navigationInterface.addWidget(
|
self.navigationInterface.addWidget(
|
||||||
routeKey="Info",
|
routeKey="Info",
|
||||||
widget=NavigationAvatarWidget('Info', 'resource/images/logo.png'),
|
widget=NavigationAvatarWidget(
|
||||||
|
"Info", resource_path("./resource/images/logo.png")
|
||||||
|
),
|
||||||
onClick=self.showMessageBox,
|
onClick=self.showMessageBox,
|
||||||
position=NavigationItemPosition.BOTTOM,
|
position=NavigationItemPosition.BOTTOM,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.navigationInterface.addItem(
|
self.navigationInterface.addItem(
|
||||||
routeKey='settingInterface',
|
routeKey="settingInterface",
|
||||||
icon=FIF.SETTING,
|
icon=FIF.SETTING,
|
||||||
text='设置',
|
text="设置",
|
||||||
position=NavigationItemPosition.BOTTOM,
|
position=NavigationItemPosition.BOTTOM,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -62,12 +83,12 @@ class Window(SplitFluentWindow):
|
|||||||
|
|
||||||
def showMessageBox(self):
|
def showMessageBox(self):
|
||||||
w = MessageBox(
|
w = MessageBox(
|
||||||
'支持作者🥰',
|
"支持作者🥰",
|
||||||
'个人开发不易,如果这个项目帮助到了您,可以考虑请作者喝一瓶快乐水🥤。您的支持就是作者开发和维护项目的动力🚀',
|
"个人开发不易,如果这个项目帮助到了您,可以考虑请作者喝一瓶快乐水🥤。您的支持就是作者开发和维护项目的动力🚀",
|
||||||
self
|
self,
|
||||||
)
|
)
|
||||||
w.yesButton.setText('来啦老弟')
|
w.yesButton.setText("来啦老弟")
|
||||||
w.cancelButton.setText('下次一定')
|
w.cancelButton.setText("下次一定")
|
||||||
|
|
||||||
if w.exec():
|
if w.exec():
|
||||||
QDesktopServices.openUrl(QUrl("https://afdian.net/a/zhiyiYo"))
|
QDesktopServices.openUrl(QUrl("https://afdian.net/a/zhiyiYo"))
|
||||||
|
|||||||
@@ -73,6 +73,7 @@ class VideoStream(QObject):
|
|||||||
if self.cap:
|
if self.cap:
|
||||||
self.cap.release()
|
self.cap.release()
|
||||||
|
|
||||||
|
|
||||||
class CameraInterface(QWidget):
|
class CameraInterface(QWidget):
|
||||||
def __init__(self, rtsp_url="rtsp://127.0.0.1:8554/stream"):
|
def __init__(self, rtsp_url="rtsp://127.0.0.1:8554/stream"):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
@@ -105,12 +106,14 @@ class CameraInterface(QWidget):
|
|||||||
|
|
||||||
def display_frame(self, image):
|
def display_frame(self, image):
|
||||||
pixmap = QPixmap.fromImage(image)
|
pixmap = QPixmap.fromImage(image)
|
||||||
self.label.setPixmap(pixmap.scaled(
|
self.label.setPixmap(
|
||||||
self.label.width(),
|
pixmap.scaled(
|
||||||
self.label.height(),
|
self.label.width(),
|
||||||
Qt.AspectRatioMode.KeepAspectRatio,
|
self.label.height(),
|
||||||
Qt.TransformationMode.SmoothTransformation
|
Qt.AspectRatioMode.KeepAspectRatio,
|
||||||
))
|
Qt.TransformationMode.SmoothTransformation,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
def display_error(self, message):
|
def display_error(self, message):
|
||||||
QMessageBox.critical(self, "RTSP视频流错误", message)
|
QMessageBox.critical(self, "RTSP视频流错误", message)
|
||||||
@@ -118,10 +121,11 @@ class CameraInterface(QWidget):
|
|||||||
self.listen_button.setEnabled(True) # 出错时重新启用按钮
|
self.listen_button.setEnabled(True) # 出错时重新启用按钮
|
||||||
|
|
||||||
def closeEvent(self, event):
|
def closeEvent(self, event):
|
||||||
if hasattr(self, 'stream') and self.stream:
|
if hasattr(self, "stream") and self.stream:
|
||||||
self.stream.stop()
|
self.stream.stop()
|
||||||
super().closeEvent(event)
|
super().closeEvent(event)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
app = QApplication(sys.argv)
|
app = QApplication(sys.argv)
|
||||||
|
|
||||||
@@ -133,4 +137,3 @@ if __name__ == "__main__":
|
|||||||
player = CameraInterface(rtsp_url)
|
player = CameraInterface(rtsp_url)
|
||||||
player.show()
|
player.show()
|
||||||
sys.exit(app.exec())
|
sys.exit(app.exec())
|
||||||
|
|
||||||
@@ -48,8 +48,8 @@ class BaseStudentDilalog(MessageBoxBase):
|
|||||||
grid_layout.setColumnStretch(1, 1)
|
grid_layout.setColumnStretch(1, 1)
|
||||||
grid_layout.setAlignment(Qt.AlignmentFlag.AlignLeft)
|
grid_layout.setAlignment(Qt.AlignmentFlag.AlignLeft)
|
||||||
|
|
||||||
self.yesButton.setText('确定')
|
self.yesButton.setText("确定")
|
||||||
self.cancelButton.setText('取消')
|
self.cancelButton.setText("取消")
|
||||||
|
|
||||||
for widget in [field[1] for field in fields]:
|
for widget in [field[1] for field in fields]:
|
||||||
widget.setMinimumWidth(200)
|
widget.setMinimumWidth(200)
|
||||||
@@ -64,4 +64,4 @@ class AddStudentDilalog(BaseStudentDilalog):
|
|||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
super().__init__("添加学生", parent)
|
super().__init__("添加学生", parent)
|
||||||
self.student_id = None
|
self.student_id = None
|
||||||
self.yesButton.setText('添加')
|
self.yesButton.setText("添加")
|
||||||
|
|||||||
@@ -1,12 +1,27 @@
|
|||||||
import json
|
import json
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
from PySide6.QtGui import QIcon
|
from PySide6.QtGui import QIcon
|
||||||
from PySide6.QtCore import Slot
|
from PySide6.QtCore import Slot
|
||||||
from PySide6.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QHeaderView, QCheckBox, QTableWidgetItem
|
from PySide6.QtWidgets import (
|
||||||
|
QWidget,
|
||||||
|
QVBoxLayout,
|
||||||
|
QHBoxLayout,
|
||||||
|
QHeaderView,
|
||||||
|
QCheckBox,
|
||||||
|
QTableWidgetItem,
|
||||||
|
)
|
||||||
|
|
||||||
from queue_sqlite.model import MessageItem
|
from queue_sqlite.model import MessageItem
|
||||||
|
|
||||||
from qfluentwidgets import CardWidget, PushButton, SearchLineEdit, TableWidget, setCustomStyleSheet
|
from qfluentwidgets import (
|
||||||
|
CardWidget,
|
||||||
|
PushButton,
|
||||||
|
SearchLineEdit,
|
||||||
|
TableWidget,
|
||||||
|
setCustomStyleSheet,
|
||||||
|
)
|
||||||
|
|
||||||
from .student_dilalog import AddStudentDilalog
|
from .student_dilalog import AddStudentDilalog
|
||||||
from ...style.button_style import ADD_BUTTON_STYLE, BATCH_DELETE_BUTTON_STYLE
|
from ...style.button_style import ADD_BUTTON_STYLE, BATCH_DELETE_BUTTON_STYLE
|
||||||
@@ -15,6 +30,13 @@ from ...signal import listen_signals
|
|||||||
from ...scheduler_manager import scheduler
|
from ...scheduler_manager import scheduler
|
||||||
|
|
||||||
|
|
||||||
|
def resource_path(relative_path):
|
||||||
|
"""获取资源的绝对路径"""
|
||||||
|
if hasattr(sys, "_MEIPASS"):
|
||||||
|
return os.path.join(sys._MEIPASS, relative_path) # type: ignore
|
||||||
|
return os.path.join(os.path.abspath("."), relative_path)
|
||||||
|
|
||||||
|
|
||||||
class StudentInterface(QWidget):
|
class StudentInterface(QWidget):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
@@ -34,7 +56,7 @@ class StudentInterface(QWidget):
|
|||||||
# 设置 title
|
# 设置 title
|
||||||
self.setWindowTitle("学生管理")
|
self.setWindowTitle("学生管理")
|
||||||
layout = QVBoxLayout(self)
|
layout = QVBoxLayout(self)
|
||||||
layout.setContentsMargins(20, 50, 20, 20) # (左, 上, 右, 下)
|
layout.setContentsMargins(20, 50, 20, 20) # (左, 上, 右, 下)
|
||||||
card_widget = CardWidget(self)
|
card_widget = CardWidget(self)
|
||||||
buttons_layout = QHBoxLayout(card_widget)
|
buttons_layout = QHBoxLayout(card_widget)
|
||||||
|
|
||||||
@@ -46,7 +68,9 @@ class StudentInterface(QWidget):
|
|||||||
self.searchInput.setPlaceholderText("搜索学生姓名或学号...")
|
self.searchInput.setPlaceholderText("搜索学生姓名或学号...")
|
||||||
self.searchInput.setFixedWidth(500)
|
self.searchInput.setFixedWidth(500)
|
||||||
self.batchDeleteButton = PushButton("批量删除", self)
|
self.batchDeleteButton = PushButton("批量删除", self)
|
||||||
setCustomStyleSheet(self.batchDeleteButton, BATCH_DELETE_BUTTON_STYLE, BATCH_DELETE_BUTTON_STYLE)
|
setCustomStyleSheet(
|
||||||
|
self.batchDeleteButton, BATCH_DELETE_BUTTON_STYLE, BATCH_DELETE_BUTTON_STYLE
|
||||||
|
)
|
||||||
|
|
||||||
buttons_layout.addWidget(self.addButton)
|
buttons_layout.addWidget(self.addButton)
|
||||||
buttons_layout.addWidget(self.searchInput)
|
buttons_layout.addWidget(self.searchInput)
|
||||||
@@ -56,25 +80,89 @@ class StudentInterface(QWidget):
|
|||||||
layout.addWidget(card_widget)
|
layout.addWidget(card_widget)
|
||||||
|
|
||||||
self.table_widget = TableWidget(self)
|
self.table_widget = TableWidget(self)
|
||||||
self.table_widget.setBorderRadius(8) # 设置边框圆角
|
self.table_widget.setBorderRadius(8) # 设置边框圆角
|
||||||
self.table_widget.setBorderVisible(True) # 设置表格边框可见
|
self.table_widget.setBorderVisible(True) # 设置表格边框可见
|
||||||
table_labels = ["", "学生ID", "姓名", "学号", "性别", "班级", "语文", "数学", "英语", "总分", "操作"]
|
table_labels = [
|
||||||
|
"",
|
||||||
|
"学生ID",
|
||||||
|
"姓名",
|
||||||
|
"学号",
|
||||||
|
"性别",
|
||||||
|
"班级",
|
||||||
|
"语文",
|
||||||
|
"数学",
|
||||||
|
"英语",
|
||||||
|
"总分",
|
||||||
|
"操作",
|
||||||
|
]
|
||||||
self.table_widget.setColumnCount(len(table_labels))
|
self.table_widget.setColumnCount(len(table_labels))
|
||||||
self.table_widget.setHorizontalHeaderLabels(table_labels)
|
self.table_widget.setHorizontalHeaderLabels(table_labels)
|
||||||
self.table_widget.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.Stretch)
|
self.table_widget.horizontalHeader().setSectionResizeMode(
|
||||||
|
QHeaderView.ResizeMode.Stretch
|
||||||
|
)
|
||||||
|
|
||||||
layout.addWidget(self.table_widget)
|
layout.addWidget(self.table_widget)
|
||||||
|
|
||||||
self.setStyleSheet("StudentInterface {background: white}")
|
self.setStyleSheet("StudentInterface {background: white}")
|
||||||
self.setWindowIcon(QIcon('./resources/images/logo.png'))
|
self.setWindowIcon(QIcon(resource_path("./resources/images/logo.png")))
|
||||||
|
|
||||||
def load_data(self):
|
def load_data(self):
|
||||||
self.students = [
|
self.students = [
|
||||||
{"student_id": 1, "student_name": "张三", "student_number": "2024010101", "gender": 1, "class_name": "1班", "chinese_score": 80, "math_score": 85, "english_score": 90, "total_score": 265},
|
{
|
||||||
{"student_id": 1, "student_name": "张三", "student_number": "2024010101", "gender": 1, "class_name": "1班", "chinese_score": 80, "math_score": 85, "english_score": 90, "total_score": 265},
|
"student_id": 1,
|
||||||
{"student_id": 1, "student_name": "张三", "student_number": "2024010101", "gender": 1, "class_name": "1班", "chinese_score": 80, "math_score": 85, "english_score": 90, "total_score": 265},
|
"student_name": "张三",
|
||||||
{"student_id": 1, "student_name": "张三", "student_number": "2024010101", "gender": 1, "class_name": "1班", "chinese_score": 80, "math_score": 85, "english_score": 90, "total_score": 265},
|
"student_number": "2024010101",
|
||||||
{"student_id": 1, "student_name": "张三", "student_number": "2024010101", "gender": 1, "class_name": "1班", "chinese_score": 80, "math_score": 85, "english_score": 90, "total_score": 265},
|
"gender": 1,
|
||||||
|
"class_name": "1班",
|
||||||
|
"chinese_score": 80,
|
||||||
|
"math_score": 85,
|
||||||
|
"english_score": 90,
|
||||||
|
"total_score": 265,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"student_id": 1,
|
||||||
|
"student_name": "张三",
|
||||||
|
"student_number": "2024010101",
|
||||||
|
"gender": 1,
|
||||||
|
"class_name": "1班",
|
||||||
|
"chinese_score": 80,
|
||||||
|
"math_score": 85,
|
||||||
|
"english_score": 90,
|
||||||
|
"total_score": 265,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"student_id": 1,
|
||||||
|
"student_name": "张三",
|
||||||
|
"student_number": "2024010101",
|
||||||
|
"gender": 1,
|
||||||
|
"class_name": "1班",
|
||||||
|
"chinese_score": 80,
|
||||||
|
"math_score": 85,
|
||||||
|
"english_score": 90,
|
||||||
|
"total_score": 265,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"student_id": 1,
|
||||||
|
"student_name": "张三",
|
||||||
|
"student_number": "2024010101",
|
||||||
|
"gender": 1,
|
||||||
|
"class_name": "1班",
|
||||||
|
"chinese_score": 80,
|
||||||
|
"math_score": 85,
|
||||||
|
"english_score": 90,
|
||||||
|
"total_score": 265,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"student_id": 1,
|
||||||
|
"student_name": "张三",
|
||||||
|
"student_number": "2024010101",
|
||||||
|
"gender": 1,
|
||||||
|
"class_name": "1班",
|
||||||
|
"chinese_score": 80,
|
||||||
|
"math_score": 85,
|
||||||
|
"english_score": 90,
|
||||||
|
"total_score": 265,
|
||||||
|
},
|
||||||
]
|
]
|
||||||
scheduler.update_listen_data("students", json.dumps(self.students))
|
scheduler.update_listen_data("students", json.dumps(self.students))
|
||||||
|
|
||||||
@@ -94,7 +182,7 @@ class StudentInterface(QWidget):
|
|||||||
if key == "gender":
|
if key == "gender":
|
||||||
value = "男" if value == 1 else "女" if value == 2 else "未知"
|
value = "男" if value == 1 else "女" if value == 2 else "未知"
|
||||||
item = QTableWidgetItem(str(value))
|
item = QTableWidgetItem(str(value))
|
||||||
self.table_widget.setItem(row, col+1, item)
|
self.table_widget.setItem(row, col + 1, item)
|
||||||
|
|
||||||
@Slot(dict)
|
@Slot(dict)
|
||||||
def show_add_student_dialog(self, message_dict: dict):
|
def show_add_student_dialog(self, message_dict: dict):
|
||||||
@@ -113,4 +201,7 @@ class StudentInterface(QWidget):
|
|||||||
|
|
||||||
def add_student(self):
|
def add_student(self):
|
||||||
self.addButton.setEnabled(False)
|
self.addButton.setEnabled(False)
|
||||||
scheduler.send_message(MessageItem(content={}, destination="add_student"), self.add_student_callback)
|
scheduler.send_message(
|
||||||
|
MessageItem(content={}, destination="add_student"),
|
||||||
|
self.add_student_callback,
|
||||||
|
)
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ class VideoInterface(QWidget):
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
from PySide6.QtMultimedia import QMediaPlayer
|
from PySide6.QtMultimedia import QMediaPlayer
|
||||||
|
|
||||||
self.player = QMediaPlayer()
|
self.player = QMediaPlayer()
|
||||||
self.setObjectName("video_interface")
|
self.setObjectName("video_interface")
|
||||||
self.setWindowTitle("Fluent Video Player")
|
self.setWindowTitle("Fluent Video Player")
|
||||||
@@ -50,7 +51,6 @@ class VideoInterface(QWidget):
|
|||||||
self.open_button.clicked.connect(self.open_video)
|
self.open_button.clicked.connect(self.open_video)
|
||||||
self.control_layout.addWidget(self.open_button)
|
self.control_layout.addWidget(self.open_button)
|
||||||
|
|
||||||
|
|
||||||
# 添加状态标签 - 修复字体设置
|
# 添加状态标签 - 修复字体设置
|
||||||
self.status_label = QLabel("准备就绪")
|
self.status_label = QLabel("准备就绪")
|
||||||
self.status_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
self.status_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
@@ -77,8 +77,10 @@ class VideoInterface(QWidget):
|
|||||||
def open_video(self):
|
def open_video(self):
|
||||||
"""打开视频文件"""
|
"""打开视频文件"""
|
||||||
file_path, _ = QFileDialog.getOpenFileName(
|
file_path, _ = QFileDialog.getOpenFileName(
|
||||||
self, "选择视频文件", "",
|
self,
|
||||||
"视频文件 (*.mp4 *.avi *.mkv *.mov *.flv *.wmv);;所有文件 (*.*)"
|
"选择视频文件",
|
||||||
|
"",
|
||||||
|
"视频文件 (*.mp4 *.avi *.mkv *.mov *.flv *.wmv);;所有文件 (*.*)",
|
||||||
)
|
)
|
||||||
|
|
||||||
if file_path:
|
if file_path:
|
||||||
|
|||||||
+226
-34
@@ -1,14 +1,59 @@
|
|||||||
from PySide6.QtCore import QObject, Slot, QUrl
|
|
||||||
from PySide6.QtWebChannel import QWebChannel
|
|
||||||
from PySide6.QtWebEngineCore import QWebEngineSettings
|
|
||||||
import time
|
|
||||||
import os
|
import os
|
||||||
|
import time
|
||||||
import zipfile
|
import zipfile
|
||||||
from pathlib import Path
|
|
||||||
import json
|
import json
|
||||||
import shutil
|
import shutil
|
||||||
import base64
|
import base64
|
||||||
import tempfile
|
import tempfile
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from PySide6.QtCore import QObject, Slot, QUrl
|
||||||
|
from PySide6.QtWebChannel import QWebChannel
|
||||||
|
from PySide6.QtWebEngineCore import QWebEngineSettings
|
||||||
|
from qframelesswindow.webengine import FramelessWebEngineView
|
||||||
|
from queue_sqlite.model import MessageItem
|
||||||
|
|
||||||
|
from ...scheduler_manager import scheduler
|
||||||
|
from ...signal import global_signals
|
||||||
|
|
||||||
|
|
||||||
|
class WebViewQueueSqliteOperations(QObject):
|
||||||
|
"""
|
||||||
|
用于前端调用QueueSqlite的队列操作
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, webview: FramelessWebEngineView, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
self.webview = webview
|
||||||
|
self.page = self.webview.page()
|
||||||
|
global_signals.update_queue_state.connect(self.update_queue_state)
|
||||||
|
|
||||||
|
@Slot(str, str, result=str)
|
||||||
|
def send_message(self, message: str, store_key: str):
|
||||||
|
def callback(result: MessageItem):
|
||||||
|
print(f"send_message callback: {result}")
|
||||||
|
global_signals.update_queue_state.emit(result.to_dict(), store_key)
|
||||||
|
|
||||||
|
scheduler.send_message(MessageItem.from_dict(json.loads(message)), callback)
|
||||||
|
|
||||||
|
def update_queue_state(self, result: dict, store_key: str):
|
||||||
|
json_str = json.dumps(result)
|
||||||
|
print(f"result: {result}")
|
||||||
|
print(f"json_str: {json_str}")
|
||||||
|
js_code = f"""
|
||||||
|
try {{
|
||||||
|
if (typeof window.queueStore.updateQueueData === 'function') {{
|
||||||
|
window.queueStore.updateQueueData('{store_key}', {json_str});
|
||||||
|
}} else {{
|
||||||
|
console.log('updateQueueData function not found');
|
||||||
|
}}
|
||||||
|
}} catch (error) {{
|
||||||
|
console.error('Error in updateQueueData:', error);
|
||||||
|
}}
|
||||||
|
"""
|
||||||
|
print(f"执行的JavaScript代码: {js_code}")
|
||||||
|
self.page.runJavaScript(js_code)
|
||||||
|
|
||||||
|
|
||||||
class WebviewBridge(QObject):
|
class WebviewBridge(QObject):
|
||||||
"""Webview桥接类,用于处理前端和Python后端之间的通信"""
|
"""Webview桥接类,用于处理前端和Python后端之间的通信"""
|
||||||
@@ -18,24 +63,38 @@ class WebviewBridge(QObject):
|
|||||||
self.webview = webview
|
self.webview = webview
|
||||||
|
|
||||||
# 启用本地文件访问
|
# 启用本地文件访问
|
||||||
self.webview.settings().setAttribute(QWebEngineSettings.WebAttribute.LocalContentCanAccessFileUrls, True)
|
self.webview.settings().setAttribute(
|
||||||
self.webview.settings().setAttribute(QWebEngineSettings.WebAttribute.LocalContentCanAccessRemoteUrls, True)
|
QWebEngineSettings.WebAttribute.LocalContentCanAccessFileUrls, True
|
||||||
self.webview.settings().setAttribute(QWebEngineSettings.WebAttribute.AllowRunningInsecureContent, True)
|
)
|
||||||
self.webview.settings().setAttribute(QWebEngineSettings.WebAttribute.AllowGeolocationOnInsecureOrigins, True)
|
self.webview.settings().setAttribute(
|
||||||
|
QWebEngineSettings.WebAttribute.LocalContentCanAccessRemoteUrls, True
|
||||||
|
)
|
||||||
|
self.webview.settings().setAttribute(
|
||||||
|
QWebEngineSettings.WebAttribute.AllowRunningInsecureContent, True
|
||||||
|
)
|
||||||
|
self.webview.settings().setAttribute(
|
||||||
|
QWebEngineSettings.WebAttribute.AllowGeolocationOnInsecureOrigins, True
|
||||||
|
)
|
||||||
|
|
||||||
# 设置Web安全策略
|
# 设置Web安全策略
|
||||||
self.webview.page().settings().setAttribute(QWebEngineSettings.WebAttribute.LocalContentCanAccessFileUrls, True)
|
self.webview.page().settings().setAttribute(
|
||||||
self.webview.page().settings().setAttribute(QWebEngineSettings.WebAttribute.LocalContentCanAccessRemoteUrls, True)
|
QWebEngineSettings.WebAttribute.LocalContentCanAccessFileUrls, True
|
||||||
self.webview.page().settings().setAttribute(QWebEngineSettings.WebAttribute.AllowRunningInsecureContent, True)
|
)
|
||||||
|
self.webview.page().settings().setAttribute(
|
||||||
|
QWebEngineSettings.WebAttribute.LocalContentCanAccessRemoteUrls, True
|
||||||
|
)
|
||||||
|
self.webview.page().settings().setAttribute(
|
||||||
|
QWebEngineSettings.WebAttribute.AllowRunningInsecureContent, True
|
||||||
|
)
|
||||||
|
|
||||||
# 创建插件目录
|
# 创建插件目录
|
||||||
self.plugins_dir = Path(os.getcwd()) / "plugins"
|
self.plugins_dir = Path("plugins")
|
||||||
self.plugins_dir.mkdir(exist_ok=True)
|
os.makedirs(self.plugins_dir, exist_ok=True)
|
||||||
|
|
||||||
# 插件信息文件
|
# 插件信息文件
|
||||||
self.plugins_info_file = self.plugins_dir / "plugins.json"
|
self.plugins_info_file = self.plugins_dir / "plugins.json"
|
||||||
if not self.plugins_info_file.exists():
|
if not self.plugins_info_file.exists():
|
||||||
with open(self.plugins_info_file, 'w') as f:
|
with open(self.plugins_info_file, "w") as f:
|
||||||
json.dump([], f)
|
json.dump([], f)
|
||||||
|
|
||||||
@Slot(str, str, str, result=str)
|
@Slot(str, str, str, result=str)
|
||||||
@@ -46,38 +105,81 @@ class WebviewBridge(QObject):
|
|||||||
file_data = base64.b64decode(file_content)
|
file_data = base64.b64decode(file_content)
|
||||||
|
|
||||||
# 创建临时文件来保存上传的ZIP文件
|
# 创建临时文件来保存上传的ZIP文件
|
||||||
with tempfile.NamedTemporaryFile(suffix='.zip', delete=False) as tmp_file:
|
with tempfile.NamedTemporaryFile(suffix=".zip", delete=False) as tmp_file:
|
||||||
tmp_file.write(file_data)
|
tmp_file.write(file_data)
|
||||||
tmp_file_path = tmp_file.name
|
tmp_file_path = tmp_file.name
|
||||||
|
|
||||||
|
# 判断插件是否已存在
|
||||||
|
if os.path.exists(self.plugins_dir / name):
|
||||||
|
os.unlink(tmp_file_path)
|
||||||
|
return json.dumps({"success": False, "error": "插件名称已存在"})
|
||||||
|
|
||||||
# 解压插件
|
# 解压插件
|
||||||
success, error = self.extract_plugin(tmp_file_path, name)
|
success, msg = self.extract_plugin(tmp_file_path, name)
|
||||||
|
|
||||||
if not success:
|
if not success:
|
||||||
# 清理临时文件
|
# 清理临时文件
|
||||||
os.unlink(tmp_file_path)
|
os.unlink(tmp_file_path)
|
||||||
return json.dumps({"success": False, "error": error})
|
return json.dumps({"success": False, "error": msg})
|
||||||
|
|
||||||
|
# 读取 plugin.config.json 文件
|
||||||
|
plugin_config_file = msg.replace("index.html", "plugin.config.json")
|
||||||
|
if not os.path.exists(plugin_config_file):
|
||||||
|
os.unlink(tmp_file_path)
|
||||||
|
return json.dumps({"success": False, "error": "插件配置文件不存在"})
|
||||||
|
|
||||||
|
with open(plugin_config_file, "r") as f:
|
||||||
|
plugin_config = json.load(f)
|
||||||
|
|
||||||
|
plugin_conofig_keys = [
|
||||||
|
"pluginName",
|
||||||
|
"pluginId",
|
||||||
|
"pluginDescription",
|
||||||
|
"pluginVersion",
|
||||||
|
"pluginAuthor",
|
||||||
|
]
|
||||||
|
|
||||||
|
for key in plugin_conofig_keys:
|
||||||
|
if key not in plugin_config:
|
||||||
|
os.unlink(tmp_file_path)
|
||||||
|
return json.dumps(
|
||||||
|
{"success": False, "error": "插件配置文件缺少必要字段"}
|
||||||
|
)
|
||||||
|
|
||||||
plugin_info = {
|
plugin_info = {
|
||||||
"id": int(time.time()), # 使用时间戳作为简单ID
|
"id": plugin_config["pluginId"],
|
||||||
"name": name,
|
"name": name,
|
||||||
|
"pluginName": plugin_config["pluginName"],
|
||||||
"description": description,
|
"description": description,
|
||||||
"version": "1.0.0",
|
"version": plugin_config["pluginVersion"],
|
||||||
"uploadDate": time.strftime('%Y-%m-%d', time.localtime()),
|
"author": plugin_config["pluginAuthor"],
|
||||||
"path": str(self.plugins_dir / name)
|
"uploadDate": time.strftime("%Y-%m-%d", time.localtime()),
|
||||||
|
"path": str(self.plugins_dir / name / plugin_config["pluginVersion"]),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# 创建版本路径并移动插件文件
|
||||||
|
version_path = self.plugins_dir / name / plugin_info["version"]
|
||||||
|
version_path.mkdir(exist_ok=True)
|
||||||
|
for file in os.listdir(os.path.dirname(msg)):
|
||||||
|
shutil.move(os.path.join(os.path.dirname(msg), file), version_path)
|
||||||
|
|
||||||
# 读取现有插件信息
|
# 读取现有插件信息
|
||||||
if self.plugins_info_file.exists():
|
if os.path.exists(self.plugins_info_file):
|
||||||
with open(self.plugins_info_file, 'r') as f:
|
with open(self.plugins_info_file, "r") as f:
|
||||||
plugins = json.load(f)
|
plugins = json.load(f)
|
||||||
else:
|
else:
|
||||||
plugins = []
|
plugins = []
|
||||||
|
|
||||||
|
# 判断pluginId是否存在
|
||||||
|
if plugin_info["id"] in [p["id"] for p in plugins]:
|
||||||
|
os.unlink(tmp_file_path)
|
||||||
|
return json.dumps({"success": False, "error": "插件ID已存在"})
|
||||||
|
|
||||||
# 添加新插件
|
# 添加新插件
|
||||||
plugins.append(plugin_info)
|
plugins.append(plugin_info)
|
||||||
|
|
||||||
# 保存插件信息
|
# 保存插件信息
|
||||||
with open(self.plugins_info_file, 'w') as f:
|
with open(self.plugins_info_file, "w") as f:
|
||||||
json.dump(plugins, f)
|
json.dump(plugins, f)
|
||||||
|
|
||||||
# 清理临时文件
|
# 清理临时文件
|
||||||
@@ -88,11 +190,94 @@ class WebviewBridge(QObject):
|
|||||||
# 确保任何异常都返回有效的JSON
|
# 确保任何异常都返回有效的JSON
|
||||||
return json.dumps({"success": False, "error": str(e)})
|
return json.dumps({"success": False, "error": str(e)})
|
||||||
|
|
||||||
|
@Slot(str, result=str)
|
||||||
|
def update_plugin(self, plugin_id, name, description, file_content):
|
||||||
|
"""处理插件更新请求"""
|
||||||
|
try:
|
||||||
|
file_data = base64.b64decode(file_content)
|
||||||
|
|
||||||
|
with tempfile.NamedTemporaryFile(suffix=".zip", delete=False) as tmp_file:
|
||||||
|
tmp_file.write(file_data)
|
||||||
|
tmp_filepath = tmp_file.name
|
||||||
|
|
||||||
|
success, msg = self.extract_plugin(tmp_filepath, name)
|
||||||
|
|
||||||
|
if not success:
|
||||||
|
os.unlink(tmp_filepath)
|
||||||
|
return json.dumps({"success": False, "error": msg})
|
||||||
|
|
||||||
|
plugin_config_file = msg.replace("index.html", "plugin.config.json")
|
||||||
|
if not os.path.exists(plugin_config_file):
|
||||||
|
os.unlink(tmp_filepath)
|
||||||
|
return json.dumps({"success": False, "error": "插件配置文件不存在"})
|
||||||
|
|
||||||
|
with open(plugin_config_file, "r") as f:
|
||||||
|
plugin_config = json.load(f)
|
||||||
|
|
||||||
|
plugin_config_keys = [
|
||||||
|
"pluginName",
|
||||||
|
"pluginId",
|
||||||
|
"pluginDescription",
|
||||||
|
"pluginVersion",
|
||||||
|
"pluginAuthor",
|
||||||
|
]
|
||||||
|
|
||||||
|
for key in plugin_config_keys:
|
||||||
|
if key not in plugin_config:
|
||||||
|
os.unlink(tmp_filepath)
|
||||||
|
return json.dumps(
|
||||||
|
{"success": False, "error": "插件配置文件缺少必要字段"}
|
||||||
|
)
|
||||||
|
|
||||||
|
# 判断插件是否存在
|
||||||
|
if not os.path.exists(self.plugins_dir / name):
|
||||||
|
os.unlink(tmp_filepath)
|
||||||
|
return json.dumps({"success": False, "error": "插件不存在"})
|
||||||
|
|
||||||
|
# 判断版本是否已存在
|
||||||
|
version_path = self.plugins_dir / name / plugin_config["pluginVersion"]
|
||||||
|
if version_path.exists():
|
||||||
|
os.unlink(tmp_filepath)
|
||||||
|
return json.dumps({"success": False, "error": "版本已存在"})
|
||||||
|
|
||||||
|
# 创建版本路径并移动插件文件
|
||||||
|
version_path.mkdir(exist_ok=True)
|
||||||
|
for file in os.listdir(os.path.dirname(msg)):
|
||||||
|
shutil.move(os.path.join(os.path.dirname(msg), file), version_path)
|
||||||
|
|
||||||
|
# 更新插件信息
|
||||||
|
with open(self.plugins_info_file, "r") as f:
|
||||||
|
plugins = json.load(f)
|
||||||
|
|
||||||
|
plugin_info = next((p for p in plugins if p["id"] == plugin_id), None)
|
||||||
|
if plugin_info:
|
||||||
|
plugin_info["name"] = name
|
||||||
|
plugin_info["description"] = description
|
||||||
|
plugin_info["version"] = [
|
||||||
|
p["version"] for p in plugins if p["id"] == plugin_id
|
||||||
|
][0]
|
||||||
|
plugin_info["uploadDate"] = time.strftime("%Y-%m-%d", time.localtime())
|
||||||
|
plugin_info["path"] = str(
|
||||||
|
self.plugins_dir / name / plugin_config["pluginVersion"]
|
||||||
|
)
|
||||||
|
|
||||||
|
# 保存插件信息
|
||||||
|
with open(self.plugins_info_file, "w") as f:
|
||||||
|
json.dump(plugins, f)
|
||||||
|
|
||||||
|
# 清理临时文件
|
||||||
|
os.unlink(tmp_filepath)
|
||||||
|
|
||||||
|
return json.dumps({"success": True})
|
||||||
|
except Exception as e:
|
||||||
|
# 确保任何异常都返回有效的JSON
|
||||||
|
return json.dumps({"success": False, "error": str(e)})
|
||||||
|
|
||||||
# 解压插件
|
# 解压插件
|
||||||
def extract_plugin(self, plugin_file, plugin_name):
|
def extract_plugin(self, plugin_file, plugin_name):
|
||||||
"""解压插件"""
|
"""解压插件"""
|
||||||
try:
|
try:
|
||||||
with zipfile.ZipFile(plugin_file, 'r') as zf:
|
with zipfile.ZipFile(plugin_file, "r") as zf:
|
||||||
# 获取插件名称
|
# 获取插件名称
|
||||||
plugin_dir = self.plugins_dir / plugin_name
|
plugin_dir = self.plugins_dir / plugin_name
|
||||||
|
|
||||||
@@ -113,7 +298,7 @@ class WebviewBridge(QObject):
|
|||||||
return False, "插件中未找到HTML文件"
|
return False, "插件中未找到HTML文件"
|
||||||
index_file = html_files[0]
|
index_file = html_files[0]
|
||||||
|
|
||||||
return True, ""
|
return True, str(index_file.absolute())
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return False, str(e)
|
return False, str(e)
|
||||||
|
|
||||||
@@ -121,8 +306,8 @@ class WebviewBridge(QObject):
|
|||||||
def list_plugins(self):
|
def list_plugins(self):
|
||||||
"""获取插件列表"""
|
"""获取插件列表"""
|
||||||
try:
|
try:
|
||||||
if self.plugins_info_file.exists():
|
if os.path.exists(self.plugins_info_file):
|
||||||
with open(self.plugins_info_file, 'r') as f:
|
with open(self.plugins_info_file, "r") as f:
|
||||||
plugins = json.load(f)
|
plugins = json.load(f)
|
||||||
return json.dumps({"success": True, "plugins": plugins})
|
return json.dumps({"success": True, "plugins": plugins})
|
||||||
else:
|
else:
|
||||||
@@ -131,16 +316,17 @@ class WebviewBridge(QObject):
|
|||||||
# 确保任何异常都返回有效的JSON
|
# 确保任何异常都返回有效的JSON
|
||||||
return json.dumps({"success": False, "error": str(e)})
|
return json.dumps({"success": False, "error": str(e)})
|
||||||
|
|
||||||
@Slot(int, result=str)
|
@Slot(str, result=str)
|
||||||
def launch_plugin(self, plugin_id):
|
def launch_plugin(self, plugin_id):
|
||||||
"""启动插件"""
|
"""启动插件"""
|
||||||
try:
|
try:
|
||||||
if self.plugins_info_file.exists():
|
if os.path.exists(self.plugins_info_file):
|
||||||
with open(self.plugins_info_file, 'r') as f:
|
with open(self.plugins_info_file, "r") as f:
|
||||||
plugins = json.load(f)
|
plugins = json.load(f)
|
||||||
|
|
||||||
# 查找插件
|
# 查找插件
|
||||||
plugin = next((p for p in plugins if p["id"] == plugin_id), None)
|
plugin = next((p for p in plugins if p["id"] == plugin_id), None)
|
||||||
|
print(f"Launching plugin: {plugin}")
|
||||||
if plugin:
|
if plugin:
|
||||||
# 构建插件URL
|
# 构建插件URL
|
||||||
plugin_path = Path(plugin["path"])
|
plugin_path = Path(plugin["path"])
|
||||||
@@ -150,10 +336,13 @@ class WebviewBridge(QObject):
|
|||||||
index_file = html_files[0]
|
index_file = html_files[0]
|
||||||
url = QUrl.fromLocalFile(str(index_file.absolute()))
|
url = QUrl.fromLocalFile(str(index_file.absolute()))
|
||||||
print(f"Launching plugin: {url.toString()}")
|
print(f"Launching plugin: {url.toString()}")
|
||||||
|
print(f"Plugin path: {plugin_path}")
|
||||||
self.webview.load(url)
|
self.webview.load(url)
|
||||||
return json.dumps({"success": True, "url": url.toString()})
|
return json.dumps({"success": True, "url": url.toString()})
|
||||||
else:
|
else:
|
||||||
return json.dumps({"success": False, "error": "插件入口文件未找到"})
|
return json.dumps(
|
||||||
|
{"success": False, "error": "插件入口文件未找到"}
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
return json.dumps({"success": False, "error": "插件未找到"})
|
return json.dumps({"success": False, "error": "插件未找到"})
|
||||||
else:
|
else:
|
||||||
@@ -174,6 +363,7 @@ class WebviewBridge(QObject):
|
|||||||
# 确保任何异常都返回有效的JSON
|
# 确保任何异常都返回有效的JSON
|
||||||
return json.dumps({"success": False, "error": str(e)})
|
return json.dumps({"success": False, "error": str(e)})
|
||||||
|
|
||||||
|
|
||||||
class WebviewController(QObject):
|
class WebviewController(QObject):
|
||||||
"""Webview控制器类,负责管理Webview的逻辑"""
|
"""Webview控制器类,负责管理Webview的逻辑"""
|
||||||
|
|
||||||
@@ -181,6 +371,8 @@ class WebviewController(QObject):
|
|||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self.page = web_engine_page
|
self.page = web_engine_page
|
||||||
self.bridge = WebviewBridge(web_webview)
|
self.bridge = WebviewBridge(web_webview)
|
||||||
|
self.queue_sqlite_operations = WebViewQueueSqliteOperations(web_webview)
|
||||||
self.channel = QWebChannel()
|
self.channel = QWebChannel()
|
||||||
self.channel.registerObject('py', self.bridge)
|
self.channel.registerObject("entrance", self.bridge)
|
||||||
|
self.channel.registerObject("queueSqlite", self.queue_sqlite_operations)
|
||||||
self.page.setWebChannel(self.channel)
|
self.page.setWebChannel(self.channel)
|
||||||
|
|||||||
@@ -2,12 +2,21 @@
|
|||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from PySide6.QtWidgets import QFrame, QVBoxLayout, QMenu, QApplication, QWidget, QPushButton, QHBoxLayout
|
|
||||||
from qframelesswindow.webengine import FramelessWebEngineView
|
|
||||||
from PySide6.QtCore import QPoint, Qt, QUrl
|
|
||||||
|
|
||||||
from ...config import config
|
from PySide6.QtWidgets import (
|
||||||
|
QFrame,
|
||||||
|
QVBoxLayout,
|
||||||
|
QMenu,
|
||||||
|
QApplication,
|
||||||
|
QWidget,
|
||||||
|
QPushButton,
|
||||||
|
QHBoxLayout,
|
||||||
|
)
|
||||||
|
from PySide6.QtCore import QPoint, Qt, QUrl
|
||||||
|
from qframelesswindow.webengine import FramelessWebEngineView
|
||||||
|
|
||||||
from .entrance import WebviewController
|
from .entrance import WebviewController
|
||||||
|
from ...config import config
|
||||||
|
|
||||||
|
|
||||||
class DevToolsWindow(FramelessWebEngineView):
|
class DevToolsWindow(FramelessWebEngineView):
|
||||||
@@ -16,6 +25,7 @@ class DevToolsWindow(FramelessWebEngineView):
|
|||||||
self.setWindowTitle("开发者工具")
|
self.setWindowTitle("开发者工具")
|
||||||
self.resize(600, 600)
|
self.resize(600, 600)
|
||||||
|
|
||||||
|
|
||||||
class DevToolsView(QWidget):
|
class DevToolsView(QWidget):
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
super().__init__(parent=parent)
|
super().__init__(parent=parent)
|
||||||
@@ -25,6 +35,7 @@ class DevToolsView(QWidget):
|
|||||||
# 设置窗口大小不可变
|
# 设置窗口大小不可变
|
||||||
self.setFixedSize(self.size())
|
self.setFixedSize(self.size())
|
||||||
|
|
||||||
|
|
||||||
class WebViewInterface(QFrame):
|
class WebViewInterface(QFrame):
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
@@ -39,8 +50,13 @@ class WebViewInterface(QFrame):
|
|||||||
self.plugins_dir.mkdir(exist_ok=True)
|
self.plugins_dir.mkdir(exist_ok=True)
|
||||||
|
|
||||||
self.webView = FramelessWebEngineView(self)
|
self.webView = FramelessWebEngineView(self)
|
||||||
self.html_path = os.getcwd().replace("\\", '/') + '/resources/webview/entrance/dist/index.html'
|
self.html_path = (
|
||||||
self.remote_url = "http://localhost:8080/"
|
os.getcwd().replace("\\", "/")
|
||||||
|
+ "/resources/webview/entrance/dist/index.html"
|
||||||
|
)
|
||||||
|
self.remote_url = config.get("project", {}).get(
|
||||||
|
"remote-url", "http://localhost:8080/"
|
||||||
|
)
|
||||||
|
|
||||||
# 创建WebviewController实例
|
# 创建WebviewController实例
|
||||||
self.page = self.webView.page()
|
self.page = self.webView.page()
|
||||||
@@ -115,7 +131,9 @@ class WebViewInterface(QFrame):
|
|||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
nav_button_style = button_style + """
|
nav_button_style = (
|
||||||
|
button_style
|
||||||
|
+ """
|
||||||
QPushButton {
|
QPushButton {
|
||||||
background-color: #6c757d;
|
background-color: #6c757d;
|
||||||
}
|
}
|
||||||
@@ -126,8 +144,11 @@ class WebViewInterface(QFrame):
|
|||||||
background-color: #495057;
|
background-color: #495057;
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
toggle_button_style = button_style + """
|
toggle_button_style = (
|
||||||
|
button_style
|
||||||
|
+ """
|
||||||
QPushButton {
|
QPushButton {
|
||||||
background-color: #0d6efd;
|
background-color: #0d6efd;
|
||||||
}
|
}
|
||||||
@@ -138,6 +159,7 @@ class WebViewInterface(QFrame):
|
|||||||
background-color: #0a58ca;
|
background-color: #0a58ca;
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
self.back_button.setStyleSheet(nav_button_style)
|
self.back_button.setStyleSheet(nav_button_style)
|
||||||
self.forward_button.setStyleSheet(nav_button_style)
|
self.forward_button.setStyleSheet(nav_button_style)
|
||||||
@@ -218,96 +240,6 @@ class WebViewInterface(QFrame):
|
|||||||
self.dev_tools_view.dev_tools_window.show()
|
self.dev_tools_view.dev_tools_window.show()
|
||||||
self.dev_tools_view.dev_tools_window.raise_() # 将窗口置于最前
|
self.dev_tools_view.dev_tools_window.raise_() # 将窗口置于最前
|
||||||
|
|
||||||
def generate_plugins_page(self):
|
|
||||||
"""生成插件列表页面"""
|
|
||||||
plugins = []
|
|
||||||
for item in self.plugins_dir.iterdir():
|
|
||||||
if item.is_dir():
|
|
||||||
index_files = list(item.glob("*.html"))
|
|
||||||
if index_files:
|
|
||||||
plugins.append({
|
|
||||||
'name': item.name,
|
|
||||||
'path': f"file:///{item.absolute()}/{index_files[0].name}",
|
|
||||||
'description': f"插件 {item.name}"
|
|
||||||
})
|
|
||||||
|
|
||||||
# 生成HTML页面
|
|
||||||
html = """
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>插件管理</title>
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
font-family: Arial, sans-serif;
|
|
||||||
margin: 20px;
|
|
||||||
background-color: #f5f5f5;
|
|
||||||
}
|
|
||||||
.header {
|
|
||||||
text-align: center;
|
|
||||||
margin-bottom: 30px;
|
|
||||||
}
|
|
||||||
.plugins-grid {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
|
||||||
gap: 20px;
|
|
||||||
}
|
|
||||||
.plugin-card {
|
|
||||||
background: white;
|
|
||||||
border-radius: 8px;
|
|
||||||
padding: 20px;
|
|
||||||
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
|
||||||
}
|
|
||||||
.plugin-card h3 {
|
|
||||||
margin-top: 0;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
.plugin-card p {
|
|
||||||
color: #666;
|
|
||||||
}
|
|
||||||
.plugin-card a {
|
|
||||||
display: inline-block;
|
|
||||||
margin-top: 10px;
|
|
||||||
padding: 8px 16px;
|
|
||||||
background-color: #007bff;
|
|
||||||
color: white;
|
|
||||||
text-decoration: none;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
.plugin-card a:hover {
|
|
||||||
background-color: #0056b3;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="header">
|
|
||||||
<h1>插件管理</h1>
|
|
||||||
<p>当前可用插件列表</p>
|
|
||||||
</div>
|
|
||||||
<div class="plugins-grid">
|
|
||||||
"""
|
|
||||||
|
|
||||||
if not plugins:
|
|
||||||
html += '<p>暂无插件,请上传插件。</p>'
|
|
||||||
else:
|
|
||||||
for plugin in plugins:
|
|
||||||
html += f"""
|
|
||||||
<div class="plugin-card">
|
|
||||||
<h3>{plugin['name']}</h3>
|
|
||||||
<p>{plugin['description']}</p>
|
|
||||||
<a href="{plugin['path']}" target="_blank">访问插件</a>
|
|
||||||
</div>
|
|
||||||
"""
|
|
||||||
|
|
||||||
html += """
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
"""
|
|
||||||
|
|
||||||
return html
|
|
||||||
|
|
||||||
def closeEvent(self, event):
|
def closeEvent(self, event):
|
||||||
"""关闭时清理资源"""
|
"""关闭时清理资源"""
|
||||||
# 停止controller中的定时器
|
# 停止controller中的定时器
|
||||||
|
|||||||
@@ -1,21 +1,18 @@
|
|||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
from pathlib import Path
|
|
||||||
from PySide6.QtWidgets import QApplication
|
from PySide6.QtWidgets import QApplication
|
||||||
from app.view import Window
|
from app.view import Window
|
||||||
from app.scheduler_manager import scheduler
|
from app.scheduler_manager import scheduler
|
||||||
|
|
||||||
def main():
|
def resource_path(relative_path):
|
||||||
# # 设置插件路径 - 关键修复
|
"""获取资源的绝对路径。用于PyInstaller/Nuitka打包后定位资源文件。"""
|
||||||
# if getattr(sys, 'frozen', False):
|
if hasattr(sys, '_MEIPASS'):
|
||||||
# # 打包后的路径
|
return os.path.join(sys._MEIPASS, relative_path) # type: ignore
|
||||||
# base_dir = Path(sys._MEIPASS)
|
return os.path.join(os.path.abspath("."), relative_path)
|
||||||
# else:
|
|
||||||
# # 开发环境路径
|
|
||||||
# base_dir = Path(__file__).parent
|
|
||||||
|
|
||||||
# # 设置Qt插件路径
|
def main():
|
||||||
# os.environ['QT_PLUGIN_PATH'] = str(base_dir / 'plugins')
|
# 设置基础路径
|
||||||
|
base_dir = os.path.abspath(".")
|
||||||
|
|
||||||
# 启动队列调度器
|
# 启动队列调度器
|
||||||
scheduler.start_queue_scheduler()
|
scheduler.start_queue_scheduler()
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
# 是否是生产环境
|
# 是否是生产环境
|
||||||
is-production = false
|
is-production = false
|
||||||
|
remote-url = "http://localhost:8080/"
|
||||||
|
|
||||||
[[tool.uv.index]]
|
[[tool.uv.index]]
|
||||||
default = true
|
default = true
|
||||||
|
|||||||
+500
-89
File diff suppressed because it is too large
Load Diff
@@ -10,6 +10,8 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"core-js": "^3.8.3",
|
"core-js": "^3.8.3",
|
||||||
"qwebchannel": "^6.2.0",
|
"qwebchannel": "^6.2.0",
|
||||||
|
"sass": "^1.92.1",
|
||||||
|
"sass-loader": "^16.0.5",
|
||||||
"vue": "^3.2.13"
|
"vue": "^3.2.13"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"pluginName": "WebView Entrance",
|
||||||
|
"pluginId": "com.webview.entrance",
|
||||||
|
"pluginDescription": "This plugin is used to show the entrance of the webview.",
|
||||||
|
"pluginVersion": "1.0.0",
|
||||||
|
"pluginAuthor": "chakcy"
|
||||||
|
}
|
||||||
+1007
-410
File diff suppressed because it is too large
Load Diff
@@ -1,58 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="hello">
|
|
||||||
<h1>{{ msg }}</h1>
|
|
||||||
<p>
|
|
||||||
For a guide and recipes on how to configure / customize this project,<br>
|
|
||||||
check out the
|
|
||||||
<a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
|
|
||||||
</p>
|
|
||||||
<h3>Installed CLI Plugins</h3>
|
|
||||||
<ul>
|
|
||||||
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a></li>
|
|
||||||
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint" target="_blank" rel="noopener">eslint</a></li>
|
|
||||||
</ul>
|
|
||||||
<h3>Essential Links</h3>
|
|
||||||
<ul>
|
|
||||||
<li><a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li>
|
|
||||||
<li><a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li>
|
|
||||||
<li><a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li>
|
|
||||||
<li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li>
|
|
||||||
<li><a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li>
|
|
||||||
</ul>
|
|
||||||
<h3>Ecosystem</h3>
|
|
||||||
<ul>
|
|
||||||
<li><a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li>
|
|
||||||
<li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li>
|
|
||||||
<li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li>
|
|
||||||
<li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li>
|
|
||||||
<li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
name: 'HelloWorld',
|
|
||||||
props: {
|
|
||||||
msg: String
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
|
||||||
<style scoped>
|
|
||||||
h3 {
|
|
||||||
margin: 40px 0 0;
|
|
||||||
}
|
|
||||||
ul {
|
|
||||||
list-style-type: none;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
li {
|
|
||||||
display: inline-block;
|
|
||||||
margin: 0 10px;
|
|
||||||
}
|
|
||||||
a {
|
|
||||||
color: #42b983;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
.DS_Store
|
||||||
|
node_modules
|
||||||
|
/dist
|
||||||
|
|
||||||
|
|
||||||
|
# local env files
|
||||||
|
.env.local
|
||||||
|
.env.*.local
|
||||||
|
|
||||||
|
# Log files
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.idea
|
||||||
|
.vscode
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
# new_plugin
|
||||||
|
|
||||||
|
## Project setup
|
||||||
|
```
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
|
||||||
|
### Compiles and hot-reloads for development
|
||||||
|
```
|
||||||
|
npm run serve
|
||||||
|
```
|
||||||
|
|
||||||
|
### Compiles and minifies for production
|
||||||
|
```
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
### Lints and fixes files
|
||||||
|
```
|
||||||
|
npm run lint
|
||||||
|
```
|
||||||
|
|
||||||
|
### Customize configuration
|
||||||
|
See [Configuration Reference](https://cli.vuejs.org/config/).
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
module.exports = {
|
||||||
|
presets: [
|
||||||
|
'@vue/cli-plugin-babel/preset'
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es5",
|
||||||
|
"module": "esnext",
|
||||||
|
"baseUrl": "./",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"paths": {
|
||||||
|
"@/*": [
|
||||||
|
"src/*"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"lib": [
|
||||||
|
"esnext",
|
||||||
|
"dom",
|
||||||
|
"dom.iterable",
|
||||||
|
"scripthost"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
+12356
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,46 @@
|
|||||||
|
{
|
||||||
|
"name": "new_plugin",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"serve": "node scripts/copy-plugin-config.js && vue-cli-service serve",
|
||||||
|
"build": "node scripts/copy-plugin-config.js && vue-cli-service build",
|
||||||
|
"lint": "node scripts/copy-plugin-config.js && vue-cli-service lint"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"core-js": "^3.8.3",
|
||||||
|
"pinia": "^3.0.3",
|
||||||
|
"pinia-plugin-persistedstate": "^4.5.0",
|
||||||
|
"qwebchannel": "^6.2.0",
|
||||||
|
"vue": "^3.2.13"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/core": "^7.12.16",
|
||||||
|
"@babel/eslint-parser": "^7.12.16",
|
||||||
|
"@vue/cli-plugin-babel": "~5.0.0",
|
||||||
|
"@vue/cli-plugin-eslint": "~5.0.0",
|
||||||
|
"@vue/cli-service": "~5.0.0",
|
||||||
|
"eslint": "^7.32.0",
|
||||||
|
"eslint-plugin-vue": "^8.0.3"
|
||||||
|
},
|
||||||
|
"eslintConfig": {
|
||||||
|
"root": true,
|
||||||
|
"env": {
|
||||||
|
"node": true
|
||||||
|
},
|
||||||
|
"extends": [
|
||||||
|
"plugin:vue/vue3-essential",
|
||||||
|
"eslint:recommended"
|
||||||
|
],
|
||||||
|
"parserOptions": {
|
||||||
|
"parser": "@babel/eslint-parser"
|
||||||
|
},
|
||||||
|
"rules": {}
|
||||||
|
},
|
||||||
|
"browserslist": [
|
||||||
|
"> 1%",
|
||||||
|
"last 2 versions",
|
||||||
|
"not dead",
|
||||||
|
"not ie 11"
|
||||||
|
]
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
@@ -0,0 +1,17 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||||
|
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||||
|
<title><%= htmlWebpackPlugin.options.title %></title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<noscript>
|
||||||
|
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
||||||
|
</noscript>
|
||||||
|
<div id="app"></div>
|
||||||
|
<!-- built files will be auto injected -->
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"pluginName": "new plugin",
|
||||||
|
"pluginId": "com.example.newplugin",
|
||||||
|
"pluginDescription": "This is a new plugin",
|
||||||
|
"pluginVersion": "1.0.0",
|
||||||
|
"pluginAuthor": "chakcy"
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
// 定义源文件和目标文件路径
|
||||||
|
const srcConfigPath = path.resolve(__dirname, '../src/config/plugin.config.json');
|
||||||
|
const publicDirPath = path.resolve(__dirname, '../public');
|
||||||
|
const publicConfigPath = path.resolve(publicDirPath, 'plugin.config.json');
|
||||||
|
|
||||||
|
// 检查源文件是否存在
|
||||||
|
if (fs.existsSync(srcConfigPath)) {
|
||||||
|
// 确保public目录存在
|
||||||
|
if (!fs.existsSync(publicDirPath)) {
|
||||||
|
fs.mkdirSync(publicDirPath, { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 复制文件到public目录
|
||||||
|
fs.copyFileSync(srcConfigPath, publicConfigPath);
|
||||||
|
console.log('Successfully copied plugin.config.json to public directory');
|
||||||
|
} else {
|
||||||
|
console.error('Source plugin.config.json not found at:', srcConfigPath);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
<template>
|
||||||
|
<img alt="Vue logo" src="./assets/logo.png">
|
||||||
|
<HelloWorld msg="Welcome to Your Vue.js App"/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import HelloWorld from './components/HelloWorld.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'App',
|
||||||
|
components: {
|
||||||
|
HelloWorld
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
#app {
|
||||||
|
font-family: Avenir, Helvetica, Arial, sans-serif;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
text-align: center;
|
||||||
|
color: #2c3e50;
|
||||||
|
margin-top: 60px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 6.7 KiB |
@@ -0,0 +1,57 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<h2>任务状态</h2>
|
||||||
|
{{ queueStore.queueData }}
|
||||||
|
<div v-if="queueStore.queueData.taskStatus">
|
||||||
|
<p>当前状态: {{ queueStore.queueData.taskStatus }}</p>
|
||||||
|
<p>处理结果: {{ queueStore.queueData.taskResult }}</p>
|
||||||
|
</div>
|
||||||
|
<button @click="sendTask">发送任务</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { useQueueStore } from '@/stores/queue'
|
||||||
|
import { onMounted } from 'vue'
|
||||||
|
import { QWebChannel } from 'qwebchannel';
|
||||||
|
|
||||||
|
const queueStore = useQueueStore()
|
||||||
|
|
||||||
|
let queueSqlite = null;
|
||||||
|
|
||||||
|
// 定义全局函数用于更新状态
|
||||||
|
window.updateQueueState = (key, value) => {
|
||||||
|
try {
|
||||||
|
console.log('更新状态:', key, value);
|
||||||
|
queueStore.updateQueueData(key, value);
|
||||||
|
} catch (e) {
|
||||||
|
console.error('更新状态时出错:', e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (window.qt && window.qt.webChannelTransport) {
|
||||||
|
new QWebChannel(window.qt.webChannelTransport, function (channel) {
|
||||||
|
queueSqlite = channel.objects.queueSqlite;
|
||||||
|
console.log("queueSqlite", queueSqlite)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 这个函数会通过 QWebChannel 调用 Python 端代码
|
||||||
|
const sendTask = () => {
|
||||||
|
// 调用 Python 端的 send_message 方法
|
||||||
|
if (queueSqlite && queueSqlite.send_message) {
|
||||||
|
// 发从 message 以及 pinia store 名称
|
||||||
|
queueSqlite.send_message(
|
||||||
|
JSON.stringify({
|
||||||
|
"content": {"num": 1},
|
||||||
|
"destination": "test"
|
||||||
|
}),
|
||||||
|
"taskStatus"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 当 Python 端执行回调并更新状态时,页面会自动显示最新状态
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"pluginName": "new plugin",
|
||||||
|
"pluginId": "com.example.newplugin",
|
||||||
|
"pluginDescription": "This is a new plugin",
|
||||||
|
"pluginVersion": "1.0.0",
|
||||||
|
"pluginAuthor": "chakcy"
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import { createApp } from 'vue'
|
||||||
|
import { createPinia } from 'pinia'
|
||||||
|
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
|
||||||
|
import App from './App.vue'
|
||||||
|
import { useQueueStore } from './stores/queue'
|
||||||
|
|
||||||
|
const app = createApp(App)
|
||||||
|
const pinia = createPinia()
|
||||||
|
pinia.use(piniaPluginPersistedstate)
|
||||||
|
|
||||||
|
app.use(pinia)
|
||||||
|
app.mount('#app')
|
||||||
|
|
||||||
|
window.queueStore = useQueueStore()
|
||||||
|
window.pluginConfig = await fetch('./plugin.config.json').then(res => res.json())
|
||||||
|
console.log('Loaded plugin config:', window.pluginConfig)
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
// src/stores/index.js 或 main.js 中
|
||||||
|
import { createPinia } from 'pinia'
|
||||||
|
|
||||||
|
const pinia = createPinia()
|
||||||
|
|
||||||
|
export default pinia
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import { defineStore } from 'pinia'
|
||||||
|
|
||||||
|
export const useQueueStore = defineStore('queueSqlite', {
|
||||||
|
state: () => ({
|
||||||
|
// 存储队列相关状态
|
||||||
|
queueData: {},
|
||||||
|
// 其他状态...
|
||||||
|
}),
|
||||||
|
persist: true, // 开启持久化
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
updateQueueData(key, value) {
|
||||||
|
this.queueData[key] = value
|
||||||
|
},
|
||||||
|
|
||||||
|
// 其他相关操作...
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
const { defineConfig } = require('@vue/cli-service')
|
||||||
|
module.exports = defineConfig({
|
||||||
|
transpileDependencies: true,
|
||||||
|
publicPath: './'
|
||||||
|
})
|
||||||
@@ -8,4 +8,5 @@ python -m nuitka ^
|
|||||||
--output-dir=output ^
|
--output-dir=output ^
|
||||||
--remove-output ^
|
--remove-output ^
|
||||||
--jobs=4 ^
|
--jobs=4 ^
|
||||||
|
--output-filename="pyside_queue_sqlite.exe" ^
|
||||||
main.py
|
main.py
|
||||||
+24
-14
@@ -1,28 +1,38 @@
|
|||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
|
||||||
def generate_markdown_from_py_files(directory, output_file):
|
def generate_markdown_from_py_files(directory, output_file):
|
||||||
with open(output_file, 'w', encoding='utf-8') as md_file:
|
with open(output_file, "w", encoding="utf-8") as md_file:
|
||||||
for root, dirs, files in os.walk(directory):
|
for root, dirs, files in os.walk(directory):
|
||||||
# 排除 venv 目录
|
# 排除 venv 目录
|
||||||
dirs[:] = [d for d in dirs if d != '.venv']
|
dirs[:] = [d for d in dirs if d != ".venv"]
|
||||||
dirs[:] = [d for d in dirs if d != '.vscode']
|
dirs[:] = [d for d in dirs if d != ".vscode"]
|
||||||
dirs[:] = [d for d in dirs if d != 'scripts']
|
dirs[:] = [d for d in dirs if d != "scripts"]
|
||||||
dirs[:] = [d for d in dirs if d != 'build']
|
dirs[:] = [d for d in dirs if d != "build"]
|
||||||
dirs[:] = [d for d in dirs if d != 'lib']
|
dirs[:] = [d for d in dirs if d != "lib"]
|
||||||
|
dirs[:] = [d for d in dirs if d != "dist"]
|
||||||
|
dirs[:] = [d for d in dirs if d != "node_modules"]
|
||||||
|
dirs[:] = [d for d in dirs if d != "plugins"]
|
||||||
for file in files:
|
for file in files:
|
||||||
if file.endswith('.py') or file.endswith('.rs'):
|
if (
|
||||||
|
file.endswith(".py")
|
||||||
|
or file.endswith(".rs")
|
||||||
|
or file.endswith(".js")
|
||||||
|
or file.endswith(".vue")
|
||||||
|
):
|
||||||
file_path = os.path.join(root, file)
|
file_path = os.path.join(root, file)
|
||||||
md_file.write(f'`{file_path}`\n')
|
md_file.write(f"`{file_path}`\n")
|
||||||
md_file.write('```python\n')
|
md_file.write("```python\n")
|
||||||
with open(file_path, 'r', encoding='utf-8') as py_file:
|
with open(file_path, "r", encoding="utf-8") as py_file:
|
||||||
md_file.write(py_file.read())
|
md_file.write(py_file.read())
|
||||||
md_file.write('\n```\n\n')
|
md_file.write("\n```\n\n")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
# 指定目录和输出文件名
|
# 指定目录和输出文件名
|
||||||
target_directory = sys.argv[1] # 替换为你的目标目录
|
target_directory = sys.argv[1] # 替换为你的目标目录
|
||||||
output_markdown_file = 'output.md' # 输出的 Markdown 文件名
|
output_markdown_file = "output.md" # 输出的 Markdown 文件名
|
||||||
|
|
||||||
generate_markdown_from_py_files(target_directory, output_markdown_file)
|
generate_markdown_from_py_files(target_directory, output_markdown_file)
|
||||||
print(f'Markdown 文件已生成:{output_markdown_file}')
|
print(f"Markdown 文件已生成:{output_markdown_file}")
|
||||||
|
|||||||
Reference in New Issue
Block a user