我想实现我自己的半透明滚动条,它绘制在 QListWidget 的顶部,而不是在其视口中占用永久空间。我不希望使用 QML 作为我的 QListWidget 并且它的动态内容现在已经完全开发了 6 个月。
我怎样才能做到这一点。样式表对此毫无用处,因为它们不会确定滚动条的位置。我希望它位于 QListWidget 之上,而不是在它的一侧,占用它的空间。
我说的是这附近的一些事情:
任何有关如何做到这一点的提示将不胜感激。
我想实现我自己的半透明滚动条,它绘制在 QListWidget 的顶部,而不是在其视口中占用永久空间。我不希望使用 QML 作为我的 QListWidget 并且它的动态内容现在已经完全开发了 6 个月。
我怎样才能做到这一点。样式表对此毫无用处,因为它们不会确定滚动条的位置。我希望它位于 QListWidget 之上,而不是在它的一侧,占用它的空间。
我说的是这附近的一些事情:
任何有关如何做到这一点的提示将不胜感激。
你正在尝试做的是一个完美的例子,它一直让我对 qt 感到恼火——如果有一些 Qt 的设计者没有想到的图形效果,那么你自己创建它是一种痛苦,不断地与 Qt 作斗争,并且通常以放弃而告终。
我怀疑您是在脑海中使用小屏幕(手机?平板电脑?),所以我想没有其他方法可以解决这个问题。
我在这里尝试的是hacky,但否则您可能必须自己重写整个滚动条才能添加那些缺少的细节。我的提议是:
#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
在 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();
}
这样的滚动条在它的任何非重要部分都是透明的(无论是在视觉上还是在事件方面)。它仍然会在位于其下方的小部件上创建一些工件 - 我想setMask()
不应该像这样使用它。为了减轻它,您可以将valueChanged()
信号连接到update()
列表小部件视口的插槽。这在我的玩具示例中效果很好,但是如果您在列表中嵌入自定义小部件,它可能会变得难以应付。在更复杂的应用程序中,它还可能使您遇到性能问题——尤其是在您为移动平台编写的情况下。
或者,您可以“分叉”整个 QScrollBar 类并简单地修改它的paintEvent 以使用比 SC_All 更少的 subControls -setAttribute(Qt::WA_OpaquePaintEvent, false);
在构造函数中附加它应该提供视觉透明度。然后,您还应该将鼠标事件(如果没有击中任何重要的东西)转发到列表小部件的视口(同样,视图中的自定义小部件有问题)。
现在剩下的就是编写自己的布局类(或者只是手动定位它),它将列表视图和滚动条放在正确的位置 - QStackedLayout 听起来不错,但它只允许在任何给定时间看到一层 - 显然不是我们正在寻找什么。
最后一步是关闭视图上的默认滚动条,并将默认(不可见)滚动条的信号/插槽连接到滚动条的插槽/信号,以实现实际滚动的效果。
很快,这将需要大量的编码才能完成。你确定这么简单的效果值得吗?
** 编辑: **
我创建了一个布局类,用于将小部件相互堆叠 - 这个问题最终给了我这样做的动力;)
#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
和 stacklayout.cpp 文件:
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;
}
假设你已经有一些漂亮的透明滚动条,插入它你会这样做:
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);
这是您的问题的示例代码。
未完成:鼠标拖动滚动条
完成:支持任何鼠标悬停/离开事件支持滚动滚动条对鼠标事件是透明的
这是根据您的任务进行任何自定义的良好起点。用法:
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();
}
}
预习:
可以为滚动按钮设置任何小部件:这是自定义小部件: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
滚动按钮.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 ) );
}
}
如果您无法处理鼠标交互,请发表评论。