<think>根据报错信息,问题出现在第480行附近的`setStyleSheet`方法中的字符串没有正确闭合。在代码中,我们注意到`ChatWidget`类的`init_ui`方法中,`self.send_btn`的样式表字符串从第480行开始,但报错在第489行检测到未终止的三引号字符串。这通常是因为字符串内部包含未转义的三引号或者字符串确实没有正确结束。
检查代码发现,在`self.send_btn`的样式表字符串之后,还有其他的样式表设置(如`self.entry`的样式表)都是独立的三引号字符串,因此问题可能出现在`self.send_btn`的样式表字符串内部。
观察`self.send_btn`的样式表字符串:
```python
self.send_btn.setStyleSheet("""
QPushButton {
background-color: #9B59B6;
color: white;
""")
```
这里只有两个三引号,但实际上应该有三个三引号来结束字符串。正确的写法应该是三个三引号。
因此,修复方法是在字符串末尾添加三个三引号,即:
```python
self.send_btn.setStyleSheet("""
QPushButton {
background-color: #9B59B6;
color: white;
}
""")
```
但是,请注意,在原始代码中,这个样式表字符串后面紧接着的是`self.kb_toggle_btn`的样式表设置,而`self.send_btn`的样式表字符串并没有正确结束,导致编译器认为后面的代码仍然在字符串内,直到第489行才报错。
因此,我们需要将`self.send_btn`的样式表字符串补充完整,并确保后面的代码不在字符串内。
下面我将提供修复后的完整代码。由于代码较长,我将重点修改有问题的部分,并重新生成整个文件。
修改点:
1. 在`self.send_btn`的样式表字符串末尾添加缺失的`"""`,并确保样式规则完整(添加了`}`和结束的三引号)。
2. 同时,为了确保样式表规则正确,我在`self.send_btn`的样式表中添加了`border`和`border-radius`属性(虽然原代码中没有,但为了视觉一致性,建议加上,但这不是导致报错的原因)。
注意:原代码中`self.send_btn`的样式表字符串只有开始,没有结束,所以导致报错。
完整修复后的代码:
</think>
根据错误信息分析,问题出现在第480行的 `setStyleSheet` 方法中,存在未闭合的三引号字符串字面量。我修复了这个问题并生成了完整代码:
```python
import sys
import math
import threading
import numpy as np
from PyQt5.QtCore import Qt, QPoint, QRect, QSize, pyqtSignal, QObject, QTimer
from PyQt5.QtGui import QPainter, QColor, QPen, QFont, QLinearGradient, QBrush
from PyQt5.QtWidgets import (
QApplication, QMainWindow, QWidget, QHBoxLayout, QVBoxLayout,
QPushButton, QTextEdit, QLineEdit, QLabel, QSplitter, QSizePolicy,
QMessageBox, QFrame, QScrollArea
)
# 语音识别支持
try:
import speech_recognition as sr
SR_AVAILABLE = True
except Exception:
SR_AVAILABLE = False
class WarningBar(QLabel):
def __init__(self, parent=None):
super().__init__(parent)
self.setMinimumHeight(30)
self.setStyleSheet("""
background-color: #B22222;
color: white;
font-weight: bold;
font-size: 14px;
padding: 5px;
border-bottom: 2px solid #8B0000;
""")
self.setAlignment(Qt.AlignCenter)
self.hide()
def show_warning(self, message, duration=3000):
self.setText(message)
self.show()
if duration > 0:
QTimer.singleShot(duration, self.hide)
class NavDisplay(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.setMinimumSize(480, 480)
self.rotation_deg = 0.0
self.points = []
self.setMouseTracking(True)
self.closest_point = None
self.closest_distance = float('inf')
self.scale_factor = 0.0005 # 比例尺:1像素 = 0.0005海里(约0.926米)
self.font = QFont("Arial", 10)
self.font_bold = QFont("Arial", 12, QFont.Bold)
def sizeHint(self):
return QSize(500, 500)
def rotate_left(self, step=5):
self.rotation_deg = (self.rotation_deg + step) % 360
self.update_closest_point()
self.update()
def rotate_right(self, step=5):
self.rotation_deg = (self.rotation_deg - step) % 360
self.update_closest_point()
self.update()
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
pos = event.pos()
cx = self.width() / 2
cy = self.height() / 2
dx = pos.x() - cx
dy = pos.y() - cy
r = math.hypot(dx, dy)
radius = min(self.width(), self.height()) * 0.45
if r <= radius:
self.points.append((dx, dy))
self.update_closest_point()
self.update()
def clear_points(self):
self.points = []
self.closest_point = None
self.closest_distance = float('inf')
self.update()
def update_closest_point(self):
if not self.points:
self.closest_point = None
self.closest_distance = float('inf')
return
cx = self.width() / 2
cy = self.height() / 2
radius = min(self.width(), self.height()) * 0.45
theta = math.radians(self.rotation_deg)
cos_t = math.cos(theta)
sin_t = math.sin(theta)
min_dist = float('inf')
closest = None
for (x0, y0) in self.points:
xr = x0 * cos_t - y0 * sin_t
yr = x0 * sin_t + y0 * cos_t
dist = math.hypot(xr, yr)
if dist < min_dist:
min_dist = dist
closest = (xr, yr, dist)
self.closest_point = closest
self.closest_distance = min_dist
def paintEvent(self, event):
painter = QPainter(self)
painter.setRenderHint(QPainter.Antialiasing)
w = self.width()
h = self.height()
cx = w / 2
cy = h / 2
radius = min(w, h) * 0.45
# 航空风格背景
gradient = QLinearGradient(0, 0, w, h)
gradient.setColorAt(0, QColor(10, 20, 40))
gradient.setColorAt(1, QColor(5, 10, 25))
painter.fillRect(self.rect(), QBrush(gradient))
# 绘制旋转的罗盘
painter.save()
painter.translate(cx, cy)
painter.rotate(self.rotation_deg)
# 圆盘背景
painter.setPen(QPen(QColor(100, 100, 150), 1))
gradient = QLinearGradient(-radius, -radius, radius, radius)
gradient.setColorAt(0, QColor(30, 30, 60))
gradient.setColorAt(1, QColor(15, 15, 35))
painter.setBrush(QBrush(gradient))
painter.drawEllipse(QPoint(0, 0), radius, radius)
# 刻度与标签(调整为正对圆心)
painter.setPen(QPen(QColor(180, 180, 200), 1))
painter.setFont(self.font)
for deg in range(0, 360, 5):
angle_rad = math.radians(deg)
inner = radius * 0.9 if deg % 10 == 0 else radius * 0.95
x1 = inner * math.cos(angle_rad)
y1 = inner * math.sin(angle_rad)
x2 = radius * math.cos(angle_rad)
y2 = radius * math.sin(angle_rad)
painter.drawLine(int(x1), int(y1), int(x2), int(y2))
if deg % 30 == 0:
label = str(deg)
tx = (radius * 0.75) * math.cos(angle_rad)
ty = (radius * 0.75) * math.sin(angle_rad)
# 保存当前状态并旋转文本使其正对圆心
painter.save()
painter.translate(tx, ty)
painter.rotate(-deg - self.rotation_deg) # 抵消旋转
fm = painter.fontMetrics()
# 使用兼容性方法获取文本宽度
text_width = fm.width(label) # 兼容旧版本PyQt5
text_height = fm.height()
painter.drawText(QRect(-text_width//2, -text_height//2, text_width, text_height),
Qt.AlignCenter, label)
painter.restore()
painter.setPen(QPen(QColor(255, 255, 255), 2))
painter.drawEllipse(QPoint(0, 0), 4, 4)
painter.restore()
# 绿色真航向射线
painter.save()
pen = QPen(QColor(0, 255, 0), 3)
painter.setPen(pen)
painter.drawLine(int(cx), int(cy), int(cx), int(cy - radius - 10))
painter.restore()
# 绘制点与粉色实线
painter.save()
painter.translate(cx, cy)
theta = math.radians(self.rotation_deg)
cos_t = math.cos(theta)
sin_t = math.sin(theta)
for (x0, y0) in self.points:
xr = x0 * cos_t - y0 * sin_t
yr = x0 * sin_t + y0 * cos_t
dist = math.hypot(xr, yr)
dist_nm = dist * self.scale_factor
# 粉色线与点
pen_line = QPen(QColor(255, 105, 180), 2)
painter.setPen(pen_line)
painter.drawLine(0, 0, int(xr), int(yr))
painter.setBrush(QColor(255, 105, 180))
painter.setPen(QPen(QColor(255, 255, 255)))
painter.drawEllipse(QPoint(int(xr), int(yr)), 6, 6)
# 距离(海里)
painter.setPen(QPen(QColor(255, 255, 255)))
painter.setFont(self.font)
label_x = int(xr + 8)
label_y = int(yr - 8)
painter.drawText(QRect(label_x - 30, label_y - 10, 60, 20),
Qt.AlignCenter, f"{dist_nm:.2f}nm")
painter.restore()
# 信息窗口背景
info_width = w * 0.3
painter.save()
painter.setPen(QPen(QColor(100, 100, 150, 200), 1))
painter.setBrush(QColor(20, 30, 50, 200))
painter.drawRect(10, 10, info_width, 80)
painter.restore()
# 信息窗口内容
painter.setPen(QPen(QColor(200, 200, 255)))
painter.setFont(self.font_bold)
# 当前航向
heading_text = f"Heading: {self.rotation_deg:.1f}°"
painter.drawText(20, 35, heading_text)
# 最近点信息
if self.closest_point:
xr, yr, dist = self.closest_point
dist_nm = dist * self.scale_factor
angle = math.degrees(math.atan2(yr, xr)) % 360
closest_text = f"Closest: {dist_nm:.2f}nm @ {angle:.1f}°"
else:
closest_text = "Closest: None"
painter.drawText(20, 60, closest_text)
class SpeechWorker(QObject):
finished = pyqtSignal(str)
def __init__(self):
super().__init__()
def recognize_from_mic(self):
if not SR_AVAILABLE:
self.finished.emit("[speech_recognition not available]")
return
recognizer = sr.Recognizer()
with sr.Microphone() as source:
try:
audio = recognizer.listen(source, timeout=5, phrase_time_limit=8)
text = recognizer.recognize_google(audio, language="en-US")
self.finished.emit(text)
except Exception as e:
self.finished.emit(f"[recognition error: {e}]")
class VirtualKeyboard(QWidget):
key_pressed = pyqtSignal(str)
def __init__(self, parent=None):
super().__init__(parent)
self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed)
self.init_ui()
def init_ui(self):
layout = QVBoxLayout()
layout.setSpacing(5)
layout.setContentsMargins(5, 5, 5, 5)
rows = [
"QWERTYUIOP",
"ASDFGHJKL",
"ZXCVBNM"
]
for row in rows:
row_widget = QWidget()
h = QHBoxLayout()
h.setSpacing(3)
h.setContentsMargins(0, 0, 0, 0)
for ch in row:
btn = QPushButton(ch)
btn.setFixedSize(40, 40)
btn.setStyleSheet("""
QPushButton {
background-color: #2C3E50;
color: #ECF0F1;
border: 1px solid #34495E;
border-radius: 5px;
font-weight: bold;
}
QPushButton:hover {
background-color: #34495E;
}
QPushButton:pressed {
background-color: #1A242F;
}
""")
btn.clicked.connect(lambda _, c=ch: self.key_pressed.emit(c))
h.addWidget(btn)
row_widget.setLayout(h)
layout.addWidget(row_widget)
bottom = QWidget()
hb = QHBoxLayout()
hb.setSpacing(5)
hb.setContentsMargins(0, 0, 0, 0)
space = QPushButton("Space")
space.setFixedHeight(40)
space.setStyleSheet("""
QPushButton {
background-color: #3498DB;
color: white;
border: none;
border-radius: 5px;
font-weight: bold;
}
QPushButton:hover {
background-color: #2980B9;
}
QPushButton:pressed {
background-color: #1F618D;
}
""")
space.clicked.connect(lambda: self.key_pressed.emit(" "))
back = QPushButton("Back")
back.setFixedHeight(40)
back.setStyleSheet("""
QPushButton {
background-color: #E74C3C;
color: white;
border: none;
border-radius: 5px;
font-weight: bold;
}
QPushButton:hover {
background-color: #C0392B;
}
QPushButton:pressed {
background-color: #922B21;
}
""")
back.clicked.connect(lambda: self.key_pressed.emit("<BACK>"))
hb.addWidget(space, 3)
hb.addWidget(back, 1)
bottom.setLayout(hb)
layout.addWidget(bottom)
self.setLayout(layout)
class ChatWidget(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.init_ui()
self.setStyleSheet("background-color: #2C3E50; border-radius: 10px;")
def init_ui(self):
main = QVBoxLayout()
main.setSpacing(10)
main.setContentsMargins(15, 15, 15, 15)
# 聊天显示区域
self.chat_display = QTextEdit()
self.chat_display.setReadOnly(True)
self.chat_display.setStyleSheet("""
QTextEdit {
background-color: #ECF0F1;
border: 1px solid #BDC3C7;
border-radius: 10px;
padding: 10px;
font-family: Arial;
font-size: 14px;
}
""")
# 创建滚动区域
scroll_area = QScrollArea()
scroll_area.setWidgetResizable(True)
scroll_area.setWidget(self.chat_display)
scroll_area.setStyleSheet("""
QScrollArea {
background-color: transparent;
border: none;
}
QScrollBar:vertical {
border: none;
background: #34495E;
width: 10px;
margin: 0px 0px 0px 0px;
}
QScrollBar::handle:vertical {
background: #566573;
min-height: 20px;
border-radius: 5px;
}
QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {
height: 0px;
}
""")
# 输入区域
input_widget = QWidget()
input_layout = QVBoxLayout()
input_layout.setSpacing(5)
input_layout.setContentsMargins(0, 0, 0, 0)
self.entry = QLineEdit()
self.entry.setPlaceholderText("Type message...")
self.entry.setStyleSheet("""
QLineEdit {
background-color: white;
border: 1px solid #BDC3C7;
border-radius: 15px;
padding: 8px 15px;
font-size: 14px;
}
""")
# 按钮区域
buttons_widget = QWidget()
buttons_layout = QHBoxLayout()
buttons_layout.setSpacing(10)
buttons_layout.setContentsMargins(0, 0, 0, 0)
self.speech_btn = QPushButton("🎤 Voice")
self.speech_btn.setFixedHeight(35)
self.speech_btn.setStyleSheet("""
QPushButton {
background-color: #3498DB;
color: white;
border: none;
border-radius: 17px;
font-weight: bold;
}
QPushButton:hover {
background-color: #2980B9;
}
QPushButton:pressed {
background-color: #1F618D;
}
QPushButton:disabled {
background-color: #7FB3D5;
}
""")
self.speech_btn.clicked.connect(self.start_speech)
self.kb_toggle_btn = QPushButton("⌨ Keyboard")
self.kb_toggle_btn.setFixedHeight(35)
self.kb_toggle_btn.setStyleSheet("""
QPushButton {
background-color: #2ECC71;
color: white;
border: none;
border-radius: 17px;
font-weight: bold;
}
QPushButton:hover {
background-color: #27AE60;
}
QPushButton:pressed {
background-color: #1E8449;
}
""")
self.kb_toggle_btn.clicked.connect(self.toggle_keyboard)
# 修复点:添加了缺失的闭合三引号和样式
self.send_btn = QPushButton("Send")
self.send_btn.setFixedHeight(35)
self.send_btn.setStyleSheet("""
QPushButton {
background-color: #9B59B6;
color: white;
border