更新样式

This commit is contained in:
李志强 2026-05-11 14:27:31 +08:00
parent dbd28458b8
commit e30c74007b
3 changed files with 1233 additions and 6 deletions

Binary file not shown.

991
layout/main1.ui Normal file
View File

@ -0,0 +1,991 @@
<?xml version='1.0' encoding='utf-8'?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>900</width>
<height>650</height>
</rect>
</property>
<property name="windowTitle">
<string>牛马Cursor登录器</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QVBoxLayout" name="verticalLayout_central">
<property name="leftMargin">
<number>8</number>
</property>
<property name="topMargin">
<number>8</number>
</property>
<property name="rightMargin">
<number>8</number>
</property>
<property name="bottomMargin">
<number>8</number>
</property>
<item>
<widget class="QSplitter" name="splitterMainLog">
<property name="orientation">
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="childrenCollapsible">
<bool>false</bool>
</property>
<widget class="QWidget" name="widgetMainArea">
<layout class="QVBoxLayout" name="verticalLayout_mainArea">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QSplitter" name="splitterBody">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="childrenCollapsible">
<bool>false</bool>
</property>
<widget class="QListWidget" name="tabList">
<property name="minimumSize">
<size>
<width>150</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>220</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">QListWidget {
border: 1px solid palette(mid);
border-radius: 4px;
background: palette(base);
color: palette(text);
outline: none;
}
QListWidget::item {
min-height: 38px;
padding: 8px 12px;
border-bottom: 1px solid palette(midlight);
}
QListWidget::item:selected {
color: palette(highlighted-text);
background: palette(highlight);
}
QListWidget::item:hover:!selected {
background: palette(alternate-base);
}</string>
</property>
<property name="currentRow">
<number>0</number>
</property>
<item>
<property name="text">
<string>Token切换</string>
</property>
</item>
<item>
<property name="text">
<string>一键续杯</string>
</property>
</item>
<item>
<property name="text">
<string>Cursor 配置</string>
</property>
</item>
<item>
<property name="text">
<string>帮助</string>
</property>
</item>
</widget>
<widget class="QStackedWidget" name="contentStack">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="pageLogin">
<layout class="QVBoxLayout" name="verticalLayout_pageLogin">
<item>
<widget class="QGroupBox" name="groupToken">
<property name="title">
<string>Token 输入</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_token">
<item>
<widget class="QTextEdit" name="txtToken">
<property name="placeholderText">
<string>请粘贴 Cursor Token 到这里...</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_tokenActions">
<item>
<widget class="QPushButton" name="btnChange">
<property name="minimumSize">
<size>
<width>0</width>
<height>50</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>50</height>
</size>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
<bold>true</bold>
</font>
</property>
<property name="styleSheet">
<string notr="true">QPushButton {
background-color: #0078D4;
color: white;
border-radius: 5px;
}
QPushButton:hover {
background-color: #106EBE;
}
QPushButton:pressed {
background-color: #005A9E;
}</string>
</property>
<property name="text">
<string>🚀 开始换号</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnOnlineShop">
<property name="minimumSize">
<size>
<width>160</width>
<height>50</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>220</width>
<height>50</height>
</size>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
<bold>true</bold>
</font>
</property>
<property name="styleSheet">
<string notr="true">QPushButton {
background-color: #FF9800;
color: white;
border-radius: 5px;
}
QPushButton:hover {
background-color: #F57C00;
}
QPushButton:pressed {
background-color: #E65100;
}</string>
</property>
<property name="text">
<string>在线购买Token</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QWidget" name="pageRenewal">
<layout class="QVBoxLayout" name="verticalLayout_pageRenewal">
<item>
<widget class="QGroupBox" name="groupDeviceId">
<property name="title">
<string>设备号</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_deviceId">
<item>
<widget class="QLineEdit" name="txtDeviceId">
<property name="readOnly">
<bool>true</bool>
</property>
<property name="placeholderText">
<string>设备号将在程序启动后自动生成...</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnCopyDeviceId">
<property name="minimumSize">
<size>
<width>88</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>复制</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupActivationCode">
<property name="title">
<string>激活码</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_activationCode">
<item>
<widget class="QLabel" name="lblActivationCode">
<property name="text">
<string>激活码:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="txtActivationCode">
<property name="placeholderText">
<string>请输入激活码...</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnActivateRenewal">
<property name="minimumSize">
<size>
<width>88</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>激活</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnBuyActivationCode">
<property name="minimumSize">
<size>
<width>88</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>在线购买</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupMemberStatus">
<property name="title">
<string>会员状态</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_memberStatus">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_memberStatusHeader">
<item>
<widget class="QLabel" name="lblMemberStatus">
<property name="text">
<string>当前状态:未刷新</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_memberStatus">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="btnRefreshMemberStatus">
<property name="minimumSize">
<size>
<width>88</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>刷新</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QFormLayout" name="formLayout_memberInfo">
<item row="0" column="0">
<widget class="QLabel" name="lblMemberLevelTitle">
<property name="text">
<string>会员等级:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="lblMemberLevel">
<property name="text">
<string>-</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="lblAccountTypeTitle">
<property name="text">
<string>账号类型:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="lblAccountType">
<property name="text">
<string>-</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="lblActivatedAtTitle">
<property name="text">
<string>激活时间:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="lblActivatedAt">
<property name="text">
<string>-</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="lblExpiredAtTitle">
<property name="text">
<string>到期时间:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLabel" name="lblExpiredAt">
<property name="text">
<string>-</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_oneClickRenewal">
<item>
<widget class="QPushButton" name="btnOneClickRenewal">
<property name="minimumSize">
<size>
<width>444</width>
<height>48</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>48</height>
</size>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
<bold>true</bold>
</font>
</property>
<property name="styleSheet">
<string notr="true">QPushButton {
background-color: #D83B01;
color: white;
border-radius: 5px;
}
QPushButton:hover {
background-color: #C23900;
}
QPushButton:pressed {
background-color: #A4262C;
}</string>
</property>
<property name="text">
<string>一键续杯</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="lblRenewalWarning">
<property name="styleSheet">
<string notr="true">color: #D83B01; font-weight: bold;</string>
</property>
<property name="text">
<string>请用完后再点击续杯,如发现大量未使用完就续杯情况,一律封号处理,请悉知!</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_renewal">
<property name="orientation">
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<widget class="QWidget" name="pageCursorConfig">
<layout class="QVBoxLayout" name="verticalLayout_pageCursorConfig">
<item>
<widget class="QGroupBox" name="groupCursor">
<property name="title">
<string>Cursor 路径</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_cursorPath">
<item>
<widget class="QLabel" name="lblCursor">
<property name="text">
<string>路径:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="txtCursorPath">
<property name="placeholderText">
<string>请选择 Cursor 安装路径...</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnBrowseCursor">
<property name="text">
<string>浏览</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnAutoCursorPath">
<property name="minimumSize">
<size>
<width>88</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>自动查找</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnOpenCursor">
<property name="minimumSize">
<size>
<width>0</width>
<height>40</height>
</size>
</property>
<property name="text">
<string>打开 Cursor</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_cursorConfig">
<property name="orientation">
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<widget class="QWidget" name="pageHelp">
<layout class="QHBoxLayout" name="horizontalLayout_pageHelp">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QListWidget" name="helpTabList">
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>190</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">QListWidget {
border: 1px solid palette(mid);
border-radius: 4px;
background: palette(base);
color: palette(text);
outline: none;
}
QListWidget::item {
min-height: 34px;
padding: 8px 12px;
border-bottom: 1px solid palette(midlight);
}
QListWidget::item:selected {
color: palette(highlighted-text);
background: palette(highlight);
}
QListWidget::item:hover:!selected {
background: palette(alternate-base);
}</string>
</property>
<property name="currentRow">
<number>0</number>
</property>
<item>
<property name="text">
<string>应急检修</string>
</property>
</item>
<item>
<property name="text">
<string>使用说明</string>
</property>
</item>
<item>
<property name="text">
<string>捐赠支持</string>
</property>
</item>
<item>
<property name="text">
<string>关于软件</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QStackedWidget" name="helpContentStack">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="pageHelpEmergency">
<layout class="QVBoxLayout" name="verticalLayout_helpEmergency">
<item>
<widget class="QGroupBox" name="groupHelpEmergency">
<property name="title">
<string>应急检修</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_helpEmergencyContent">
<item>
<widget class="QLabel" name="lblHelpEmergencyDesc">
<property name="text">
<string>打开应急检修工具面板,可进行 DB Browser 下载、清除 Cursor 缓存、Token 提取等操作。</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnEmergencyRepair">
<property name="minimumSize">
<size>
<width>0</width>
<height>42</height>
</size>
</property>
<property name="text">
<string>打开应急检修</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer_helpEmergency">
<property name="orientation">
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<widget class="QWidget" name="pageHelpUsageGuide">
<layout class="QVBoxLayout" name="verticalLayout_helpUsageGuide">
<item>
<widget class="QGroupBox" name="groupHelpUsageGuide">
<property name="title">
<string>使用说明</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_helpUsageGuideContent">
<item>
<widget class="QLabel" name="lblHelpUsageGuideDesc">
<property name="text">
<string>查看软件使用说明图片,支持滚轮查看和缩放。</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnUsageGuide">
<property name="minimumSize">
<size>
<width>0</width>
<height>42</height>
</size>
</property>
<property name="text">
<string>打开使用说明</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer_helpUsageGuide">
<property name="orientation">
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<widget class="QWidget" name="pageHelpDonate">
<layout class="QVBoxLayout" name="verticalLayout_helpDonate">
<item>
<widget class="QGroupBox" name="groupHelpDonate">
<property name="title">
<string>捐赠支持</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_helpDonateContent">
<item>
<widget class="QLabel" name="lblHelpDonateDesc">
<property name="text">
<string>打开捐赠支持窗口,查看微信和支付宝捐赠二维码。</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnDonate">
<property name="minimumSize">
<size>
<width>0</width>
<height>42</height>
</size>
</property>
<property name="text">
<string>打开捐赠支持</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer_helpDonate">
<property name="orientation">
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<widget class="QWidget" name="pageHelpAbout">
<layout class="QVBoxLayout" name="verticalLayout_helpAbout">
<item>
<widget class="QGroupBox" name="groupHelpAbout">
<property name="title">
<string>关于软件</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_helpAboutContent">
<item>
<widget class="QLabel" name="lblHelpAboutDesc">
<property name="text">
<string>查看当前软件版本、用途和交流群信息。</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnAbout">
<property name="minimumSize">
<size>
<width>0</width>
<height>42</height>
</size>
</property>
<property name="text">
<string>查看关于软件</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer_helpAbout">
<property name="orientation">
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
</widget>
</widget>
</item>
</layout>
</widget>
<widget class="QGroupBox" name="groupLog">
<property name="minimumSize">
<size>
<width>0</width>
<height>140</height>
</size>
</property>
<property name="title">
<string>日志展示</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_log">
<item>
<widget class="QTextEdit" name="txtLog">
<property name="readOnly">
<bool>true</bool>
</property>
<property name="placeholderText">
<string>日志输出...</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_logButtons">
<item>
<spacer name="horizontalSpacer_log">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="btnCheckUpdate">
<property name="minimumSize">
<size>
<width>88</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>检查更新</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnClearLog">
<property name="minimumSize">
<size>
<width>88</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>清空日志</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>900</width>
<height>33</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
<property name="title">
<string>设置</string>
</property>
<addaction name="actionExit" />
</widget>
<addaction name="menuFile" />
</widget>
<widget class="QStatusBar" name="statusbar">
<property name="sizeGripEnabled">
<bool>true</bool>
</property>
</widget>
<action name="actionExit">
<property name="text">
<string>退出</string>
</property>
</action>
<action name="actionEmergencyRepair">
<property name="text">
<string>应急检修</string>
</property>
</action>
<action name="actionUsageGuide">
<property name="text">
<string>使用说明</string>
</property>
</action>
<action name="actionDonate">
<property name="text">
<string>捐赠支持</string>
</property>
</action>
<action name="actionAbout">
<property name="text">
<string>关于软件</string>
</property>
</action>
</widget>
<resources />
<connections>
<connection>
<sender>tabList</sender>
<signal>currentRowChanged(int)</signal>
<receiver>contentStack</receiver>
<slot>setCurrentIndex(int)</slot>
<hints>
<hint type="sourcelabel">
<x>93</x>
<y>120</y>
</hint>
<hint type="destinationlabel">
<x>420</x>
<y>120</y>
</hint>
</hints>
</connection>
<connection>
<sender>helpTabList</sender>
<signal>currentRowChanged(int)</signal>
<receiver>helpContentStack</receiver>
<slot>setCurrentIndex(int)</slot>
<hints>
<hint type="sourcelabel">
<x>220</x>
<y>180</y>
</hint>
<hint type="destinationlabel">
<x>520</x>
<y>180</y>
</hint>
</hints>
</connection>
</connections>
</ui>

