class plotScroller: def update_val(self, proc): d=str(self.scl.value()) self.v.setText(d) if proc != None: proc(self.scl.value()) def __init__(self, master, text, geo, proc=None, lim=(0,100,1)): self.scl=QScrollBar(Qt.Horizontal, master) self.scl.resize(geo[2]-50,geo[3]) self.scl.move(geo[0]+50,geo[1]) self.scl.setMinimum(lim[0]) self.scl.setMaximum(lim[1]) self.scl.setSingleStep(lim[2]) self.scl.valueChanged.connect(lambda: self.update_val(proc)) q=QLabel(master) q.move(geo[0],geo[1]+10) q.resize(50,20) q.setText(text) self.v=QLabel(master) self.v.move(geo[2]+10,geo[1]+10) self.v.resize(50,20) self.v.setText(str(self.scl.value())) def setValue(self, v): self.scl.setValue(v)
class ScrollBar(QWidget): """ 定义一个可以变换样式的滚动条 """ def __init__(self, externalScrollBar=None, parent=None): super().__init__(parent) self.externalScrollBar = externalScrollBar # 实例化两个滚动条 self.minScrollBar = QScrollBar(Qt.Vertical, self) self.maxScrollBar = QScrollBar(Qt.Vertical, self) # 实例化一个控制滚动条显示的计时器 self.timer = QTimer(self) # 初始化 self.initWidget() self.associateScrollBar() self.setQss() def initWidget(self): """ 初始化小部件 """ self.setFixedWidth(20) self.minScrollBar.move(15, 0) self.maxScrollBar.hide() self.timer.setInterval(2000) self.timer.timeout.connect(self.showMinScrollBar) self.setAttribute(Qt.WA_TranslucentBackground) # 分配ID self.setObjectName('father') self.minScrollBar.setObjectName('minScrollBar') self.maxScrollBar.setObjectName('maxScrollBar') def adjustSrollBarHeight(self): """ 根据父级窗口的高度调整滚动条高度 """ if self.parent(): self.minScrollBar.setFixedHeight(self.parent().height() - 153) self.maxScrollBar.setFixedHeight(self.parent().height() - 153) def enterEvent(self, e: QEnterEvent): """ 鼠标进入界面时显示大滚动条并停止秒表 """ self.maxScrollBar.show() self.minScrollBar.hide() self.timer.stop() def leaveEvent(self, e): """ 鼠标离开打开秒表 """ self.timer.start() def showMinScrollBar(self): """ 定时溢出时隐藏大滚动条 """ self.timer.stop() self.maxScrollBar.hide() self.minScrollBar.show() def setQss(self): """ 设置层叠样式 """ with open(r'resource\css\my_scrollBar.qss', encoding='utf-8') as f: self.setStyleSheet(f.read()) def associateScrollBar(self): """ 关联滚动条 """ if self.externalScrollBar: # 设置最大值 self.minScrollBar.setMaximum(self.externalScrollBar.maximum()) self.maxScrollBar.setMaximum(self.externalScrollBar.maximum()) # 关联滚动条 self.externalScrollBar.valueChanged.connect( lambda: self.minScrollBar.setValue(self.externalScrollBar. value())) self.minScrollBar.valueChanged.connect(self.__minScrollBarChanged) self.maxScrollBar.valueChanged.connect( lambda: self.minScrollBar.setValue(self.maxScrollBar.value())) def __minScrollBarChanged(self): """ minScrollBar改变时同时改变另外两个滚动条的值 """ self.maxScrollBar.setValue(self.minScrollBar.value()) self.externalScrollBar.setValue(self.minScrollBar.value())
class TextArea(QWidget): """ 自定义文本展示区域(PS:不能键盘输入那种) 功能:能自动拉长文本区域并且带有滑动条 """ def __init__(self, parent): super().__init__(parent) self.setContentsMargins(0, 0, 0, 0) # 滚动条的宽度 # 对于水平方向的滚动条其实是高度 self.scrollBarWidth = 15 # 文本区 self.textContainer = TextView(self) self.textContainer.setContentsMargins(5, 0, 0, 0) # 设置居顶部显示 self.textContainer.setAlignment(Qt.AlignTop) # 设置文本区域根据文本进行自适应 self.textContainer.adjustSize() self.textContainer.setCallback(self.sizeChange) # 垂直滚动条 self.vBar = QScrollBar(Qt.Vertical, self) # self.vBar.sliderMoved.connect(self.dragV) self.vBar.valueChanged.connect(self.changeVertical) # 一开始隐藏 self.vBar.setVisible(False) # 在水平方向上文字的总长度是否超过容器的宽度标志位 # 只要出现一次超过,那么 self.overHorizontal就永远是True # 除非将超过容器宽度的文本都去除 self.overHorizontal = False # 水平方向上滑块上一次的值 self.hPreValue = 0 # 水平滚动条 self.hBar = QScrollBar(Qt.Horizontal, self) # self.hBar.sliderMoved.connect(self.dragH) self.hBar.valueChanged.connect(self.changeHorizontal) # 初始化的时候隐藏自身 self.hBar.setVisible(False) # 在垂直方向上文字的总高度是否超过容器的高度标志位 # 只要出现一次超过,那么 self.overVertical就永远是True # 除非将超过容器高度的文本都去除 self.overVertical = False # 垂直方向上滑块上一次的值 self.vPreValue = 0 def setSize(self, width, height): """ 设置TextArea的宽度和高度 :param width: 宽度 :param height: 高度 :return: no return """ self.resize(width, height) self.setMaximumSize(width, height) self.setMinimumSize(width, height) # 设置垂直方向ScrollBar的宽度和高度 # 垂直方向ScrollBar的高度等于TextArea的高度 self.vBar.resize(self.scrollBarWidth, height - self.scrollBarWidth) # 移动到指定位置 self.vBar.move(width - self.scrollBarWidth, 0) # 设置水平方向的ScrollBar的宽度和高度 # 水平方向ScrollBar的高度为self.scrollBarWidth self.hBar.resize(width, self.scrollBarWidth) self.hBar.move(0, height - self.scrollBarWidth) # 设置文本区域的高度和宽度 self.textContainer.resize(width, height) self.setStyleSheet("background-color: #088A68;") self.textContainer.setStyleSheet("border: 1px solid #FF0000; color: #FFFFFF;") def setMaximumSize(self, maxw: int, maxh: int) -> None: super().setMaximumSize(maxw, maxh) def setClassName(self, textAreaName=None, labelName=None, vBarName=None, hBarName=None): """ 设置ObjectName :param textAreaName: TextArea的ObjectName :param labelName: 文本区域的ObjectName :param vBarName: 垂直ScrollBar的ObjectName :param hBarName: 水平ScrollBar的ObjectName :return: """ self.setObjectName(textAreaName) self.textContainer.setObjectName(labelName) self.hBar.setObjectName(hBarName) self.vBar.setObjectName(vBarName) def append(self, text): self.textContainer.adjustSize() self.textContainer.append(text) margin = self.contentsMargins() # 如果文本长度超过了容器的宽度, 将标准位置为True # 并且显示水平滑动条 if self.textContainer.width() > (self.width() - margin.left() - margin.right()): self.overHorizontal = True self.hBar.setVisible(True) # 如果文本总高度度超过了容器的宽度, 将标准位置为True # 并且显示垂直滑动条 if self.textContainer.height() > (self.height() - margin.top() - margin.bottom()): self.overVertical = True self.vBar.setVisible(True) def dragHortizontal(self, value): """ 滑块水平方向上的拖拽事件 :argument value :return: """ print("H: " + str(value)) def dragVertical(self, value): """ 滑块垂直方向上的拖拽事件 :argument value :return: """ print("V: " + str(value)) def changeHorizontal(self, v): """ 垂直滑块的值发生改变时 :param v: :return: """ if v == 0: # 由于下列的操作会产生浮点型数据,所以在计算坐标的时候总有偏差 # 所以在 v = 0, 也就是滑动条回到最开始的地方, # self.textContainer对应方向上的坐标也归0,那样就不会出问题了 self.textContainer.move(0, self.textContainer.y()) # 将当前值作为下一次的preValue self.hPreValue = v return # print("change H: {}".format(v)) textW = self.textContainer.width() # 最大值(从0开始计算) maximum = self.hBar.maximum() + 1 # 计算每次改变值对应 self.textContainer在水平方向上需要移动的尺寸 stepW = textW / maximum # 当前值与上一次的值得差 change = v - self.hPreValue move = int(self.textContainer.x() - stepW * change) self.textContainer.move(move, self.textContainer.y()) # 将当前值作为下一次的preValue self.hPreValue = v def changeVertical(self, v): """ 水平滑块的值发生改变时 :param v: :return: """ if v == 0: # 由于下列的操作会产生浮点型数据,所以在计算坐标的时候总有偏差 # 所以在 v = 0, 也就是滑动条回到最开始的地方, # self.textContainer对应方向上的坐标也归0,那样就不会出问题了 self.textContainer.move(self.textContainer.x(), 0) self.vPreValue = v return # print("change V: {}".format(v)) textH = self.textContainer.width() # 最大值(从0开始计算) maximum = self.vBar.maximum() - self.vBar.minimum() + self.vBar.pageStep() # 计算每次改变值对应 self.textContainer在水平方向上需要移动的尺寸 stepH = float(textH) / maximum # 当前值与上一次的值得差 change = v - self.vPreValue move = int(self.textContainer.y() - stepH * change) self.textContainer.move(self.textContainer.x(), move) # 将当前值作为下一次的preValue self.vPreValue = v def sizeChange(self, obj: QLabel): print("size change:{},{} ".format(obj.width(), obj.height()))