Setting scrollbar via QListWidget

I would like to implement my own translucent scrollbar, which rests on top of a QListWidget instead of occupying a constant space in my viewport. I do not want to use QML as my QListWidget, and its dynamic content has been fully developed for 6 months.

How can i achieve this. Style sheets are useless for this purpose, because they will not determine the location of the scroll bar. I would like it to be on top of the QListWidget, and not on its side, occupying its space.

I'm talking about something nearby:

enter image description here

Any hints as to how to do this would be appreciated.

+8
qt
source share
2 answers

What you are trying to do is a great example of what qt persistently annoys me - if there is any graphic effect that Qt designers did not think about, creating it themselves, it’s a pain, a constant struggle against Qt, and usually ends up which still refuses.

I suspect you are doing this with small screens in your mind (cell phones? Tablets?), So I think there is no other way to solve this problem.

What I'm trying here is hacked, but otherwise you may have to rewrite the entire scrollbar yourself to add these few missing details. My suggestion:

#ifndef MYSCROLLBAR_H #define MYSCROLLBAR_H #include <QScrollBar> class MyScrollBar : public QScrollBar { Q_OBJECT public: explicit MyScrollBar(QWidget *parent = 0); protected: void showEvent ( QShowEvent * event ); signals: public slots: void updateMask(); }; #endif // MYSCROLLBAR_H 

And in myscrollbar.cpp

 #include "myscrollbar.h" #include <QPaintEvent> #include <QRegion> #include <QStyleOptionSlider> MyScrollBar::MyScrollBar(QWidget *parent) : QScrollBar(parent) { connect(this, SIGNAL(valueChanged(int)), this, SLOT(updateMask())); } void MyScrollBar::updateMask(){ QStyleOptionSlider opt; initStyleOption(&opt); QRegion r(style()->subControlRect(QStyle::CC_ScrollBar, &opt, QStyle::SC_ScrollBarSlider, this)); r+= style()->subControlRect(QStyle::CC_ScrollBar, &opt, QStyle::SC_ScrollBarAddLine, this); r+= style()->subControlRect(QStyle::CC_ScrollBar, &opt, QStyle::SC_ScrollBarSubLine, this); setMask(r); } void MyScrollBar::showEvent ( QShowEvent * event ){ QScrollBar::showEvent(event); updateMask(); } 

Such a scroller will be transparent (both visually and randomly) in any of these non-vital parts. It still creates some artifacts on the widgets below it - I think setMask() should never have been used like that. To reduce it, you can connect the valueChanged() signal to the update() slot of the view widget of your list. This was well reflected in my toy example, but if you add custom widgets to your list, it can become unbearable. It can also lead to performance issues with more complex applications - especially if you are writing for mobile platforms.

Alternatively, you can simply fork out the entire QScrollBar class and simply change its paintEvent to use fewer subControls than SC_All - with the optional setAttribute(Qt::WA_OpaquePaintEvent, false); constructor setAttribute(Qt::WA_OpaquePaintEvent, false); in the designer, it must provide visual transparency. Then you also have to drag the mouse events (if you don’t do anything important) to the list widget widget (again, there is a problem with custom widgets in the view).

Now it remains to write your own layout class (or just manually position it) that will put both the listview and the scroll bar on top of each other in the correct positions - QStackedLayout sounds good, but allows you to see only one layer at any time - obviously not what we are looking for.

The last step is to turn off the default default scrollbars and connect the default signals / slots (invisible) to the slots / signals of your scrollbar to achieve the effect of the actual scroll.

Soon this will require LOT coding. Are you sure such a simple effect is worth it?

** EDIT: **

I create a layout class for stacking widgets on top of each other - this question gave me the motivation to do this completely;)

 #ifndef STACKLAYOUT_H #define STACKLAYOUT_H #include <QLayout> class StackLayout : public QLayout { Q_OBJECT public: StackLayout(); explicit StackLayout(QWidget *parent); ~StackLayout(); void addItem ( QLayoutItem * item ); int count () const; Qt::Orientations expandingDirections () const; bool hasHeightForWidth () const; int heightForWidth ( int w ) const; QLayoutItem * itemAt ( int index ) const; bool isEmpty () const; QSize maximumSize () const; int minimumHeightForWidth ( int w ) const; QSize minimumSize () const; void setGeometry ( const QRect & r ); QSize sizeHint () const; QLayoutItem * takeAt ( int index ); private: QList<QLayoutItem *> itemList; }; #endif // STACKLAYOUT_H 

