QT layout布局子部分大小问题

本文介绍了一种在Qt中使用布局管理器解决phonon组件和其他widget融合显示的问题,通过将不同组件分配到独立的子布局中,避免了视频播放时出现的黑边现象。

最近做phonon和普通widget相互融合的时候,试验出来的。

1. 当我用

layout->addWidget(1);

layout->addWidget(2);  // or

                       // tmp_layout->addWidget(2);

                       // layout->addLayout(tmp_layout);

这种情况下,1和2占的大小大致一样,而且,我用phonon作为2的时候,phonon播放视频的时候,上下多了很难看的很大一片黑色框

 

2. 最后我的方案

tmp_1_layout->addWidget(1);

tmp_2_layout->addWidget(2);

 

layout->addLayout(1);

layout->addLayout(2);

这样写的时候,他们各自控制,很好用,而且针对phonon的时候,也没有那个难看的黑边了

# 请将标题行设为全宽,即与界面全宽,计时器可叠加到其上 import sys import os import base64 import json import requests import time from datetime import datetime from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QComboBox, QTextEdit, QFileDialog, QGroupBox, QSlider, QDoubleSpinBox, QProgressBar, QFrame, QSplitter, QMessageBox, QListWidget, QListWidgetItem) from PyQt5.QtGui import QPixmap, QImage, QFont, QPalette, QColor, QIcon, QPainter, QLinearGradient, QBrush from PyQt5.QtCore import Qt, QSize, QThread, pyqtSignal, QPoint, QTimer # 配置Ollama API设置 - 使用默认端口11434 OLLAMA_HOST = "http://localhost:11434" class ModelLoaderThread(QThread): models_loaded = pyqtSignal(list) error_occurred = pyqtSignal(str) def __init__(self, parent=None): super().__init__(parent) def run(self): try: health_url = f"{OLLAMA_HOST}" try: response = requests.get(health_url, timeout=10) if response.status_code != 200: raise Exception(f"Ollama服务不可用 (HTTP {response.status_code})") except Exception as e: self.error_occurred.emit(f"Ollama服务检查失败: {str(e)}") return response = requests.get(f"{OLLAMA_HOST}/api/tags", timeout=15) if response.status_code == 200: models_data = response.json() model_list = [model["name"] for model in models_data.get("models", [])] if model_list: self.models_loaded.emit(model_list) else: self.error_occurred.emit("Ollama服务返回了空模型列表") else: self.error_occurred.emit(f"API错误 ({response.status_code}): {response.text[:200]}") except requests.exceptions.ConnectionError: self.error_occurred.emit("无法连接到Ollama服务。请确保Ollama已启动并正在运行。") except requests.exceptions.Timeout: self.error_occurred.emit("连接Ollama服务超时。请检查网络连接或增加超时设置。") except Exception as e: self.error_occurred.emit(f"加载模型时发生错误: {str(e)}") class StreamAnalysisThread(QThread): chunk_received = pyqtSignal(str) analysis_complete = pyqtSignal(str) progress_updated = pyqtSignal(int) error_occurred = pyqtSignal(str) def __init__(self, model, image_path, temperature, max_tokens, prompt, parent=None): super().__init__(parent) self.model = model self.image_path = image_path self.temperature = temperature self.max_tokens = max_tokens self.prompt = prompt self._is_running = True def stop(self): self._is_running = False def run(self): try: if not os.path.exists(self.image_path): self.error_occurred.emit(f"图片文件不存在: {self.image_path}") return self.progress_updated.emit(10) with open(self.image_path, "rb") as image_file: base64_image = base64.b64encode(image_file.read()).decode("utf-8") data = { "model": self.model, "prompt": self.prompt, "images": [base64_image], "options": { "temperature": self.temperature, "num_predict": self.max_tokens }, "stream": True } self.progress_updated.emit(30) response = requests.post( f"{OLLAMA_HOST}/api/generate", json=data, headers={"Content-Type": "application/json"}, stream=True, timeout=180 ) if response.status_code == 200: full_response = "" for line in response.iter_lines(): if not self._is_running: break if line: try: json_response = json.loads(line.decode('utf-8')) if 'response' in json_response: chunk = json_response['response'] full_response += chunk self.chunk_received.emit(chunk) if json_response.get('done', False): break except json.JSONDecodeError: continue self.progress_updated.emit(90) self.analysis_complete.emit(full_response) else: self.error_occurred.emit(f"API错误 ({response.status_code}): {response.text[:200]}") self.progress_updated.emit(100) except requests.exceptions.ConnectionError: self.error_occurred.emit("无法连接到Ollama服务。请确保Ollama已启动并正在运行。") except requests.exceptions.Timeout: self.error_occurred.emit("分析请求超时。请尝试减小图片大小或降低模型复杂度。") except Exception as e: self.error_occurred.emit(f"分析图片时发生错误: {str(e)}") class GradientLabel(QLabel): def __init__(self, text, parent=None): super().__init__(text, parent) self.setAlignment(Qt.AlignCenter) self.setMinimumHeight(60) def paintEvent(self, event): painter = QPainter(self) gradient = QLinearGradient(0, 0, self.width(), 0) gradient.setColorAt(0, QColor("#6a11cb")) gradient.setColorAt(1, QColor("#2575fc")) painter.fillRect(self.rect(), QBrush(gradient)) painter.setPen(Qt.white) painter.setFont(QFont("Microsoft YaHei UI", 18, QFont.Bold)) painter.drawText(self.rect(), Qt.AlignCenter, self.text()) class MultiModalApp(QMainWindow): def __init__(self): super().__init__() self.image_path = "" self.analysis_thread = None self.model_loader_thread = None self.current_response = "" self.start_time = None self.timer = QTimer() self.elapsed_time = 0 self.initUI() self.setWindowTitle("多模态大模型图片解读系统") self.setGeometry(100, 100, 1400, 900) self.timer.timeout.connect(self.update_timer) QTimer.singleShot(500, self.load_models) def initUI(self): self.setStyleSheet(""" QMainWindow { background-color: #f0f4f8; } QGroupBox { border: 2px solid #4a86e8; border-radius: 12px; margin-top: 20px; background-color: rgba(255, 255, 255, 0.95); color: #2c3e50; font-weight: bold; font-size: 12pt; } QGroupBox::title { subcontrol-origin: margin; subcontrol-position: top center; padding: 8px 20px; background-color: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 #6a11cb, stop:1 #2575fc); color: white; border-radius: 8px; top: -0px; min-height: 35px; } QLabel { color: #2c3e50; } QPushButton { background-color: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 #4a86e8, stop:1 #6a11cb); color: white; border: none; border-radius: 8px; padding: 8px 16px; font-weight: bold; font-size: 11pt; min-height: 35px; } QPushButton:hover { background-color: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 #5a96f8, stop:1 #7a21db); } QPushButton:pressed { background-color: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 #3a76d8, stop:1 #5a01bb); } QPushButton:disabled { background-color: #b0c4de; color: #777777; } QComboBox { background-color: white; color: #2c3e50; border: 1px solid #cccccc; border-radius: 8px; padding: 5px 10px; min-height: 30px; font-size: 11pt; } QComboBox::drop-down { border: none; } QComboBox QAbstractItemView { background-color: white; color: #2c3e50; selection-background-color: #e0f0ff; border-radius: 8px; border: 1px solid #cccccc; font-size: 11pt; } QTextEdit { background-color: white; color: #2c3e50; border: 1px solid #cccccc; border-radius: 8px; padding: 10px; font-size: 10pt; } QSlider::groove:horizontal { border: 1px solid #cccccc; height: 10px; background: #e0e0e0; margin: 2px 0; border-radius: 5px; } QSlider::handle:horizontal { background: qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 #6a11cb, stop:1 #4a86e8); border: 1px solid #4a76b0; width: 22px; margin: -6px 0; border-radius: 11px; } QDoubleSpinBox { background-color: white; color: #2c3e50; border: 1px solid #cccccc; border-radius: 8px; padding: 5px 10px; min-height: 30px; font-size: 11pt; } QProgressBar { border: 1px solid #cccccc; border-radius: 8px; text-align: center; color: #2c3e50; background-color: white; font-size: 11pt; height: 25px; } QProgressBar::chunk { background-color: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 #6a11cb, stop:1 #4a86e8); border-radius: 7px; } QListWidget { background-color: white; color: #2c3e50; border: 1px solid #cccccc; border-radius: 8px; font-size: 11pt; } QSplitter::handle { background-color: #4a86e8; width: 4px; } """) app_font = QFont("Microsoft YaHei UI", 10) app_font.setBold(False) QApplication.setFont(app_font) central_widget = QWidget() self.setCentralWidget(central_widget) main_layout = QVBoxLayout(central_widget) main_layout.setSpacing(10) main_layout.setContentsMargins(15, 10, 15, 15) timer_row = QWidget() timer_row.setFixedHeight(80) timer_layout = QHBoxLayout(timer_row) timer_layout.setContentsMargins(0, 0, 0, 0) title_label = QLabel("多模态大模型图片解读系统") title_label.setStyleSheet(""" background-color: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 #6a11cb, stop:1 #2575fc); color: white; font-size: 24pt; font-weight: bold; padding: 15px; border-radius: 12px; """) title_label.setAlignment(Qt.AlignCenter) timer_widget = QWidget() timer_widget.setFixedWidth(200) timer_widget_layout = QVBoxLayout(timer_widget) timer_widget_layout.setContentsMargins(10, 5, 10, 5) timer_label = QLabel("分析用时") timer_label.setStyleSheet(""" font-weight: bold; font-size: 12pt; color: white; background-color: rgba(255, 255, 255, 0.2); padding: 5px; border-radius: 5px; """) timer_label.setAlignment(Qt.AlignCenter) self.timer_display = QLabel("00:00:00") self.timer_display.setStyleSheet(""" background-color: rgba(255, 255, 255, 0.9); color: #2c3e50; font-size: 18pt; font-weight: bold; padding: 8px 12px; border-radius: 8px; border: 2px solid rgba(255, 255, 255, 0.5); """) self.timer_display.setAlignment(Qt.AlignCenter) self.timer_display.setFixedHeight(45) timer_widget_layout.addWidget(timer_label) timer_widget_layout.addWidget(self.timer_display) timer_layout.addWidget(title_label, 1) timer_layout.addWidget(timer_widget) main_layout.addWidget(timer_row) splitter = QSplitter(Qt.Horizontal) splitter.setChildrenCollapsible(False) left_panel = QWidget() left_layout = QVBoxLayout(left_panel) left_layout.setSpacing(15) left_layout.setContentsMargins(10, 15, 10, 10) self.image_group = QGroupBox("图片预览") self.image_group.setMinimumHeight(350) image_layout = QVBoxLayout(self.image_group) image_layout.setContentsMargins(10, 35, 10, 10) self.image_label = QLabel() self.image_label.setAlignment(Qt.AlignCenter) self.image_label.setStyleSheet(""" background-color: #ffffff; border: 1px solid #cccccc; border-radius: 8px; padding: 10px; """) self.image_label.setText("请选择图片进行分析") self.image_label.setFont(QFont("Microsoft YaHei UI", 12)) image_layout.addWidget(self.image_label) control_group = QGroupBox("分析控制") control_layout = QVBoxLayout(control_group) control_layout.setContentsMargins(15, 35, 15, 15) model_layout = QHBoxLayout() model_label = QLabel("选择模型:") model_label.setFixedWidth(100) model_label.setStyleSheet("font-weight: bold;") self.model_list = QListWidget() self.model_list.setMaximumHeight(150) self.model_list.addItem("正在加载模型...") model_layout.addWidget(model_label) model_layout.addWidget(self.model_list) control_layout.addLayout(model_layout) temp_layout = QHBoxLayout() temp_label = QLabel("温度:") temp_label.setFixedWidth(100) temp_label.setStyleSheet("font-weight: bold;") self.temp_slider = QSlider(Qt.Horizontal) self.temp_slider.setRange(0, 100) self.temp_slider.setValue(50) self.temp_value = QDoubleSpinBox() self.temp_value.setRange(0.0, 1.0) self.temp_value.setSingleStep(0.1) self.temp_value.setValue(0.5) self.temp_value.setDecimals(1) self.temp_slider.valueChanged.connect(lambda val: self.temp_value.setValue(val / 100)) self.temp_value.valueChanged.connect(lambda val: self.temp_slider.setValue(int(val * 100))) temp_layout.addWidget(temp_label) temp_layout.addWidget(self.temp_slider) temp_layout.addWidget(self.temp_value) control_layout.addLayout(temp_layout) token_layout = QHBoxLayout() token_label = QLabel("最大Token:") token_label.setFixedWidth(100) token_label.setStyleSheet("font-weight: bold;") self.token_spin = QDoubleSpinBox() self.token_spin.setRange(100, 5000) self.token_spin.setValue(1000) self.token_spin.setSingleStep(100) token_layout.addWidget(token_label) token_layout.addWidget(self.token_spin) control_layout.addLayout(token_layout) prompt_layout = QVBoxLayout() prompt_label = QLabel("提示词:") prompt_label.setStyleSheet("font-weight: bold;") self.prompt_edit = QTextEdit() self.prompt_edit.setPlainText( "请用中文详细描述这张图片的内容,要求描述清晰、有条理,分段落呈现,各段首行按要求缩进2个汉字。") self.prompt_edit.setMaximumHeight(100) prompt_layout.addWidget(prompt_label) prompt_layout.addWidget(self.prompt_edit) control_layout.addLayout(prompt_layout) button_layout = QHBoxLayout() self.load_button = QPushButton("加载图片") self.load_button.setIcon(QIcon.fromTheme("document-open")) self.load_button.clicked.connect(self.load_image) self.analyze_button = QPushButton("分析图片") self.analyze_button.setIcon(QIcon.fromTheme("system-search")) self.analyze_button.clicked.connect(self.analyze_image) self.analyze_button.setEnabled(False) self.clear_button = QPushButton("清除结果") self.clear_button.setIcon(QIcon.fromTheme("edit-clear")) self.clear_button.clicked.connect(self.clear_results) self.refresh_models_button = QPushButton("刷新模型") self.refresh_models_button.setIcon(QIcon.fromTheme("view-refresh")) self.refresh_models_button.clicked.connect(self.load_models) self.stop_button = QPushButton("停止分析") self.stop_button.setIcon(QIcon.fromTheme("process-stop")) self.stop_button.clicked.connect(self.stop_analysis) self.stop_button.setEnabled(False) button_layout.addWidget(self.load_button) button_layout.addWidget(self.analyze_button) button_layout.addWidget(self.stop_button) button_layout.addWidget(self.clear_button) button_layout.addWidget(self.refresh_models_button) control_layout.addLayout(button_layout) self.progress_bar = QProgressBar() self.progress_bar.setRange(0, 100) self.progress_bar.setValue(0) self.progress_bar.setTextVisible(True) self.progress_bar.setFormat("等待操作...") control_layout.addWidget(self.progress_bar) right_panel = QWidget() right_layout = QVBoxLayout(right_panel) right_layout.setContentsMargins(5, 5, 5, 5) result_group = QGroupBox("分析结果") result_layout = QVBoxLayout(result_group) result_layout.setContentsMargins(10, 35, 10, 10) self.result_edit = QTextEdit() self.result_edit.setReadOnly(True) self.result_edit.setStyleSheet(""" font-size: 10pt; line-height: 1.15; background-color: white; color: #2c3e50; padding: 15px; border: 1px solid #cccccc; border-radius: 8px; """) result_layout.addWidget(self.result_edit) left_layout.addWidget(self.image_group) left_layout.addWidget(control_group) right_layout.addWidget(result_group) splitter.addWidget(left_panel) splitter.addWidget(right_panel) splitter.setSizes([500, 900]) main_layout.addWidget(splitter) self.statusBar().setStyleSheet(""" background-color: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 #6a11cb, stop:1 #2575fc); color: white; font-weight: bold; padding-left: 10px; """) self.statusBar().showMessage("正在初始化...") def update_timer(self): self.elapsed_time += 1 hours = self.elapsed_time // 3600 minutes = (self.elapsed_time % 3600) // 60 seconds = self.elapsed_time % 60 self.timer_display.setText(f"{hours:02d}:{minutes:02d}:{seconds:02d}") def start_timer(self): self.elapsed_time = 0 self.timer.start(1000) self.timer_display.setText("00:00:00") def stop_timer(self): self.timer.stop() def show_error_dialog(self, title, message): msg = QMessageBox(self) msg.setIcon(QMessageBox.Critical) msg.setWindowTitle(title) msg.setText("发生错误") msg.setInformativeText(message) if "Ollama" in title: msg.addButton("打开Ollama网站", QMessageBox.ActionRole) msg.addButton("重试", QMessageBox.ActionRole) msg.addButton(QMessageBox.Ok) msg.setStyleSheet(""" QMessageBox { background-color: #f0f4f8; border: 2px solid #4a86e8; border-radius: 12px; } QLabel { color: #2c3e50; } QPushButton { background-color: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 #4a86e8, stop:1 #6a11cb); color: white; border: none; border-radius: 8px; padding: 8px 16px; min-width: 80px; font-weight: bold; } QPushButton:hover { background-color: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 #5a96f8, stop:1 #7a21db); } """) result = msg.exec_() if result == QMessageBox.Ok: pass elif msg.clickedButton().text() == "打开Ollama网站": import webbrowser webbrowser.open("https://ollama.com/") elif msg.clickedButton().text() == "重试": self.load_models() def load_models(self): self.model_list.clear() self.model_list.addItem("正在加载模型...") self.refresh_models_button.setEnabled(False) self.statusBar().showMessage("正在从Ollama获取模型列表...") self.model_loader_thread = ModelLoaderThread() self.model_loader_thread.models_loaded.connect(self.update_model_list) self.model_loader_thread.error_occurred.connect(self.handle_model_load_error) self.model_loader_thread.finished.connect(self.model_loader_finished) self.model_loader_thread.start() def update_model_list(self, model_list): self.model_list.clear() if model_list: multimodal_models = [model for model in model_list if "vision" in model.lower() or "llava" in model.lower()] title_item = QListWidgetItem("=== 多模态大模型 ===") self.model_list.addItem(title_item) title_item.setForeground(QColor("#4a86e8")) title_item.setFont(QFont("Microsoft YaHei UI", 10, QFont.Bold)) title_item.setFlags(Qt.NoItemFlags) for model in multimodal_models: item = QListWidgetItem(f"● {model}") item.setForeground(QColor("#2c3e50")) item.setFont(QFont("Microsoft YaHei UI", 10)) item.setData(Qt.UserRole, model) self.model_list.addItem(item) other_models = [model for model in model_list if model not in multimodal_models] if other_models: title_item = QListWidgetItem("=== 其他模型 ===") self.model_list.addItem(title_item) title_item.setForeground(QColor("#4a86e8")) title_item.setFont(QFont("Microsoft YaHei UI", 10, QFont.Bold)) title_item.setFlags(Qt.NoItemFlags) for model in other_models: item = QListWidgetItem(f"○ {model}") item.setForeground(QColor("#2c3e50")) item.setFont(QFont("Microsoft YaHei UI", 10)) item.setData(Qt.UserRole, model) self.model_list.addItem(item) self.statusBar().showMessage(f"已加载 {len(model_list)} 个模型") if multimodal_models: self.model_list.setCurrentRow(1) else: self.model_list.addItem("未找到可用模型") self.statusBar().showMessage("未找到可用模型") def handle_model_load_error(self, error): self.model_list.clear() self.model_list.addItem("加载模型失败") self.statusBar().showMessage(error) self.show_error_dialog("模型加载错误", f"{error}\n\n可能原因:\n1. Ollama服务未启动 (当前地址: {OLLAMA_HOST})\n2. Ollama未正确安装\n3. 网络连接问题\n\n解决方案:\n1. 下载并安装Ollama: https://ollama.com/download\n2. 启动Ollama服务\n3. 检查网络连接") def model_loader_finished(self): self.refresh_models_button.setEnabled(True) def load_image(self): file_path, _ = QFileDialog.getOpenFileName( self, "选择图片", "", "图片文件 (*.png *.jpg *.jpeg *.bmp *.gif)" ) if file_path: try: self.image_path = file_path pixmap = QPixmap(file_path) if pixmap.isNull(): raise Exception("无法加载图片文件,可能格式不支持或文件已损坏") self.original_pixmap = pixmap.copy() label_width = self.image_label.width() - 20 label_height = self.image_label.height() - 20 scaled_pixmap = pixmap.scaled( label_width, label_height, Qt.KeepAspectRatio, Qt.SmoothTransformation ) self.image_label.setPixmap(scaled_pixmap) self.analyze_button.setEnabled(True) self.statusBar().showMessage(f"已加载图片: {os.path.basename(file_path)}") self.progress_bar.setFormat("图片已加载,准备分析") except Exception as e: self.statusBar().showMessage(f"错误: {str(e)}") self.show_error_dialog("图片加载错误", f"无法加载图片:\n{str(e)}") def analyze_image(self): if not self.image_path: self.statusBar().showMessage("错误: 请先加载图片") return selected_items = self.model_list.selectedItems() if not selected_items: self.statusBar().showMessage("错误: 请选择模型") return selected_item = selected_items[0] if selected_item.text().startswith("==="): self.statusBar().showMessage("错误: 请选择有效的模型") return model_name = selected_item.data(Qt.UserRole) self.current_response = "" self.start_timer() timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") self.result_edit.setHtml(f""" <div style='background: linear-gradient(to right, #6a11cb, #2575fc); color: white; font-size: 16pt; font-weight: bold; padding: 12px; border-radius: 8px; margin-bottom: 15px;'>图片分析结果</div> <div style='color: #2c3e50; font-size: 10pt; line-height: 1.15;'><p style='color: #4a86e8; font-weight: bold;'>正在分析图片,请稍候...</p></div> <div style='margin-top: 20px; color: #7f8c8d; font-size: 9pt; border-top: 1px solid #ecf0f1; padding-top: 10px;'> <span style='color: #6a11cb; font-weight: bold;'>模型:</span> {model_name}   <span style='color: #6a11cb; font-weight: bold;'>开始时间:</span> {timestamp} </div> """) self.progress_bar.setValue(0) self.progress_bar.setFormat("正在分析图片...") self.set_buttons_enabled(False) self.stop_button.setEnabled(True) temperature = self.temp_value.value() max_tokens = int(self.token_spin.value()) prompt = self.prompt_edit.toPlainText().strip() self.analysis_thread = StreamAnalysisThread(model_name, self.image_path, temperature, max_tokens, prompt) self.analysis_thread.chunk_received.connect(self.handle_stream_chunk) self.analysis_thread.analysis_complete.connect(self.handle_analysis_result) self.analysis_thread.progress_updated.connect(self.update_progress) self.analysis_thread.error_occurred.connect(self.handle_analysis_error) self.analysis_thread.finished.connect(self.analysis_finished) self.analysis_thread.start() def handle_stream_chunk(self, chunk): self.current_response += chunk formatted_result = self.format_result(self.current_response) selected_items = self.model_list.selectedItems() model_name = selected_items[0].text()[2:] if selected_items else "未知模型" timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") result_html = f""" <div style='background: linear-gradient(to right, #6a11cb, #2575fc); color: white; font-size: 16pt; font-weight: bold; padding: 12px; border-radius: 8px; margin-bottom: 15px;'>图片分析结果</div> <div style='color: #2c3e50; font-size: 10pt; line-height: 1.15;'>{formatted_result}</div> <div style='margin-top: 20px; color: #7f8c8d; font-size: 9pt; border-top: 1px solid #ecf0f1; padding-top: 10px;'> <span style='color: #6a11cb; font-weight: bold;'>模型:</span> {model_name}   <span style='color: #6a11cb; font-weight: bold;'>时间:</span> {timestamp}   <span style='color: #27ae60; font-weight: bold;'>● 流式传输中...</span> </div> """ self.result_edit.setHtml(result_html) cursor = self.result_edit.textCursor() cursor.movePosition(cursor.End) self.result_edit.setTextCursor(cursor) self.result_edit.ensureCursorVisible() def handle_analysis_result(self, result): self.stop_timer() timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") selected_items = self.model_list.selectedItems() model_name = selected_items[0].text()[2:] if selected_items else "未知模型" current_html = self.result_edit.toHtml() updated_html = current_html.replace( '<span style=\'color: #27ae60; font-weight: bold;\'>● 流式传输中...</span>', f'<span style=\'color: #2ecc71; font-weight: bold;\'>✓ 分析完成 - 用时: {self.timer_display.text()}</span>' ) self.result_edit.setHtml(updated_html) self.statusBar().showMessage("图片分析完成") def stop_analysis(self): if self.analysis_thread and self.analysis_thread.isRunning(): self.analysis_thread.stop() self.analysis_thread.quit() self.analysis_thread.wait(2000) self.stop_timer() self.set_buttons_enabled(True) self.stop_button.setEnabled(False) self.progress_bar.setFormat("分析已停止") self.statusBar().showMessage("分析已停止") def format_result(self, result): formatted = result formatted = formatted.replace("# ", "").replace("## ", "").replace("### ", "") formatted = formatted.replace("**", "").replace("*", "").replace("__", "").replace("_", "") formatted = formatted.replace("- ", "• ").replace("• ", " • ") formatted = formatted.replace(".", "。").replace(",", ",").replace(":", ":") paragraphs = [] double_newline_paras = formatted.split("\n\n") for para in double_newline_paras: if "\n" in para: sub_paras = para.split("\n") paragraphs.extend(sub_paras) else: paragraphs.append(para) if len(paragraphs) == 1: paragraphs = formatted.split("。") paragraphs = [p.strip() + ("。" if i < len(paragraphs) - 1 else "") for i, p in enumerate(paragraphs) if p.strip()] html_paragraphs = [] for i, p in enumerate(paragraphs): p = p.strip() if p: if i == 0: html_paragraphs.append( f"<div style='margin: 10px 0; padding: 10px; background-color: #f8f9fa; border-left: 4px solid #4a86e8;'><p style='font-size: 12pt; font-weight: bold; color: #2c3e50; margin: 0; line-height: 1.15;'>{p}</p></div>") else: if p.strip().startswith("•"): html_paragraphs.append( f"<div style='margin: 8px 0; padding-left: 20px;'><p style='font-size: 10pt; color: #2c3e50; margin: 0; line-height: 1.15;'>{p}</p></div>") else: html_paragraphs.append( f"<div style='margin: 8px 0; padding: 8px; background-color: #ffffff; border-radius: 5px; border: 1px solid #f0f0f0;'><p style='font-size: 10pt; color: #2c3e50; margin: 0; text-indent: 2em; line-height: 1.15;'>{p}</p></div>") return "".join(html_paragraphs) def handle_analysis_error(self, error): self.stop_timer() self.result_edit.setPlainText(f"错误: {error}") self.statusBar().showMessage(f"错误: {error}") self.progress_bar.setFormat("分析失败") self.show_error_dialog("分析错误", error) def update_progress(self, value): self.progress_bar.setValue(value) if value < 30: self.progress_bar.setFormat("准备分析... %p%") elif value < 70: self.progress_bar.setFormat("发送请求到Ollama... %p%") elif value < 90: self.progress_bar.setFormat("处理响应... %p%") else: self.progress_bar.setFormat("完成分析... %p%") def analysis_finished(self): self.set_buttons_enabled(True) self.stop_button.setEnabled(False) self.progress_bar.setFormat("分析完成") def set_buttons_enabled(self, enabled): self.load_button.setEnabled(enabled) self.analyze_button.setEnabled(enabled and bool(self.image_path)) self.clear_button.setEnabled(enabled) self.refresh_models_button.setEnabled(enabled) def clear_results(self): self.result_edit.clear() self.image_label.clear() self.image_label.setText("请选择图片进行分析") self.image_path = "" self.analyze_button.setEnabled(False) self.progress_bar.setValue(0) self.progress_bar.setFormat("等待操作...") self.statusBar().showMessage("已清除结果") self.stop_timer() self.timer_display.setText("00:00:00") def closeEvent(self, event): if self.analysis_thread and self.analysis_thread.isRunning(): self.analysis_thread.stop() self.analysis_thread.quit() if not self.analysis_thread.wait(2000): self.analysis_thread.terminate() if self.model_loader_thread and self.model_loader_thread.isRunning(): self.model_loader_thread.quit() if not self.model_loader_thread.wait(2000): self.model_loader_thread.terminate() self.timer.stop() event.accept() if __name__ == "__main__": def exception_handler(exctype, value, traceback): error_msg = f"程序发生未捕获的异常:\n\n类型: {exctype.__name__}\n\n描述: {value}" print(error_msg) try: app = QApplication.instance() if app is not None: msg = QMessageBox() msg.setIcon(QMessageBox.Critical) msg.setText("程序遇到严重错误") msg.setInformativeText(error_msg) msg.setWindowTitle("未处理的异常") msg.setStandardButtons(QMessageBox.Ok) msg.exec_() except: pass sys.__excepthook__(exctype, value, traceback) sys.excepthook = exception_handler app = QApplication(sys.argv) app.setApplicationName("多模态大模型图片解读系统") app.setStyle("Fusion") palette = QPalette() palette.setColor(QPalette.Window, QColor(200, 104, 248)) palette.setColor(QPalette.WindowText, QColor(44, 62, 20)) palette.setColor(QPalette.Base, QColor(255, 255, 255)) palette.setColor(QPalette.AlternateBase, QColor(240, 240, 240)) palette.setColor(QPalette.ToolTipBase, Qt.white) palette.setColor(QPalette.ToolTipText, Qt.black) palette.setColor(QPalette.Text, QColor(44, 62, 20)) palette.setColor(QPalette.Button, QColor(74, 134, 232)) palette.setColor(QPalette.ButtonText, Qt.white) palette.setColor(QPalette.BrightText, Qt.red) palette.setColor(QPalette.Highlight, QColor(106, 17, 203)) palette.setColor(QPalette.HighlightedText, Qt.white) app.setPalette(palette) try: window = MultiModalApp() window.show() sys.exit(app.exec_()) except Exception as e: print(f"应用程序启动失败: {str(e)}") QMessageBox.critical(None, "启动错误", f"应用程序启动失败:\n{str(e)}")
最新发布
10-14
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值