248
main.py
View File

@ -1,6 +1,7 @@
import sys import sys
import json import json
import base64 import base64
import hashlib
import shutil import shutil
import uuid import uuid
import random import random
@ -20,7 +21,7 @@ from datetime import datetime
from pathlib import Path from pathlib import Path
from typing import Optional from typing import Optional
__VERSION__ = "0.0.6" __VERSION__ = "0.0.7"
from PySide6.QtWidgets import ( from PySide6.QtWidgets import (
QApplication, QApplication,
@ -42,7 +43,7 @@ from PySide6.QtWidgets import (
) )
from PySide6.QtCore import QThread, Signal, Qt, QTimer, QMetaObject, Q_ARG, Slot from PySide6.QtCore import QThread, Signal, Qt, QTimer, QMetaObject, Q_ARG, Slot
from PySide6.QtUiTools import QUiLoader from PySide6.QtUiTools import QUiLoader
from PySide6.QtGui import QFont, QPixmap, QColor, QPainter, QPalette, QIcon from PySide6.QtGui import QFont, QPixmap, QColor, QPainter, QPalette, QIcon, QAction
def get_resource_path(relative_path): def get_resource_path(relative_path):
@ -147,6 +148,104 @@ def generate_machine_id():
return str(uuid.uuid4()) return str(uuid.uuid4())
def _run_command_text(args):
"""执行系统命令并返回文本输出,失败时返回空字符串。"""
try:
creationflags = getattr(subprocess, "CREATE_NO_WINDOW", 0) if sys.platform == "win32" else 0
return subprocess.check_output(
args,
stderr=subprocess.DEVNULL,
stdin=subprocess.DEVNULL,
text=True,
encoding="utf-8",
errors="ignore",
creationflags=creationflags,
).strip()
except Exception:
return ""
def _get_wmic_value(alias: str, field: str) -> str:
"""通过 WMIC 获取硬件字段值,兼容空值/表头输出。"""
output = _run_command_text(["wmic", alias, "get", field, "/value"])
for line in output.splitlines():
line = line.strip()
prefix = f"{field}="
if line.startswith(prefix):
value = line[len(prefix):].strip()
if value and value.lower() not in ("none", "null", "to be filled by o.e.m."):
return value
output = _run_command_text(["wmic", alias, "get", field])
lines = [line.strip() for line in output.splitlines() if line.strip()]
for line in lines[1:]:
if line and line.lower() not in ("none", "null", "to be filled by o.e.m."):
return line
return ""
def _get_windows_cim_value(class_name: str, field: str) -> str:
"""WMIC 不可用时,通过 PowerShell CIM 获取硬件字段值。"""
command = (
f"(Get-CimInstance {class_name} | "
f"Select-Object -First 1 -ExpandProperty {field})"
)
value = _run_command_text([
"powershell",
"-NoProfile",
"-ExecutionPolicy",
"Bypass",
"-Command",
command,
]).strip()
if value and value.lower() not in ("none", "null", "to be filled by o.e.m."):
return value
return ""
def _get_cpu_id() -> str:
"""获取 CPU 标识。"""
if sys.platform == "win32":
return (
_get_wmic_value("cpu", "ProcessorId")
or _get_windows_cim_value("Win32_Processor", "ProcessorId")
or os.environ.get("PROCESSOR_IDENTIFIER", "").strip()
)
return os.environ.get("PROCESSOR_IDENTIFIER", "").strip() or os.uname().machine
def _get_baseboard_id() -> str:
"""获取主板标识。"""
if sys.platform == "win32":
serial = (
_get_wmic_value("baseboard", "SerialNumber")
or _get_windows_cim_value("Win32_BaseBoard", "SerialNumber")
)
product = (
_get_wmic_value("baseboard", "Product")
or _get_windows_cim_value("Win32_BaseBoard", "Product")
)
manufacturer = (
_get_wmic_value("baseboard", "Manufacturer")
or _get_windows_cim_value("Win32_BaseBoard", "Manufacturer")
)
return "|".join(part for part in (manufacturer, product, serial) if part)
return socket.gethostname()
def get_renewal_device_id():
"""生成用于一键续杯的本机设备号CPU + MAC + 主板。"""
try:
cpu_id = _get_cpu_id()
mac = f"{uuid.getnode():012x}"
baseboard_id = _get_baseboard_id()
raw = f"cpu={cpu_id}|mac={mac}|baseboard={baseboard_id}"
digest = hashlib.sha256(raw.encode("utf-8")).digest()
return base64.urlsafe_b64encode(digest).decode("ascii").rstrip("=")
except Exception:
return str(uuid.uuid4())
def get_cursor_config_path(): def get_cursor_config_path():
home = Path.home() home = Path.home()
if sys.platform == "win32": if sys.platform == "win32":
@ -727,7 +826,7 @@ class MainWindow(QMainWindow):
self._splash_pulse("正在加载") self._splash_pulse("正在加载")
# 获取UI文件路径 # 获取UI文件路径
ui_path = get_resource_path(os.path.join("layout", "main.ui")) ui_path = get_resource_path(os.path.join("layout", "main1.ui"))
# 详细的调试信息 # 详细的调试信息
debug_info = f"UI文件路径: {ui_path}\n" debug_info = f"UI文件路径: {ui_path}\n"
@ -802,6 +901,8 @@ class MainWindow(QMainWindow):
self.txtToken = self.findChild(QTextEdit, "txtToken") self.txtToken = self.findChild(QTextEdit, "txtToken")
self.txtLog = self.findChild(QTextEdit, "txtLog") self.txtLog = self.findChild(QTextEdit, "txtLog")
self.txtCursorPath = self.findChild(QLineEdit, "txtCursorPath") self.txtCursorPath = self.findChild(QLineEdit, "txtCursorPath")
self.txtDeviceId = self.findChild(QLineEdit, "txtDeviceId")
self.txtActivationCode = self.findChild(QLineEdit, "txtActivationCode")
self.btnChange = self.findChild(QPushButton, "btnChange") self.btnChange = self.findChild(QPushButton, "btnChange")
self.btnOpenCursor = self.findChild(QPushButton, "btnOpenCursor") self.btnOpenCursor = self.findChild(QPushButton, "btnOpenCursor")
self.btnOnlineShop = self.findChild(QPushButton, "btnOnlineShop") self.btnOnlineShop = self.findChild(QPushButton, "btnOnlineShop")
@ -813,6 +914,22 @@ class MainWindow(QMainWindow):
self.btnCheckUpdate = self.findChild(QPushButton, "btnCheckUpdate") self.btnCheckUpdate = self.findChild(QPushButton, "btnCheckUpdate")
self.btnEmergencyRepair = self.findChild(QPushButton, "btnEmergencyRepair") self.btnEmergencyRepair = self.findChild(QPushButton, "btnEmergencyRepair")
self.btnUsageGuide = self.findChild(QPushButton, "btnUsageGuide") self.btnUsageGuide = self.findChild(QPushButton, "btnUsageGuide")
self.btnAbout = self.findChild(QPushButton, "btnAbout")
self.btnCopyDeviceId = self.findChild(QPushButton, "btnCopyDeviceId")
self.btnActivateRenewal = self.findChild(QPushButton, "btnActivateRenewal")
self.btnBuyActivationCode = self.findChild(QPushButton, "btnBuyActivationCode")
self.btnRefreshMemberStatus = self.findChild(QPushButton, "btnRefreshMemberStatus")
self.btnOneClickRenewal = self.findChild(QPushButton, "btnOneClickRenewal")
self.lblMemberStatus = self.findChild(QLabel, "lblMemberStatus")
self.lblMemberLevel = self.findChild(QLabel, "lblMemberLevel")
self.lblAccountType = self.findChild(QLabel, "lblAccountType")
self.lblActivatedAt = self.findChild(QLabel, "lblActivatedAt")
self.lblExpiredAt = self.findChild(QLabel, "lblExpiredAt")
self.actionExit = self.findChild(QAction, "actionExit")
self.actionEmergencyRepair = self.findChild(QAction, "actionEmergencyRepair")
self.actionUsageGuide = self.findChild(QAction, "actionUsageGuide")
self.actionDonate = self.findChild(QAction, "actionDonate")
self.actionAbout = self.findChild(QAction, "actionAbout")
self._load_cached_logs_to_ui() self._load_cached_logs_to_ui()
# 调试信息 # 调试信息
@ -829,15 +946,21 @@ class MainWindow(QMainWindow):
# 设置默认Cursor路径 # 设置默认Cursor路径
if self.txtCursorPath: if self.txtCursorPath:
self.txtCursorPath.setText(get_default_cursor_path()) self.txtCursorPath.setText(get_default_cursor_path())
if self.txtDeviceId:
self.txtDeviceId.setText(get_renewal_device_id())
self._reset_member_status_display()
# 为在线商城按钮设置图标(使用 Qt 内置图标,避免依赖外部资源) # 为在线商城按钮设置图标(使用 Qt 内置图标,避免依赖外部资源)
if self.btnOnlineShop: if self.btnOnlineShop or self.btnBuyActivationCode:
shop_icon = QIcon.fromTheme("shopping-cart") shop_icon = QIcon.fromTheme("shopping-cart")
if shop_icon.isNull(): if shop_icon.isNull():
shop_icon = QIcon.fromTheme("emblem-sales") shop_icon = QIcon.fromTheme("emblem-sales")
if shop_icon.isNull(): if shop_icon.isNull():
shop_icon = self.style().standardIcon(QStyle.SP_DialogOpenButton) shop_icon = self.style().standardIcon(QStyle.SP_DialogOpenButton)
self.btnOnlineShop.setIcon(shop_icon) if self.btnOnlineShop:
self.btnOnlineShop.setIcon(shop_icon)
if self.btnBuyActivationCode:
self.btnBuyActivationCode.setIcon(shop_icon)
# 信号连接 # 信号连接
if self.btnChange: if self.btnChange:
@ -862,6 +985,28 @@ class MainWindow(QMainWindow):
self.btnEmergencyRepair.clicked.connect(self.on_emergency_repair_clicked) self.btnEmergencyRepair.clicked.connect(self.on_emergency_repair_clicked)
if self.btnUsageGuide: if self.btnUsageGuide:
self.btnUsageGuide.clicked.connect(self.on_usage_guide_clicked) self.btnUsageGuide.clicked.connect(self.on_usage_guide_clicked)
if self.btnAbout:
self.btnAbout.clicked.connect(self.on_about_clicked)
if self.btnCopyDeviceId:
self.btnCopyDeviceId.clicked.connect(self.on_copy_device_id_clicked)
if self.btnActivateRenewal:
self.btnActivateRenewal.clicked.connect(self.on_activate_renewal_clicked)
if self.btnBuyActivationCode:
self.btnBuyActivationCode.clicked.connect(self.on_open_online_shop_clicked)
if self.btnRefreshMemberStatus:
self.btnRefreshMemberStatus.clicked.connect(self.on_refresh_member_status_clicked)
if self.btnOneClickRenewal:
self.btnOneClickRenewal.clicked.connect(self.on_one_click_renewal_clicked)
if self.actionExit:
self.actionExit.triggered.connect(self.close)
if self.actionEmergencyRepair:
self.actionEmergencyRepair.triggered.connect(self.on_emergency_repair_clicked)
if self.actionUsageGuide:
self.actionUsageGuide.triggered.connect(self.on_usage_guide_clicked)
if self.actionDonate:
self.actionDonate.triggered.connect(self.on_donate_clicked)
if self.actionAbout:
self.actionAbout.triggered.connect(self.on_about_clicked)
# 设置状态栏左侧显示QQ群右侧显示版本号 # 设置状态栏左侧显示QQ群右侧显示版本号
qq_icon_label = QLabel() qq_icon_label = QLabel()
qq_icon = QIcon.fromTheme("im-qq") qq_icon = QIcon.fromTheme("im-qq")
@ -1232,6 +1377,86 @@ class MainWindow(QMainWindow):
self.log(f"❌ 打开AI中转站失败: {str(e)}") self.log(f"❌ 打开AI中转站失败: {str(e)}")
QMessageBox.warning(self, "警告", f"打开AI中转站失败: {str(e)}") QMessageBox.warning(self, "警告", f"打开AI中转站失败: {str(e)}")
def on_copy_device_id_clicked(self):
"""复制一键续杯设备号。"""
device_id = self.txtDeviceId.text().strip() if self.txtDeviceId else ""
if not device_id:
QMessageBox.warning(self, "提示", "设备号为空,无法复制。")
return
QApplication.clipboard().setText(device_id)
self.log("✅ 设备号已复制到剪贴板")
QMessageBox.information(self, "成功", "设备号已复制到剪贴板。")
def on_activate_renewal_clicked(self):
"""提交一键续杯激活码。"""
activation_code = self.txtActivationCode.text().strip() if self.txtActivationCode else ""
device_id = self.txtDeviceId.text().strip() if self.txtDeviceId else ""
if not activation_code:
QMessageBox.warning(self, "提示", "请输入激活码。")
return
self.log(f"🔑 正在激活一键续杯,设备号:{device_id}")
self.log(" 激活码已提交,请根据服务端接口补充实际激活逻辑。")
QMessageBox.information(self, "提示", "激活码已提交,当前版本尚未配置服务端激活接口。")
self.on_refresh_member_status_clicked()
def _reset_member_status_display(self):
"""重置会员状态展示。"""
if self.lblMemberStatus:
self.lblMemberStatus.setText("当前状态:未刷新")
if self.lblMemberLevel:
self.lblMemberLevel.setText("-")
if self.lblAccountType:
self.lblAccountType.setText("-")
if self.lblActivatedAt:
self.lblActivatedAt.setText("-")
if self.lblExpiredAt:
self.lblExpiredAt.setText("-")
def on_refresh_member_status_clicked(self):
"""刷新会员状态展示。"""
device_id = self.txtDeviceId.text().strip() if self.txtDeviceId else ""
if not device_id:
QMessageBox.warning(self, "提示", "设备号为空,无法刷新会员状态。")
return
if self.lblMemberStatus:
self.lblMemberStatus.setText("当前状态:待接入服务端")
if self.lblMemberLevel:
self.lblMemberLevel.setText("未激活")
if self.lblAccountType:
self.lblAccountType.setText("普通账号")
if self.lblActivatedAt:
self.lblActivatedAt.setText("-")
if self.lblExpiredAt:
self.lblExpiredAt.setText("-")
self.log("🔄 已刷新会员状态展示,当前版本尚未配置服务端查询接口。")
def on_one_click_renewal_clicked(self):
"""执行一键续杯。"""
device_id = self.txtDeviceId.text().strip() if self.txtDeviceId else ""
if not device_id:
QMessageBox.warning(self, "提示", "设备号为空,无法执行一键续杯。")
return
confirm = QMessageBox.question(
self,
"确认一键续杯",
"请确认当前额度已经用完后再续杯。\n\n"
"如发现大量未使用完就续杯情况,一律封号处理,请悉知!\n\n"
"确定要继续执行一键续杯吗?",
QMessageBox.Yes | QMessageBox.No,
QMessageBox.No,
)
if confirm != QMessageBox.Yes:
self.log(" 已取消一键续杯。")
return
self.log(f"☕ 正在执行一键续杯,设备号:{device_id}")
self.log(" 一键续杯请求已提交,请根据服务端接口补充实际续杯逻辑。")
QMessageBox.information(self, "提示", "一键续杯请求已提交,当前版本尚未配置服务端续杯接口。")
self.on_refresh_member_status_clicked()
def _extract_session_token(self, raw_value: str) -> str: def _extract_session_token(self, raw_value: str) -> str:
"""从输入文本中提取 SessionToken兼容纯 token / Cookie 串。""" """从输入文本中提取 SessionToken兼容纯 token / Cookie 串。"""
s = (raw_value or "").strip().strip('"').strip("'") s = (raw_value or "").strip().strip('"').strip("'")
@ -1344,6 +1569,17 @@ class MainWindow(QMainWindow):
dialog = DonateDialog(self) dialog = DonateDialog(self)
dialog.exec() dialog.exec()
def on_about_clicked(self):
"""显示关于软件信息。"""
QMessageBox.about(
self,
"关于软件",
f"牛马Cursor登录器\n\n"
f"当前版本:{__VERSION__}\n\n"
"用于 Cursor 账号登录、配置管理和常用工具操作。\n\n"
"QQ群720797421"
)
def on_usage_guide_clicked(self): def on_usage_guide_clicked(self):
"""打开使用说明图片(非模态,可与其他窗口并行)。""" """打开使用说明图片(非模态,可与其他窗口并行)。"""
if self._usage_guide_dialog and self._usage_guide_dialog.isVisible(): if self._usage_guide_dialog and self._usage_guide_dialog.isVisible():
@ -1735,4 +1971,4 @@ def main():
if __name__ == "__main__": if __name__ == "__main__":
main() main()