当前位置: 首页 > news >正文

pyside控件_左右范围滑动控件

背景:

PySide2 与 qt5 有版本冲突,

# from superqt import QRangeSlider # 得QT6才行

from qtrangeslider import QRangeSlider 也不兼容

有不方便升级 PySide2

所以自实现写一个 完全 PySide2 5.15.2 兼容的双滑块控件,不依赖外部 qtrangeslider,直接用 QWidget + QPainter 自绘两个滑块,支持水平模式、数值范围、信号触发。

效果如下:

实现:

自定义控件

(统一放到新建main_test.py文件中)

from PySide2.QtCore import Qt, QRect, Signal, QPoint
from PySide2.QtGui import QPainter, QColor, QPen, QBrush
from PySide2.QtWidgets import QWidget, QSizePolicyclass QRangeSlider(QWidget):valueChanged = Signal(tuple)  # (low, high)def __init__(self, orientation=Qt.Horizontal, parent=None):super().__init__(parent)self.orientation = orientationself._min = 0self._max = 100self._low_value = 20self._high_value = 80self._handle_radius = 8self._bar_height = 4self._active_handle = Noneself.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)self.setMinimumHeight(30)# ------- Public API -------def setRange(self, min_val, max_val):self._min = min_valself._max = max_valself.update()def setValue(self, values):low, high = valuesself._low_value = max(self._min, min(low, self._max))self._high_value = max(self._min, min(high, self._max))self.update()self.valueChanged.emit((self._low_value, self._high_value))def value(self):return (self._low_value, self._high_value)# ------- Qt Events -------def paintEvent(self, event):painter = QPainter(self)painter.setRenderHint(QPainter.Antialiasing)# 计算坐标bar_rect = QRect(self._handle_radius,(self.height() - self._bar_height) // 2,self.width() - self._handle_radius * 2,self._bar_height)# 绘制背景条painter.setPen(Qt.NoPen)painter.setBrush(QColor(200, 200, 200))painter.drawRect(bar_rect)# 绘制选中范围low_x = self._value_to_pos(self._low_value)high_x = self._value_to_pos(self._high_value)selected_rect = QRect(low_x, bar_rect.y(), high_x - low_x, self._bar_height)painter.setBrush(QColor(100, 180, 255))painter.drawRect(selected_rect)# 绘制两个滑块painter.setBrush(QBrush(QColor(255, 100, 100)))painter.drawEllipse(QPoint(low_x, self.height() // 2), self._handle_radius, self._handle_radius)painter.setBrush(QBrush(QColor(100, 100, 255)))painter.drawEllipse(QPoint(high_x, self.height() // 2), self._handle_radius, self._handle_radius)def mousePressEvent(self, event):if event.button() == Qt.LeftButton:low_x = self._value_to_pos(self._low_value)high_x = self._value_to_pos(self._high_value)if abs(event.x() - low_x) < self._handle_radius + 2:self._active_handle = "low"elif abs(event.x() - high_x) < self._handle_radius + 2:self._active_handle = "high"def mouseMoveEvent(self, event):if self._active_handle:new_val = self._pos_to_value(event.x())if self._active_handle == "low":self._low_value = new_valelif self._active_handle == "high":self._high_value = new_valself.update()self.valueChanged.emit((self._low_value, self._high_value))def mouseReleaseEvent(self, event):if self._low_value > self._high_value:# 交换,保证 low <= highself._low_value, self._high_value = self._high_value, self._low_valueself._active_handle = Noneself.update()self.valueChanged.emit((self._low_value, self._high_value))# ------- Utils -------def _value_to_pos(self, value):bar_start = self._handle_radiusbar_end = self.width() - self._handle_radiusratio = (value - self._min) / (self._max - self._min)return int(bar_start + ratio * (bar_end - bar_start))def _pos_to_value(self, pos):bar_start = self._handle_radiusbar_end = self.width() - self._handle_radiusratio = (pos - bar_start) / (bar_end - bar_start)ratio = max(0, min(1, ratio))return int(self._min + ratio * (self._max - self._min))

测试:

在上述同一py文件下,添加:

