kindle manager

This commit is contained in:
douboer
2025-09-18 17:02:40 +08:00
parent 6df3ce42a3
commit 0e370533e7
14 changed files with 241 additions and 158 deletions

BIN
.DS_Store vendored

Binary file not shown.

4
.exclude Normal file
View File

@@ -0,0 +1,4 @@
backup
mobiparse
.git
My Clippings.txt.bk

8
.idea/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,8 @@
# 默认忽略的文件
/shelf/
/workspace.xml
# 基于编辑器的 HTTP 客户端请求
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

6
.idea/misc.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" languageLevel="JDK_22" default="true" project-jdk-name="openjdk-22" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

6
.idea/vcs.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

View File

@@ -1,4 +1,4 @@
pyside2-uic mainwindow.ui -o mainwindow.py pyside6-uic mainwindow.ui -o mainwindow.py
pyside2-rcc kmanapp.qrc -o kmanapp_rc.py pyside6-rcc kmanapp.qrc -o kmanapp_rc.py
cp -fr icons *py *md *ico *qrc *ui ~/penv/kman/ cp -fr icons *py *md *ico *qrc *ui ~/penv/kman/

View File

@@ -18,10 +18,10 @@ import threading
if hasattr(sys, 'frozen'): if hasattr(sys, 'frozen'):
os.environ['PATH'] = sys._MEIPASS + ";" + os.environ['PATH'] os.environ['PATH'] = sys._MEIPASS + ";" + os.environ['PATH']
from PySide2.QtWidgets import (QMainWindow, QApplication, QMessageBox, from PySide6.QtWidgets import (QMainWindow, QApplication, QMessageBox,
QFileDialog, QLabel, QAbstractItemView, QHeaderView, QMenu) QFileDialog, QLabel, QAbstractItemView, QHeaderView, QMenu)
from PySide2.QtCore import (QAbstractTableModel, Signal, QSize, QTimer, Qt) from PySide6.QtCore import (QAbstractTableModel, Signal, QSize, QTimer, Qt)
from PySide2.QtGui import (QPalette, QStandardItemModel, QStandardItem, QIcon) from PySide6.QtGui import (QPalette, QStandardItemModel, QStandardItem, QIcon)
from mainwindow import Ui_MainWindow from mainwindow import Ui_MainWindow
from kman import * from kman import *

View File

@@ -1,37 +1,52 @@
# -*- mode: python ; coding: utf-8 -*- # -*- mode: python ; coding: utf-8 -*-
block_cipher = None
a = Analysis(
a = Analysis(['kmanapp.spec\r'], ['kmanapp.py'],
pathex=['/Users/mark/kman'], pathex=[],
binaries=[], binaries=[],
datas=[], datas=[
('icons/Cbb20.png', 'icons'),
('icons/*.png', 'icons'),
('kmanapp.qrc', '.'),
('kmanapp_rc.py', '.'),
('vocab.db', '.'),
# 如有其他资源请补充
],
hiddenimports=[], hiddenimports=[],
hookspath=[], hookspath=[],
hooksconfig={},
runtime_hooks=[], runtime_hooks=[],
excludes=[], excludes=[],
win_no_prefer_redirects=False, noarchive=False,
win_private_assemblies=False, optimize=0,
cipher=block_cipher, )
noarchive=False) pyz = PYZ(a.pure)
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher) exe = EXE(
exe = EXE(pyz, pyz,
a.scripts, a.scripts,
a.binaries,
a.datas,
[], [],
exclude_binaries=True,
name='kmanapp', name='kmanapp',
debug=False, debug=False,
bootloader_ignore_signals=False, bootloader_ignore_signals=False,
strip=False, strip=False,
upx=True, upx=True,
console=True )
coll = COLLECT(exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=True,
upx_exclude=[], upx_exclude=[],
name='kmanapp') runtime_tmpdir=None,
console=False,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
icon=['icons/Cbb20.png'],
)
app = BUNDLE(
exe,
name='kmanapp.app',
icon='icons/Cbb20.png',
bundle_identifier=None,
)

View File

@@ -3,7 +3,7 @@
# Created by: The Resource Compiler for Qt version 5.15.0 # Created by: The Resource Compiler for Qt version 5.15.0
# WARNING! All changes made in this file will be lost! # WARNING! All changes made in this file will be lost!
from PySide2 import QtCore from PySide6 import QtCore
qt_resource_data = b"\ qt_resource_data = b"\
\x00\x00\x05\x8e\ \x00\x00\x05\x8e\

