在開發項目時,有需求要做雙滑塊的滑動條,本着能不造輪子就不造輪子的原則,去網上搜了一番,果然有Qt開源的拓展庫,Qt Extension Library,有一個控件是QxtSpanSlider,實現了簡單的雙滑塊,然而自定義了一些樣式後,就各種問題。
於是又搜索相關的解決辦法,有搜到一位博主的博客,url:https://blog.csdn.net/Ilson_/article/details/103960278,按照博主的方法,沒有起左右,於是又花了C幣去下載,還是不起作用。無奈之下,又不想自己寫這個控件(我不會啊...),於是在源碼基礎上進行了一些猥瑣的(聰明的)封裝,最終實現了目的。
問題:
1、拖動一個滑塊,另一個滑塊軸的位置被span覆蓋,而且兩側的顏色不特殊處理的話,顏色不對。sub-page和add-page要處理;
2、範圍span的顏色控制不了,即使按照上面博主的修改方法也不行,當兩側的顏色有一定透明度時,也還是會把中間的span顏色蒙一層,UI走查的時候一眼就可以看出來,然後就該被深深低鄙視了,不就是改個顏色的事兒嗎...
3、沒有處理鼠標mousePressEvent,那麼鼠標在某個位置按下的時候,滑塊就不能定位到指定位置;
解決思路:
1、設置sub-page和add-page顏色一致,舉例子:
QSlider#TwoSlider::sub-page:horizontal
{
background: rgba(0,0,0,0.1);
height: 8px;
border-radius: 4px;
}
QSlider#TwoSlider::add-page:horizontal
{
background: rgba(0,0,0,0.1);
height: 8px;
border-radius: 4px;
}
2、代碼裏設置顏色(沒成功...),具體修改位置如下:
void QxtSpanSliderPrivate::drawSpan(QStylePainter* painter, const QRect& rect) const
{
QStyleOptionSlider opt;
initStyleOption(&opt);
const QSlider* p = q_ptr;
// area
QRect groove = p->style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderGroove, p);
if (opt.orientation == Qt::Horizontal)
groove.adjust(0, 0, -1, 0);
else
groove.adjust(0, 0, 0, -1);
// pen & brush
/*painter->setPen(QPen(p->palette().color(QPalette::Dark).light(110), 0));
if (opt.orientation == Qt::Horizontal)
setupPainter(painter, opt.orientation, groove.center().x(), groove.top(), groove.center().x(), groove.bottom());
else
setupPainter(painter, opt.orientation, groove.left(), groove.center().y(), groove.right(), groove.center().y());*/
// 相交得到的矩形寬度、高度少了1pixel,需要加上
QRect rt = rect.intersected(groove);
rt.adjust(0, -1, 1, 3);
// 調用自寫函數修改樣式
setupPainter(painter, opt.orientation, rt);
// draw groove
painter->drawRect(rt);
}
自己重載一個setupPainter函數
void QxtSpanSliderPrivate::setupPainter(QPainter* painter, Qt::Orientation orientation,QRect& rect) const
{
painter->setBrush(QBrush(QColor(255, 96, 0)));
painter->setPen(Qt::transparent);
}
這裏面的顏色就是我想要的顏色。然而我這邊的效果是被sub-page和add-page刷新覆蓋了一層,如果sub-page和add-page是不帶透明度的純色,中間設置的就完全不起作用了。
而且,拖動一個滑塊,另一個滑塊座標軸的位置就會被span覆蓋掉,如果是同色還好,不同色的話就廢了。
最後我想了一個猥瑣的辦法,增加三個單獨的QWidget,覆蓋在滑動條上,即兩個滑塊和中間span控件,並設置這個三個控件的顯示不影響下面滑塊控件的使用。 在滑動條刷新的時候,這三個控件也去更新位置和大小,效果還是相當完美的。
具體操作:
在滑塊paintEvent裏添加信號,方便我們封裝的控件處理添加的三個控件
void QxtSpanSlider::paintEvent(QPaintEvent* event)
{
Q_UNUSED(event);
QStylePainter painter(this);
// groove & ticks
QStyleOptionSlider opt;
d_ptr->initStyleOption(&opt);
opt.sliderValue = 0;
opt.sliderPosition = 0;
opt.subControls = QStyle::SC_SliderGroove | QStyle::SC_SliderTickmarks;
painter.drawComplexControl(QStyle::CC_Slider, opt);
// handle rects
opt.sliderPosition = d_ptr->lowerPos;
const QRect lr = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this);
const int lrv = d_ptr->pick(lr.center());
opt.sliderPosition = d_ptr->upperPos;
const QRect ur = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this);
const int urv = d_ptr->pick(ur.center());
emit updateHandle(lr,ur);
...
...
最後的信號即是我添加的,方便知道兩個滑塊的具體位置,然後在我封裝的控件裏,進行如下處理:
connect(m_Slider, &QxtSpanSlider::updateHandle, this, [this](const QRect &lRect, const QRect &uRect)
{
m_Span->move(lRect.x() + m_lWgdt->width(), 10);
m_Span->setFixedWidth(uRect.x() - lRect.x()-m_lWgdt->width());
m_Span->raise();
for (auto &time : m_ValueVector)
{
time.circleWidget->raise();
}
m_lWgdt->move(lRect.x(), lRect.y());
m_uWgdt->move(uRect.x(), uRect.y());
m_lWgdt->raise();
m_uWgdt->raise();
});
time.circleWidget無需理會,是我實現更復雜的功能需要顯示和處理的。這樣就解決了最頭疼的第二個問題;
3、鼠標點擊事件,這個就相對好處理多了。不過你得事先跟產品經理通個氣,看看點擊某個位置,兩個滑塊怎麼處理,就比如在中間點一下,是小值變大,還是大值變小,blabla...
我最終實現的代碼:
bool DoubleSlider::eventFilter(QObject *obj, QEvent *event)
{
if (obj == m_Slider)
{
if (event->type() == QEvent::MouseButtonPress)
{
QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
if (mouseEvent->button() == Qt::LeftButton)
{
double pos = mouseEvent->pos().x() / (double)m_Slider->width();
int value = (pos * (m_Slider->maximum() - m_Slider->minimum()) + m_Slider->minimum()) + 0.5;
int lowerValue = m_Slider->lowerValue();
int upperValue = m_Slider->upperValue();
if (value < upperValue)
{
m_Slider->setLowerValue(value);
}
else if (value > upperValue)
{
m_Slider->setUpperValue(value);
}
}
}
}
return QObject::eventFilter(obj, event);
}
最後就完美的解決問題了,給大家看下最後的效果圖吧:
代碼有時間整理後上傳。
原創不易,且行且珍惜: