2013-04-29 2 views
8

Я хотел бы реализовать свою собственную полупрозрачную полосу прокрутки, которая опирается на верх QListWidget вместо того, чтобы занимать постоянное пространство в своем окне просмотра. Я не хочу использовать QML как мой QListWidget, и его динамический контент уже полностью разработан в течение 6 месяцев.Настройка полосы прокрутки над QListWidget

Как я могу это достичь. Таблицы стилей бесполезны для этой цели, поскольку они не будут определять расположение полосы прокрутки. Я бы хотел, чтобы он был поверх QListWidget, а не на его стороне, занимая его пространство.

Я говорю о чем-то в районе этого:

enter image description here

Любые намеки о том, как сделать это будет оценено.

+0

Я думаю, что единственный способ - создать собственный контроль на основе QListView и реализовать собственный рисунок полосы прокрутки (теперь только рисование, но и логика). Я попытаюсь написать пример кода завтра. –

+0

Должен быть QListWidget, кстати, это то, что я использую, когда я помещаю живые «виджеты» в строки согласно требованиям моего приложения. Вы не можете добиться этого с помощью системы делегирования QListView. – JasonGenX

+0

Делегаты используются для рисования контента. Свиток не является содержимым. Цель состоит в том, чтобы скрыть прокрутку и реализовать пользовательский прокрутка по существующему QListWidget. –

ответ

14

То, что вы пытаетесь сделать, - прекрасный пример того, что настойчиво меня раздражает qt - если есть какой-то графический эффект, о котором дизайнеры Qt не думали, создание его по себе - это боль, постоянная борьба против Qt и, как правило, заканчивается тем, что все равно отказывается.

Я подозреваю, что вы делаете это с помощью небольших экранов в вашем сознании (сотовые телефоны? Планшеты?), Поэтому я думаю, что нет другого способа решить эту проблему.

То, что я пытаюсь здесь, является взломанным, но в противном случае вам, вероятно, придется переписать всю полосу прокрутки самостоятельно, чтобы добавить эти несколько недостающих деталей. Мое суждение:

#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, чтобы использовать меньше subControls, чем SC_All - с дополнительным setAttribute(Qt::WA_OpaquePaintEvent, false); в конструкторе он должен обеспечивать визуальную прозрачность. Затем вы также должны перенаправить события мыши (если не нанести ничего важного) в виджет виджета списка (опять же, проблема с пользовательскими виджетами в представлении).

Теперь остается только написать собственный класс макета (или просто вручную разместить его), который будет позиционировать как списки, так и полосу прокрутки друг на друге в правильных положениях - QStackedLayout звучит хорошо, но позволяет видеть только один слой в любом заданном время - явно не то, что мы ищем.

Последний шаг - выключение отображения прокрутки по умолчанию и подключение сигналов/слотов по умолчанию (невидимая) полоса прокрутки к слотам/сигналам вашей полосы прокрутки для достижения эффекта фактической прокрутки.

Вскоре для этого потребуется кодирование LOT. Вы уверены, что такой простой эффект стоит того?

** EDIT: **

создать класс макет для укладки виджетов друг на друга - этот вопрос дал мне мотивацию, чтобы сделать это, наконец,)

#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); 
+0

BTW Если вы решите его кодировать, я могу предложить еще несколько кода/помощи. –

+0

Простите меня, но этот файл является классом TransparentScrollBar? Я полагаю, что это простой виджет. –

+0

Прозрачная полоса прокрутки Я имел в виду что-то вроде ответа Дмитрия Сазонова. –

2

Вот пример кода для вашего квестоина.

Не сделан: мыши перетаскивания скроллера

Сделан: Поддержки любого наведению мышей/оставить события Поддержки прокрутки полосы прокрутки прозрачно для событий мыши

Это хороший старт точка для любого настройка в зависимости от вашей задачи. Использование:

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

можно настроить у виджет кнопки прокрутки: Здесь обычай один: 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)); 
    } 

} 

Прокомментируйте, если вы не можете справиться взаимодействия с мышью.

+0

Будет ли 'Qt :: WA_TransparentForMouseEvents' не отключать всю функциональность мыши на такой полосе прокрутки? Единственный способ, с помощью которого я могу заставить его работать, - это отфильтровать события мыши (или перехватить какой-либо другой способ, например, с помощью подкласса виджета дерева), из окна просмотра и передать его виджет полосы прокрутки. Это становится все труднее, если у вас есть пользовательские виджеты в вашем представлении. Применение маски, как я предложил, может быть вариантом. –

+0

Или вы имеете в виду, что ваша полоса прокрутки предназначена только для информации, а не для активной прокрутки виджета с ее помощью? –

+0

Для взаимодействия вы можете установить любой виджет в MegaScrollBar :: MegaScrollBar(). Также должно быть реализовано перенаправление событий мыши из виджета m_scrollBtn. –

Смежные вопросы