View File

@@ -3,80 +3,115 @@
################################################################################ ################################################################################
## Form generated from reading UI file 'mainwindow.ui' ## Form generated from reading UI file 'mainwindow.ui'
## ##
## Created by: Qt User Interface Compiler version 5.15.0 ## Created by: Qt User Interface Compiler version 6.9.1
## ##
## WARNING! All changes made in this file will be lost when recompiling UI file! ## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################ ################################################################################
from PySide2.QtCore import (QCoreApplication, QDate, QDateTime, QMetaObject, from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
QObject, QPoint, QRect, QSize, QTime, QUrl, Qt) QMetaObject, QObject, QPoint, QRect,
from PySide2.QtGui import (QBrush, QColor, QConicalGradient, QCursor, QFont, QSize, QTime, QUrl, Qt)
QFontDatabase, QIcon, QKeySequence, QLinearGradient, QPalette, QPainter, from PySide6.QtGui import (QAction, QBrush, QColor, QConicalGradient,
QPixmap, QRadialGradient) QCursor, QFont, QFontDatabase, QGradient,
from PySide2.QtWidgets import * QIcon, QImage, QKeySequence, QLinearGradient,
QPainter, QPalette, QPixmap, QRadialGradient,
QTransform)
from PySide6.QtWidgets import (QApplication, QComboBox, QGridLayout, QHBoxLayout,
QHeaderView, QLabel, QLineEdit, QMainWindow,
QMenuBar, QSizePolicy, QSplitter, QStatusBar,
QTableView, QTextBrowser, QToolBar, QToolButton,
QTreeView, QWidget)
import kmanapp_rc import kmanapp_rc
class Ui_MainWindow(object): class Ui_MainWindow(object):
def setupUi(self, MainWindow): def setupUi(self, MainWindow):
if not MainWindow.objectName(): if not MainWindow.objectName():
MainWindow.setObjectName(u"MainWindow") MainWindow.setObjectName(u"MainWindow")
MainWindow.resize(774, 410) MainWindow.resize(837, 622)
icon = QIcon()
icon.addFile(u":/icons/Cbb20.png", QSize(), QIcon.Mode.Normal, QIcon.State.On)
MainWindow.setWindowIcon(icon)
MainWindow.setIconSize(QSize(40, 40)) MainWindow.setIconSize(QSize(40, 40))
self.actionimportlocal = QAction(MainWindow) self.actionimportlocal = QAction(MainWindow)
self.actionimportlocal.setObjectName(u"actionimportlocal") self.actionimportlocal.setObjectName(u"actionimportlocal")
icon = QIcon() icon1 = QIcon()
icon.addFile(u":/icons/downr.png", QSize(), QIcon.Normal, QIcon.Off) icon1.addFile(u":/icons/downr.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionimportlocal.setIcon(icon) self.actionimportlocal.setIcon(icon1)
self.actionimportkindle = QAction(MainWindow) self.actionimportkindle = QAction(MainWindow)
self.actionimportkindle.setObjectName(u"actionimportkindle") self.actionimportkindle.setObjectName(u"actionimportkindle")
icon1 = QIcon() icon2 = QIcon()
icon1.addFile(u":/icons/kindle.png", QSize(), QIcon.Normal, QIcon.Off) icon2.addFile(u":/icons/kindle.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionimportkindle.setIcon(icon1) self.actionimportkindle.setIcon(icon2)
self.actionconfig = QAction(MainWindow) self.actionconfig = QAction(MainWindow)
self.actionconfig.setObjectName(u"actionconfig") self.actionconfig.setObjectName(u"actionconfig")
icon2 = QIcon() icon3 = QIcon()
icon2.addFile(u":/icons/config.png", QSize(), QIcon.Normal, QIcon.Off) icon3.addFile(u":/icons/config.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionconfig.setIcon(icon2) self.actionconfig.setIcon(icon3)
self.actionflush = QAction(MainWindow) self.actionflush = QAction(MainWindow)
self.actionflush.setObjectName(u"actionflush") self.actionflush.setObjectName(u"actionflush")
icon3 = QIcon() icon4 = QIcon()
icon3.addFile(u":/icons/refresh.png", QSize(), QIcon.Normal, QIcon.Off) icon4.addFile(u":/icons/refresh.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionflush.setIcon(icon3) self.actionflush.setIcon(icon4)
self.actionwords = QAction(MainWindow) self.actionwords = QAction(MainWindow)
self.actionwords.setObjectName(u"actionwords") self.actionwords.setObjectName(u"actionwords")
icon4 = QIcon() icon5 = QIcon()
icon4.addFile(u":/icons/books.png", QSize(), QIcon.Normal, QIcon.Off) icon5.addFile(u":/icons/books.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionwords.setIcon(icon4) self.actionwords.setIcon(icon5)
self.actionstatistic = QAction(MainWindow) self.actionstatistic = QAction(MainWindow)
self.actionstatistic.setObjectName(u"actionstatistic") self.actionstatistic.setObjectName(u"actionstatistic")
icon5 = QIcon() icon6 = QIcon()
icon5.addFile(u":/icons/statistics.png", QSize(), QIcon.Normal, QIcon.Off) icon6.addFile(u":/icons/statistics.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionstatistic.setIcon(icon5) self.actionstatistic.setIcon(icon6)
self.actionhomepage = QAction(MainWindow) self.actionhomepage = QAction(MainWindow)
self.actionhomepage.setObjectName(u"actionhomepage") self.actionhomepage.setObjectName(u"actionhomepage")
icon6 = QIcon() icon7 = QIcon()
icon6.addFile(u":/icons/web.png", QSize(), QIcon.Normal, QIcon.Off) icon7.addFile(u":/icons/web.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionhomepage.setIcon(icon6) self.actionhomepage.setIcon(icon7)
self.actionabout = QAction(MainWindow) self.actionabout = QAction(MainWindow)
self.actionabout.setObjectName(u"actionabout") self.actionabout.setObjectName(u"actionabout")
icon7 = QIcon() icon8 = QIcon()
icon7.addFile(u":/icons/question.png", QSize(), QIcon.Normal, QIcon.Off) icon8.addFile(u":/icons/question.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionabout.setIcon(icon7) self.actionabout.setIcon(icon8)
self.actionsearch = QAction(MainWindow) self.actionsearch = QAction(MainWindow)
self.actionsearch.setObjectName(u"actionsearch") self.actionsearch.setObjectName(u"actionsearch")
icon8 = QIcon() icon9 = QIcon()
icon8.addFile(u":/icons/Pixadex.png", QSize(), QIcon.Normal, QIcon.Off) icon9.addFile(u":/icons/Pixadex.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionsearch.setIcon(icon8) self.actionsearch.setIcon(icon9)
self.actionexport = QAction(MainWindow) self.actionexport = QAction(MainWindow)
self.actionexport.setObjectName(u"actionexport") self.actionexport.setObjectName(u"actionexport")
icon9 = QIcon() icon10 = QIcon()
icon9.addFile(u":/icons/md2.png", QSize(), QIcon.Normal, QIcon.Off) icon10.addFile(u":/icons/md2.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionexport.setIcon(icon9) self.actionexport.setIcon(icon10)
self.centralwidget = QWidget(MainWindow) self.centralwidget = QWidget(MainWindow)
self.centralwidget.setObjectName(u"centralwidget") self.centralwidget.setObjectName(u"centralwidget")
self.gridLayout = QGridLayout(self.centralwidget) self.gridLayout = QGridLayout(self.centralwidget)
self.gridLayout.setObjectName(u"gridLayout") self.gridLayout.setObjectName(u"gridLayout")
self.splitter_2 = QSplitter(self.centralwidget)
self.splitter_2.setObjectName(u"splitter_2")
self.splitter_2.setOrientation(Qt.Orientation.Horizontal)
self.treeView = QTreeView(self.splitter_2)
self.treeView.setObjectName(u"treeView")
sizePolicy = QSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.treeView.sizePolicy().hasHeightForWidth())
self.treeView.setSizePolicy(sizePolicy)
self.treeView.setMaximumSize(QSize(401, 16777215))
self.splitter_2.addWidget(self.treeView)
self.treeView.header().setVisible(False)
self.splitter = QSplitter(self.splitter_2)
self.splitter.setObjectName(u"splitter")
self.splitter.setOrientation(Qt.Orientation.Vertical)
self.tableView = QTableView(self.splitter)
self.tableView.setObjectName(u"tableView")
self.splitter.addWidget(self.tableView)
self.textEdit = QTextBrowser(self.splitter)
self.textEdit.setObjectName(u"textEdit")
self.splitter.addWidget(self.textEdit)
self.splitter_2.addWidget(self.splitter)
self.gridLayout.addWidget(self.splitter_2, 1, 0, 1, 1)
self.horizontalLayout = QHBoxLayout() self.horizontalLayout = QHBoxLayout()
self.horizontalLayout.setObjectName(u"horizontalLayout") self.horizontalLayout.setObjectName(u"horizontalLayout")
self.searchLabel = QLabel(self.centralwidget) self.searchLabel = QLabel(self.centralwidget)
@@ -96,57 +131,26 @@ class Ui_MainWindow(object):
self.searchToolButton = QToolButton(self.centralwidget) self.searchToolButton = QToolButton(self.centralwidget)
self.searchToolButton.setObjectName(u"searchToolButton") self.searchToolButton.setObjectName(u"searchToolButton")
icon10 = QIcon() icon11 = QIcon()
icon10.addFile(u":/icons/search.jpeg", QSize(), QIcon.Normal, QIcon.Off) icon11.addFile(u":/icons/search.jpeg", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.searchToolButton.setIcon(icon10) self.searchToolButton.setIcon(icon11)
self.horizontalLayout.addWidget(self.searchToolButton) self.horizontalLayout.addWidget(self.searchToolButton)
self.gridLayout.addLayout(self.horizontalLayout, 0, 0, 1, 1) self.gridLayout.addLayout(self.horizontalLayout, 0, 0, 1, 1)
self.splitter_2 = QSplitter(self.centralwidget)
self.splitter_2.setObjectName(u"splitter_2")
self.splitter_2.setOrientation(Qt.Horizontal)
self.treeView = QTreeView(self.splitter_2)
self.treeView.setObjectName(u"treeView")
sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.treeView.sizePolicy().hasHeightForWidth())
self.treeView.setSizePolicy(sizePolicy)
self.treeView.setMaximumSize(QSize(401, 16777215))
self.splitter_2.addWidget(self.treeView)
self.treeView.header().setVisible(False)
self.splitter = QSplitter(self.splitter_2)
self.splitter.setObjectName(u"splitter")
self.splitter.setOrientation(Qt.Vertical)
self.tableView = QTableView(self.splitter)
self.tableView.setObjectName(u"tableView")
sizePolicy1 = QSizePolicy(QSizePolicy.Maximum, QSizePolicy.Expanding)
sizePolicy1.setHorizontalStretch(0)
sizePolicy1.setVerticalStretch(0)
sizePolicy1.setHeightForWidth(self.tableView.sizePolicy().hasHeightForWidth())
self.tableView.setSizePolicy(sizePolicy1)
self.splitter.addWidget(self.tableView)
self.textEdit = QTextBrowser(self.splitter)
self.textEdit.setObjectName(u"textEdit")
self.splitter.addWidget(self.textEdit)
self.splitter_2.addWidget(self.splitter)
self.gridLayout.addWidget(self.splitter_2, 1, 0, 1, 1)
MainWindow.setCentralWidget(self.centralwidget) MainWindow.setCentralWidget(self.centralwidget)
self.statusbar = QStatusBar(MainWindow) self.statusbar = QStatusBar(MainWindow)
self.statusbar.setObjectName(u"statusbar") self.statusbar.setObjectName(u"statusbar")
MainWindow.setStatusBar(self.statusbar) MainWindow.setStatusBar(self.statusbar)
self.menuBar = QMenuBar(MainWindow) self.menuBar = QMenuBar(MainWindow)
self.menuBar.setObjectName(u"menuBar") self.menuBar.setObjectName(u"menuBar")
self.menuBar.setGeometry(QRect(0, 0, 774, 22)) self.menuBar.setGeometry(QRect(0, 0, 837, 32))
MainWindow.setMenuBar(self.menuBar) MainWindow.setMenuBar(self.menuBar)
self.toolBar = QToolBar(MainWindow) self.toolBar = QToolBar(MainWindow)
self.toolBar.setObjectName(u"toolBar") self.toolBar.setObjectName(u"toolBar")
MainWindow.addToolBar(Qt.TopToolBarArea, self.toolBar) MainWindow.addToolBar(Qt.ToolBarArea.TopToolBarArea, self.toolBar)
self.toolBar.addAction(self.actionimportkindle) self.toolBar.addAction(self.actionimportkindle)
self.toolBar.addAction(self.actionimportlocal) self.toolBar.addAction(self.actionimportlocal)

View File

@@ -6,13 +6,17 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>774</width> <width>837</width>
<height>410</height> <height>622</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
<string>Kindle Management</string> <string>Kindle Management</string>
</property> </property>
<property name="windowIcon">
<iconset resource="kmanapp.qrc">
<normaloff>:/icons/Cbb20.png</normaloff>:/icons/Cbb20.png</iconset>
</property>
<property name="iconSize"> <property name="iconSize">
<size> <size>
<width>40</width> <width>40</width>
@@ -21,6 +25,37 @@
</property> </property>
<widget class="QWidget" name="centralwidget"> <widget class="QWidget" name="centralwidget">
<layout class="QGridLayout" name="gridLayout"> <layout class="QGridLayout" name="gridLayout">
<item row="1" column="0">
<widget class="QSplitter" name="splitter_2">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<widget class="QTreeView" name="treeView">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>401</width>
<height>16777215</height>
</size>
</property>
<attribute name="headerVisible">
<bool>false</bool>
</attribute>
</widget>
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Orientation::Vertical</enum>
</property>
<widget class="QTableView" name="tableView"/>
<widget class="QTextBrowser" name="textEdit"/>
</widget>
</widget>
</item>
<item row="0" column="0"> <item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout"> <layout class="QHBoxLayout" name="horizontalLayout">
<item> <item>
@@ -57,44 +92,6 @@
</item> </item>
</layout> </layout>
</item> </item>
<item row="1" column="0">
<widget class="QSplitter" name="splitter_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="QTreeView" name="treeView">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>401</width>
<height>16777215</height>
</size>
</property>
<attribute name="headerVisible">
<bool>false</bool>
</attribute>
</widget>
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<widget class="QTableView" name="tableView">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
<widget class="QTextBrowser" name="textEdit"/>
</widget>
</widget>
</item>
</layout> </layout>
</widget> </widget>
<widget class="QStatusBar" name="statusbar"/> <widget class="QStatusBar" name="statusbar"/>
@@ -103,8 +100,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>774</width> <width>837</width>
<height>22</height> <height>32</height>
</rect> </rect>
</property> </property>
</widget> </widget>

View File

@@ -1,3 +1,3 @@
git add . git add .
git commit -m "kindle manager" git commit -m "kindle manager"
git push origin master git push origin main

View File

@@ -9,7 +9,7 @@ from setuptools import setup
APP = ['kmanapp.py'] APP = ['kmanapp.py']
DATA_FILES = ['kman.py', 'kmanapp.py', 'kmanapp_rc.py', 'mainwindow.py', 'parseweb.py', 'unitest.kman.py'] DATA_FILES = ['kman.py', 'kmanapp.py', 'kmanapp_rc.py', 'mainwindow.py', 'parseweb.py', 'unitest.kman.py']
OPTIONS = {'includes':['PySide2', 'pandas', 'requests',], OPTIONS = {'includes':['PySide6', 'pandas', 'requests',],
'iconfile':'/Users/mark/kman/kmanapp.ico'} 'iconfile':'/Users/mark/kman/kmanapp.ico'}
setup( setup(

43
打包MacBook指南.md Normal file
View File

@@ -0,0 +1,43 @@
# MacBook 打包 PySide6 应用指南
## 1. 安装 PyInstaller
```zsh
pip install pyinstaller
```
## 2. 打包命令(推荐单文件)
```zsh
pyinstaller --noconfirm --windowed --onefile --icon=icons/Cbb20.png kmanapp.py
```
- `--windowed`无控制台窗口GUI应用必选
- `--onefile`:生成单一可执行文件
- `--icon=...`:指定应用图标
## 3. 资源文件处理
如有图片、qrc、数据库等资源需在 `kmanapp.spec``datas` 字段添加:
```python
# 在 .spec 文件的 a = Analysis(...) 里 datas= 加入:
('icons/Cbb20.png', 'icons'),
('kmanapp.qrc', '.'),
('kmanapp_rc.py', '.'),
('vocab.db', '.'),
# 其他资源...
```
## 4. 重新打包
```zsh
pyinstaller kmanapp.spec
```
## 5. 运行
打包后在 `dist/` 目录下生成可执行文件,双击即可运行。
---
如需自动化脚本或遇到打包报错,请告知具体需求。