And the stacklayout.cpp file:

 StackLayout::StackLayout() :QLayout() {} StackLayout::StackLayout(QWidget *parent) : QLayout(parent) { } StackLayout::~StackLayout(){ QLayoutItem *item; foreach (item, itemList){ delete item; } } void StackLayout::addItem ( QLayoutItem * item ){ itemList.append(item); } int StackLayout::count () const{ return itemList.count(); } Qt::Orientations StackLayout::expandingDirections () const{ Qt::Orientations result = 0; QLayoutItem *item; foreach (item, itemList){ result = result | item->expandingDirections(); } return result; } bool StackLayout::hasHeightForWidth () const{ QLayoutItem *item; foreach (item, itemList){ if (item->hasHeightForWidth()) return true; } return false; } int StackLayout::heightForWidth ( int w ) const{ int result = 0; QLayoutItem *item; foreach (item, itemList){ if (item->hasHeightForWidth()) result = qMax(result, item->heightForWidth(w)); } return result; } QLayoutItem * StackLayout::itemAt ( int index ) const{ if (index<itemList.count()) return itemList[index]; return 0; } bool StackLayout::isEmpty () const{ QLayoutItem *item; foreach (item, itemList){ if (!item->isEmpty()) return false; } return true; } QSize StackLayout::maximumSize () const{ QSize result=QLayout::maximumSize(); QLayoutItem *item; foreach (item, itemList){ result = result.boundedTo(item->maximumSize()); } return result; } int StackLayout::minimumHeightForWidth ( int w ) const{ int result = 0; QLayoutItem *item; foreach (item, itemList){ if (item->hasHeightForWidth()) result = qMax(result, item->minimumHeightForWidth(w)); } return result; } QSize StackLayout::minimumSize () const{ QSize result=QLayout::minimumSize(); QLayoutItem *item; foreach (item, itemList){ result = result.expandedTo(item->minimumSize()); } return result; } void StackLayout::setGeometry ( const QRect & r ){ QLayoutItem *item; foreach (item, itemList){ item->setGeometry(r); } } QSize StackLayout::sizeHint () const{ QSize result=QSize(0,0); QLayoutItem *item; foreach (item, itemList){ result = result.expandedTo(item->sizeHint()); } return result; } QLayoutItem * StackLayout::takeAt ( int index ){ if (index < itemList.count()) return itemList.takeAt(index); return 0; } 

Assuming you already have a nice transparent scrollbar to insert it, you would do:

 QWidget* w = new QWidget(); StackLayout* sl = new StackLayout(w); QListView* lv = new QListView(w); sl->addWidget(lv); QHBoxLayout* hbl = new QHBoxLayout(); sl->addItem(hbl); TransparentScrollBar* tsc = new TransparentScrollBar(w); hbl->addWidget(tsc,0); hbl->insertStretch(0,1); 
+14
source share

Here is a sample code for your quests.

Not done: Drag and drop the scroller mouse

Done: Support for any mouseover / shutdown events. Scroll support. The scroll bar is transparent for mouse events.

This is a good starting point for any setting, depending on your task. Using:

 GUI::MegaScrollBar *bar = new GUI::MegaScrollBar( ui->listWidget ); bar->resize( 40, 30 ); // First arg - width of scroller 

MegaScrollBar.h

 #ifndef MEGASCROLLBAR_H #define MEGASCROLLBAR_H #include <QWidget> #include <QPointer> class QAbstractItemView; class QResizeEvent; namespace GUI { class MegaScrollBar : public QWidget { Q_OBJECT public: MegaScrollBar( QAbstractItemView *parentView ); ~MegaScrollBar(); private slots: void updatePos(); private: bool eventFilter( QObject *obj, QEvent *event ); void onResize( QResizeEvent *e ); void paintEvent( QPaintEvent * event ); void resizeEvent( QResizeEvent * event ); QPointer< QAbstractItemView > m_view; QPointer< QWidget > m_scrollBtn; }; } #endif // MEGASCROLLBAR_H 

