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

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.xdnf.cn/news/17640.html

相关文章:

  • Kubernetes 资源管理全解析:从基础到企业级实践
  • 初识神经网络04——构建神经网络2
  • 【基于DesignStart的M3 SoC】
  • 当“超级高速“遇见“智能大脑“:5G-A×AI如何重塑万物智联时代
  • 告别数据孤岛!React 路由 3 种传参方法全解析
  • Qt之QMetaEnum的简单使用(含源码和注释)
  • 标准IO操作
  • Python 常用的正则表达式
  • Redis序列化配置类
  • vue2+elementUI实现园型动态步骤条小组件,带缩放功能
  • 【4】Transformers快速入门:自然语言模型 vs 统计语言模型
  • 【无标题】centos 配置阿里云的yum源
  • vue文件或文件夹拖拽上传
  • WPS文字和Word:不只是表格,段落也可以排序
  • 校园快递小程序(腾讯地图API、二维码识别、Echarts图形化分析)
  • 【Dify学习笔记】:Dify搭建图片文件数据分析助手
  • Kimi K2 架构深度解析:万亿MoE模型的效率革命与智能体突破
  • Linux文件系统:从虚拟接口到物理实现的架构解析
  • 【C++】5. 内存管理
  • Android Studio注释如何不从行首开始
  • gpt-5与gpt-5-fast
  • CPPIO流
  • Postman接口测试:postman设置接口关联,实现参数化
  • 当多模态大语言模型遇上视觉难题!AI视觉探索之旅
  • 视频输入输出模块介绍和示例
  • 编译 BusyBox for ARM 平台
  • 力扣面试150题--爬楼梯 打家劫舍 零钱兑换 最长递增子序列
  • Elasticsearch JS 自定义 ConnectionPool / Connection / Serializer、敏感信息脱敏与 v8 平滑迁移
  • 01-Ansible 自动化介绍与使用
  • 83. 删除排序链表中的重复元素