if __name__ == "__main__":from PySide2.QtWidgets import QApplication, QVBoxLayout, QWidgetimport sysapp = QApplication(sys.argv)win = QWidget()layout = QVBoxLayout(win)slider = QRangeSlider()slider.setRange(0, 100)slider.setValue((20, 80))slider.valueChanged.connect(lambda v: print("当前值:", v))layout.addWidget(slider)win.show()sys.exit(app.exec_())

布局

上述控件定义好后,如何在QT creater中使用呢?

可以采用占位符 + 替换的方式,替换成自己的控件

首先,先在ui中插入一个QWidget, 如下:

      <widget class="QWidget" name="range_slider_placeholder" native="true"><property name="geometry"><rect><x>150</x><y>440</y><width>451</width><height>81</height></rect></property></widget>

然后代码替换:

placeholder = self.ui.findChild(QWidget, "range_slider_placeholder")
range_slider = QRangeSliderJ(Qt.Horizontal, self.ui)
range_slider.setRange(0, 100)
range_slider.setValue((20, 80))replace_placeholder_with_widget(placeholder, range_slider)

替换函数的具体实现如下:


def replace_placeholder_with_widget(placeholder: QWidget, new_widget: QWidget):"""将 UI 中的占位符控件替换为新的控件,兼容:- QGridLayout- QHBoxLayout / QVBoxLayout- 无布局的父控件(直接定位到占位符的位置):param placeholder: 原占位符 QWidget:param new_widget: 替换用的新 QWidget"""if placeholder is None or placeholder.parent() is None:raise ValueError("占位符无效或没有父控件")parent = placeholder.parent()layout = parent.layout()if layout is None:# 无布局:直接放到占位符的几何位置new_widget.setParent(parent)new_widget.setGeometry(placeholder.geometry())elif isinstance(layout, QGridLayout):index = layout.indexOf(placeholder)if index != -1:row, col, row_span, col_span = layout.getItemPosition(index)layout.addWidget(new_widget, row, col, row_span, col_span)layout.removeWidget(placeholder)else:layout.addWidget(new_widget)else:# QHBoxLayout / QVBoxLayoutindex = layout.indexOf(placeholder)if index != -1:layout.insertWidget(index, new_widget)layout.removeWidget(placeholder)else:layout.addWidget(new_widget)placeholder.hide()placeholder.deleteLater()new_widget.show()

http://www.dtcms.com/a/327182.html

相关文章:

  • 深层神经网络
  • torch.max() 函数使用
  • uv 配置和简单使用
  • 6深度学习Pytorch-神经网络--过拟合欠拟合问题解决(Dropout、正则化、早停法、数据增强)、批量标准化
  • OpenHarmony编译与烧录
  • 【完美解决】在 Ubuntu 24.04 上为小米 CyberDog 2 刷机/交叉编译:终极 Docker 环境搭建指南
  • 【LeetCode】2. 两数相加
  • 一台云主机“被黑”后的 24 小时排查手记
  • 【力扣 Hot100】刷题日记
  • 《Redis ACL验证流程:从用户认证到权限检查的完整步骤》
  • 【doris基础与进阶】3-Doris安装与部署
  • 模板打印技术——自动识别office类型 打印模板:为政务土地确权定制的替换利器—仙盟创梦IDE
  • Go 语言 里 `var`、`make`、`new`、`:=` 的区别
  • Python 标准库模块shutil
  • 当多模态大语言模型遇上视觉难题!AI视觉探索之旅
  • 基于Hadoop的全国农产品批发价格数据分析与可视化与价格预测研究
  • grpc浅入门
  • jdk升级
  • 【Redis在在线表单提交防重复机制中的应用策略】
  • 【开发环境下浏览器前后端Cookie跨域问题】
  • 实现文字在块元素中水平/垂直居中详解
  • 深度贴:前端网络基础及进阶(3)
  • Linux 常用命令大全:覆盖日常 99% 操作需求
  • 【SpringBoot】05 容器功能 - SpringBoot底层注解的应用与实战 - @Configuration + @Bean
  • WebAssembly的原理与使用
  • Day24|学习前端CSS
  • 虚拟机高级玩法-网页也能运行虚拟机——WebAssembly
  • GitHub的简单使用方法----(4)
  • Seata深度剖析:微服务分布式事务解决方案
  • 如何应对CAN总线冲突和数据丢包