MegaScrollBar.cpp

 #include "MegaScrollBar.h" #include <QAbstractItemView> #include <QEvent> #include <QResizeEvent> #include <QScrollBar> #include <QDebug> #include <QPainter> #include "ScrollButton.h" namespace GUI { MegaScrollBar::MegaScrollBar( QAbstractItemView *parentView ) : QWidget( parentView, Qt::FramelessWindowHint ) , m_view( parentView ) { Q_ASSERT( parentView ); setAttribute( Qt::WA_TranslucentBackground ); setAttribute( Qt::WA_TransparentForMouseEvents ); m_scrollBtn = new ScrollButton( parentView ); m_scrollBtn->setFixedSize( 20, 40 ); m_view->installEventFilter( this ); QScrollBar *sb = m_view->verticalScrollBar(); connect( sb, SIGNAL( valueChanged( int ) ), this, SLOT( updatePos() ) ); } MegaScrollBar::~MegaScrollBar() { removeEventFilter( m_view ); } bool MegaScrollBar::eventFilter( QObject *obj, QEvent *event ) { switch ( event->type() ) { case QEvent::Enter: m_scrollBtn->show(); break; case QEvent::Leave: m_scrollBtn->hide(); break; case QEvent::Resize: onResize( static_cast< QResizeEvent * >( event ) ); break; } return QWidget::eventFilter( obj, event ); } void MegaScrollBar::onResize( QResizeEvent *e ) { const int x = e->size().width() - width(); const int y = 0; const int w = width(); const int h = e->size().height(); move( x, y ); resize( w, h ); updatePos(); } void MegaScrollBar::updatePos() { QScrollBar *sb = m_view->verticalScrollBar(); const int min = sb->minimum(); const int val = sb->value(); const int max = sb->maximum(); const int x = pos().x() + ( width() - m_scrollBtn->width() ) / 2; if ( max == 0 ) { m_scrollBtn->move( x, pos().y() ); return ; } const int maxY = height() - m_scrollBtn->height(); const int y = ( maxY * val ) / max; m_scrollBtn->move( x, y ); } void MegaScrollBar::paintEvent( QPaintEvent * event ) { Q_UNUSED( event ); QPainter p( this ); QRect rc( 0, 0, rect().width() - 1, rect().height() - 1 ); // Draw any scroll background p.fillRect( rc, QColor( 255, 255, 200, 100 ) ); } void MegaScrollBar::resizeEvent( QResizeEvent * event ) { Q_UNUSED( event ); updatePos(); } } 

Preview:

Sample

You can configure any widget for the scroll button: Here's the custom: ScrollButton.h

 #ifndef SCROLLBUTTON_H #define SCROLLBUTTON_H #include <QWidget> namespace GUI { class ScrollButton : public QWidget { Q_OBJECT public: ScrollButton( QWidget *parent ); ~ScrollButton(); private: void paintEvent( QPaintEvent * event ); }; } #endif // SCROLLBUTTON_H 

ScrollButton.cpp

 #include "ScrollButton.h" #include <QPainter> #include <QGraphicsOpacityEffect> #include <QColor> namespace GUI { ScrollButton::ScrollButton( QWidget *parent ) : QWidget( parent ) { QGraphicsOpacityEffect *op = new QGraphicsOpacityEffect( this ); op->setOpacity( 0.5 ); setGraphicsEffect( op ); } ScrollButton::~ScrollButton() { } void ScrollButton::paintEvent( QPaintEvent * event ) { Q_UNUSED( event ); // Draw any scroll button QPainter p( this ); QRect rc( 5, 5, rect().width() - 6, rect().height() - 6 ); p.fillRect( rc, QColor( 0, 0, 0, 255 ) ); } } 

Please comment if you cannot handle the interaction with the mouse.

+2
source share

All Articles