Issue
I have a numpy
1d-array where each entry is either 0 or 1. I want to set the sytle sheet of QSlider
systematically according to this numpy array .
Suppose I have a numpy array like this :
import numpy as np
myarray = np.array([0,0,0,1,1,1,0,1,0,1]) #len(myarray) = 10
I want to construct a QSlider
with the same length as myarray
with background blue whenever myarray
reads 1 and background red whenever myarray
reads 0. The slider should have no iteraction at all: the user can neither see nor use the handle.
slider = QSlider()
slider.setRange(0, len(myarray)-1)
But I am stuck afterwards: the command
slider.setStyleSheet("QSlider::groove:horizontal {background-color:blue;}")
What I need is, wherever the array reads 0, it shows red background like this(the picture only paints the first cell to red):
I am not sure about how to change parts of it to red. In my program, myarray
can be a large array so I probably need a way to programmatically generate a stylesheet, which I don't know how to do.
Solution
This cannot be achieved using style sheets, as there is no way to set a different background for each "chunk" of the slider.
The only way to properly "colorize" each value is by using QStyle features. Specifically, we need to use sliderPositionFromValue
, which returns the position in pixel for each value.
Since we can assume that all values are equally spread across the slider, we can create "ranges" that split each pair of "ticks" in half based on the full range count.
For instance, if we have a slider length (the space in which the handle can move) of 100 pixels and the slider has 5 values (from 0 to 4), we will have 5 chunks, each 25 pixels wide, with the first one starting ~12 pixels on the left of the slider position at 0
.
For optimization reasons (and to avoid graphic issues of same-color rectangles that are side by side), we only draw rectangles that join similar values.
Here is the result:
And the code (note that I added ticks to the slider to highlight the alignment of the chunks):
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class SliderChunkWidget(QWidget):
def __init__(self, slider, values):
super().__init__()
self.slider = slider
self.values = values
self.setFixedHeight(self.fontMetrics().height())
def paintEvent(self, event):
opt = QStyleOptionSlider()
self.slider.initStyleOption(opt)
style = self.slider.style()
# get the extent of the slider handle
sliderLength = style.pixelMetric(style.PM_SliderLength, opt, self.slider)
offset = sliderLength // 2
# the actual space in which the handle can move
avail = opt.rect.width() - sliderLength
smin = self.slider.minimum()
smax = self.slider.maximum()
# get the position of the first value of the slider
start = style.sliderPositionFromValue(
smin, smax, smin, avail) + offset
# the size of each "chunk"
chunkSize = avail / (len(self.values) - 1)
x = start - chunkSize / 2
chunks = 1
height = self.height() - 1
qp = QPainter(self)
qp.setRenderHint(qp.Antialiasing)
qp.setPen(Qt.NoPen)
oldValue = self.values[0]
for value in self.values[1:]:
if value != oldValue:
# the new value is different, draw the previous rectangle
qp.setBrush(Qt.blue if oldValue else Qt.red)
rect = QRectF(x, 0, chunks * chunkSize, height)
qp.drawRect(rect)
chunks = 1
x = rect.right()
else:
chunks += 1
oldValue = value
# draw the last rectangle
qp.setBrush(Qt.blue if value else Qt.red)
qp.drawRect(x, 0, chunks * chunkSize, height)
app = QApplication([])
values = [0, 0, 0, 1, 1, 1, 0, 1, 0, 1]
test = QWidget()
layout = QVBoxLayout(test)
slider = QSlider(Qt.Horizontal, maximum=len(values) - 1)
layout.addWidget(slider)
slider.setTickPosition(slider.TicksBothSides)
slider.setTickInterval(1)
chunkWidget = SliderChunkWidget(slider, values)
layout.addWidget(chunkWidget)
test.show()
app.exec()
Answered By - musicamante
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.