调整缩放、多核运行、图标显示
This commit is contained in:
@ -15,7 +15,7 @@ from datetime import datetime
|
||||
from typing import Dict, Optional, List, Union
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
|
||||
import multiprocessing
|
||||
from PyQt5.QtWidgets import (
|
||||
QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
|
||||
QPushButton, QLabel, QLineEdit, QComboBox, QCheckBox, QSpinBox,
|
||||
@ -3462,6 +3462,10 @@ class ImageViewerWidget(QWidget):
|
||||
super().__init__(parent)
|
||||
self.current_image_path = None
|
||||
self.scale_factor = 1.0
|
||||
self._update_timer = QTimer() # 防抖定时器
|
||||
self._update_timer.setSingleShot(True)
|
||||
self._update_timer.timeout.connect(self._do_update_display)
|
||||
self._pending_scale = None # 待更新的缩放比例
|
||||
self.setup_ui()
|
||||
|
||||
def setup_ui(self):
|
||||
@ -3564,28 +3568,62 @@ class ImageViewerWidget(QWidget):
|
||||
self.status_label.setText(f"{pixmap.width()}x{pixmap.height()} | {size_mb:.2f} MB | {Path(image_path).name} | 适应窗口")
|
||||
|
||||
def update_image_display(self):
|
||||
"""更新图像显示"""
|
||||
"""更新图像显示 - 使用防抖避免频繁重绘卡顿"""
|
||||
# 取消之前的待执行更新,重新计时
|
||||
self._update_timer.stop()
|
||||
self._pending_scale = self.scale_factor
|
||||
self._update_timer.start(50) # 50ms后执行实际更新
|
||||
|
||||
def _do_update_display(self):
|
||||
"""实际执行图像更新"""
|
||||
if not hasattr(self, 'original_pixmap') or self.original_pixmap.isNull():
|
||||
return
|
||||
|
||||
if self._pending_scale is None:
|
||||
return
|
||||
|
||||
# 根据缩放比例选择变换模式:大幅度缩放用Fast模式提升性能
|
||||
if self._pending_scale > 2.0 or self._pending_scale < 0.5:
|
||||
transform = Qt.FastTransformation
|
||||
else:
|
||||
transform = Qt.SmoothTransformation
|
||||
|
||||
scaled_pixmap = self.original_pixmap.scaled(
|
||||
int(self.original_pixmap.width() * self.scale_factor),
|
||||
int(self.original_pixmap.height() * self.scale_factor),
|
||||
int(self.original_pixmap.width() * self._pending_scale),
|
||||
int(self.original_pixmap.height() * self._pending_scale),
|
||||
Qt.KeepAspectRatio,
|
||||
Qt.SmoothTransformation
|
||||
transform
|
||||
)
|
||||
self.image_label.setPixmap(scaled_pixmap)
|
||||
self._pending_scale = None
|
||||
|
||||
def wheelEvent(self, event):
|
||||
"""鼠标滚轮缩放 - 实时响应"""
|
||||
delta = event.angleDelta().y()
|
||||
|
||||
if delta > 0:
|
||||
# 向上滚动 - 放大
|
||||
if self.scale_factor < 5.0:
|
||||
self.scale_factor = min(self.scale_factor * 1.1, 5.0)
|
||||
self.update_image_display()
|
||||
else:
|
||||
# 向下滚动 - 缩小
|
||||
if self.scale_factor > 0.1:
|
||||
self.scale_factor = max(self.scale_factor / 1.1, 0.1)
|
||||
self.update_image_display()
|
||||
|
||||
event.accept()
|
||||
|
||||
def zoom_in(self):
|
||||
"""放大"""
|
||||
if self.scale_factor < 5.0:
|
||||
self.scale_factor *= 1.25
|
||||
self.scale_factor = min(self.scale_factor * 1.25, 5.0)
|
||||
self.update_image_display()
|
||||
|
||||
def zoom_out(self):
|
||||
"""缩小"""
|
||||
if self.scale_factor > 0.1:
|
||||
self.scale_factor /= 1.25
|
||||
self.scale_factor = max(self.scale_factor / 1.25, 0.1)
|
||||
self.update_image_display()
|
||||
|
||||
def fit_to_window(self):
|
||||
@ -3599,14 +3637,20 @@ class ImageViewerWidget(QWidget):
|
||||
|
||||
scale_w = view_size.width() / img_size.width()
|
||||
scale_h = view_size.height() / img_size.height()
|
||||
self.scale_factor = min(scale_w, scale_h, 1.0) # 不超过原始大小
|
||||
|
||||
# 记录适应前的比例(用于后续恢复参考)
|
||||
self._fit_scale = min(scale_w, scale_h)
|
||||
self.scale_factor = self._fit_scale
|
||||
|
||||
self.update_image_display()
|
||||
self.status_label.setText(f"适应窗口 | 缩放: {self.scale_factor:.1%}")
|
||||
|
||||
def original_size(self):
|
||||
"""原始大小"""
|
||||
self.scale_factor = 1.0
|
||||
self._fit_scale = None # 清除适应记录
|
||||
self.update_image_display()
|
||||
self.status_label.setText("原始大小 | 缩放: 100%")
|
||||
|
||||
def save_image(self):
|
||||
"""保存图像"""
|
||||
@ -5229,6 +5273,7 @@ class WaterQualityGUI(QMainWindow):
|
||||
|
||||
self.init_ui()
|
||||
self.apply_stylesheet()
|
||||
self._disable_wheel_for_all_spinboxes()
|
||||
|
||||
def get_icon_path(self, icon_filename):
|
||||
"""
|
||||
@ -5245,10 +5290,48 @@ class WaterQualityGUI(QMainWindow):
|
||||
|
||||
return os.path.join(icon_dir, icon_filename)
|
||||
|
||||
def _disable_wheel_for_all_spinboxes(self):
|
||||
"""
|
||||
遍历所有子控件,为 QSpinBox/QDoubleSpinBox/QComboBox 禁用滚轮事件
|
||||
防止滚动页面时意外改变数值
|
||||
"""
|
||||
from PyQt5.QtCore import Qt
|
||||
|
||||
# 找到所有数值输入控件
|
||||
for spinbox in self.findChildren(QSpinBox):
|
||||
spinbox.setFocusPolicy(Qt.StrongFocus) # 只有聚焦时才响应滚轮
|
||||
spinbox.wheelEvent = lambda event, sb=spinbox: None # 完全禁用滚轮
|
||||
|
||||
for spinbox in self.findChildren(QDoubleSpinBox):
|
||||
spinbox.setFocusPolicy(Qt.StrongFocus)
|
||||
spinbox.wheelEvent = lambda event, sb=spinbox: None
|
||||
|
||||
for combobox in self.findChildren(QComboBox):
|
||||
combobox.setFocusPolicy(Qt.StrongFocus)
|
||||
combobox.wheelEvent = lambda event, cb=combobox: None
|
||||
|
||||
def init_ui(self):
|
||||
"""初始化UI"""
|
||||
self.setWindowTitle("水质参数反演分析系统 v1.0")
|
||||
self.setGeometry(100, 100, 1200, 800)
|
||||
|
||||
# 获取屏幕可用区域(排除任务栏)
|
||||
screen_geometry = QApplication.primaryScreen().availableGeometry()
|
||||
screen_width = screen_geometry.width()
|
||||
screen_height = screen_geometry.height()
|
||||
|
||||
# 初始尺寸:宽度固定 800,高度占满屏幕
|
||||
window_width = 1200
|
||||
window_height = screen_height
|
||||
# 仅设置初始大小,不锁定
|
||||
self.resize(window_width, window_height)
|
||||
|
||||
# 计算水平居中、垂直贴顶的位置
|
||||
x = (screen_width - window_width) // 2
|
||||
y = 0
|
||||
self.move(x, y)
|
||||
|
||||
# 可选:设置最小尺寸,防止用户缩得太小
|
||||
self.setMinimumSize(600, 400)
|
||||
|
||||
# 创建自定义标题栏(包含Logo和菜单栏)
|
||||
self.create_title_bar()
|
||||
@ -5297,8 +5380,12 @@ class WaterQualityGUI(QMainWindow):
|
||||
""")
|
||||
|
||||
# 设置Logo图片路径 - 使用相对路径(打包兼容)
|
||||
logo_path = r"E:\code\WQ\GUI_v1\fengzhuang-ui2V3\data\icons\logo.png"
|
||||
logo_pixmap = QPixmap(str(logo_path))
|
||||
from pathlib import Path
|
||||
if hasattr(sys, '_MEIPASS'):
|
||||
logo_path = os.path.join(sys._MEIPASS, 'data', 'icons', 'logo.png')
|
||||
else:
|
||||
logo_path = str(Path(__file__).parent.parent.parent / "data" / "icons" / "logo.png")
|
||||
logo_pixmap = QPixmap(logo_path)
|
||||
|
||||
if not logo_pixmap.isNull():
|
||||
# 按高度缩放图片,保持宽高比,让Logo更显眼
|
||||
@ -5406,17 +5493,23 @@ class WaterQualityGUI(QMainWindow):
|
||||
banner_layout = QHBoxLayout()
|
||||
banner_layout.setContentsMargins(0, 0, 0, 0)
|
||||
banner_layout.setSpacing(0)
|
||||
# 不设置居中对齐,让横幅填满整个容器
|
||||
|
||||
# 创建横幅标签
|
||||
# 创建横幅标签 - 完全跟随窗口等比缩放,填满整个区域
|
||||
self.banner_label = QLabel()
|
||||
self.banner_label.setMinimumHeight(65)
|
||||
self.banner_label.setMaximumHeight(110)
|
||||
self.banner_label.setAlignment(Qt.AlignCenter)
|
||||
# 最小高度保证:当窗口很小时至少显示 38px 高 (200px 宽 / 5.25)
|
||||
self.banner_label.setMinimumHeight(int(200 / 5.25)) # ≈ 38px
|
||||
# 使用 Expanding 策略让标签填满可用空间
|
||||
self.banner_label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
|
||||
self.banner_label.setScaledContents(False)
|
||||
# 清除 QLabel 默认的 margin 和 padding,消除右侧空白
|
||||
self.banner_label.setStyleSheet("margin: 0px; padding: 0px; border: none;")
|
||||
|
||||
# 保存原始pixmap用于后续缩放
|
||||
banner_path = r"E:\code\WQ\GUI_v1\fengzhuang-ui2\data\icons\Mega Water 1.0.png"
|
||||
if hasattr(sys, '_MEIPASS'):
|
||||
banner_path = os.path.join(sys._MEIPASS, 'data', 'icons', 'Mega Water 1.0.png')
|
||||
else:
|
||||
banner_path = str(Path(__file__).parent.parent.parent / "data" / "icons" / "Mega Water 1.0.png")
|
||||
self.banner_pixmap = QPixmap(banner_path)
|
||||
|
||||
if not self.banner_pixmap.isNull():
|
||||
@ -5444,13 +5537,19 @@ class WaterQualityGUI(QMainWindow):
|
||||
banner_toolbar.setMovable(False)
|
||||
banner_toolbar.setFloatable(False)
|
||||
banner_toolbar.addWidget(banner_widget)
|
||||
banner_toolbar.setContentsMargins(0, 0, 0, 0) # 清除工具栏布局的边距
|
||||
banner_toolbar.setStyleSheet("""
|
||||
QToolBar {
|
||||
background-color: white;
|
||||
border: none;
|
||||
border-bottom: 1px solid #ddd;
|
||||
padding: 2px 0px;
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
spacing: 0px;
|
||||
}
|
||||
QToolBar QWidget {
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
""")
|
||||
|
||||
@ -6203,44 +6302,42 @@ class WaterQualityGUI(QMainWindow):
|
||||
self.training_mode_action.setText("有训练数据模式" if checked else "无训练数据模式")
|
||||
|
||||
def update_banner_image(self):
|
||||
"""更新横幅图片 - 等比自适应缩放"""
|
||||
"""更新横幅图片 - 完全跟随窗口等比缩放,填满可用宽度"""
|
||||
if not hasattr(self, 'banner_pixmap') or self.banner_pixmap.isNull():
|
||||
return
|
||||
|
||||
# 获取可用宽度(考虑工具栏边距)
|
||||
available_width = max(200, self.width() - 60) # 最小宽度保护
|
||||
# 获取可用宽度(考虑工具栏边距),跟随窗口实时变化
|
||||
available_width = max(200, self.width() - 60)
|
||||
|
||||
# 先根据可用宽度计算目标高度(严格 5.25:1)
|
||||
target_height = int(available_width / 5.25)
|
||||
|
||||
# 限制最小高度
|
||||
if target_height < 38:
|
||||
target_height = 38
|
||||
available_width = int(38 * 5.25)
|
||||
|
||||
# 第一步:按宽度缩放,保持比例
|
||||
# 计算图片目标尺寸(保持 5.25:1 比例)
|
||||
target_width = available_width
|
||||
|
||||
# 设置固定尺寸,确保标签严格填满整个区域
|
||||
self.banner_label.setFixedSize(target_width, target_height)
|
||||
|
||||
# 等比缩放到目标尺寸,填满整个区域(允许轻微裁剪)
|
||||
scaled_pixmap = self.banner_pixmap.scaled(
|
||||
available_width,
|
||||
120, # 最大允许高度
|
||||
Qt.KeepAspectRatio, # 关键:等比缩放
|
||||
Qt.SmoothTransformation # 平滑缩放
|
||||
target_width,
|
||||
target_height,
|
||||
Qt.KeepAspectRatioByExpanding, # 保持比例,填满区域,允许裁剪超出部分
|
||||
Qt.SmoothTransformation
|
||||
)
|
||||
|
||||
# 如果高度仍然过大,则按高度限制缩放
|
||||
if scaled_pixmap.height() > 110:
|
||||
scaled_pixmap = self.banner_pixmap.scaled(
|
||||
int(available_width * 0.9),
|
||||
110,
|
||||
Qt.KeepAspectRatio,
|
||||
Qt.SmoothTransformation
|
||||
)
|
||||
|
||||
self.banner_label.setPixmap(scaled_pixmap)
|
||||
|
||||
def resizeEvent(self, event):
|
||||
"""窗口大小改变事件 - 实时更新横幅图片等比缩放"""
|
||||
super().resizeEvent(event)
|
||||
# 使用定时器避免频繁调用
|
||||
if hasattr(self, '_banner_timer'):
|
||||
self._banner_timer.stop()
|
||||
else:
|
||||
self._banner_timer = QTimer()
|
||||
self._banner_timer.setSingleShot(True)
|
||||
self._banner_timer.timeout.connect(self.update_banner_image)
|
||||
|
||||
self._banner_timer.start(50) # 50ms后更新
|
||||
# 直接调用,不使用定时器延迟(或缩短到 10ms)
|
||||
self.update_banner_image()
|
||||
|
||||
def update_ui_for_training_mode(self):
|
||||
"""根据训练数据模式更新UI状态"""
|
||||
@ -6296,5 +6393,7 @@ def main():
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
#冻结,只显示1个exe
|
||||
# multiprocessing.freeze_support()
|
||||
main()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user