Russian Qt Forum
Апрель 24, 2024, 12:25 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1]   Вниз
  Печать  
Автор Тема: [РЕШЕНО]Виджет с автоизменяемым размером картинок при ресайзе  (Прочитано 7139 раз)
PavelIgorevich
Гость
« : Сентябрь 12, 2014, 14:46 »

Добрый день всем любителям QT!

Стоит задача: есть 6 картинок разных размеров. Нужно вставить их в виджет вертикально одна за одной, но так, чтобы ширина этих изображений всегда подгонялась под ширину виджета, куда они были вставлены, даже при ресайзе. Проблема также в том, что при ресайзе картинки по горизонтали, она должна ресайзится и по вертикали для сохранения пропорций. В этом случае наступает момент, когда суммарный выртикальный размер 6 картинок становится больше, чем размер виджета, поэтому нужно показывать скролл.

Перепробовал scrollArea, переопределил paintEvent - все равно выходит не то. Буду рад любой помощи! Спасибо!

Код:
class ImageLabel2 : public QWidget
{
    Q_OBJECT

public:
    int startHeigth;
    int startWidth;
    explicit ImageLabel2(QWidget *parent = 0);
    const QPixmap* pixmap() const;

public slots:
    void setPixmap(const QPixmap&);

protected:
    void paintEvent(QPaintEvent *);

private:
    QPixmap pix;
};


Код:
ImageLabel2::ImageLabel2(QWidget *parent) :
    QWidget(parent)
{
}

void ImageLabel2::paintEvent(QPaintEvent *event) {
    QWidget::paintEvent(event);

    if (pix.isNull())
        return;

    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing);

    QSize pixSize = pix.size();
    pixSize.scale(this->size(), Qt::KeepAspectRatio);//event->rect().size()




    QPixmap scaledPix = pix.scaled(pixSize,
                                   Qt::KeepAspectRatio,
                                   Qt::SmoothTransformation
                                   );

    painter.drawPixmap(QPoint(), scaledPix);

    qDebug() << pixSize.width() << " " << scaledPix.width() << " " << this->width();

}

const QPixmap* ImageLabel2::pixmap() const {
    return &pix;
}

void ImageLabel2::setPixmap (const QPixmap &pixmap){
    pix = pixmap;
    startHeigth = pixmap.height();
    startWidth = pixmap.width();
}


Использую так:
Код:
QVBoxLayout *scrollWidgetLayout = new QVBoxLayout(ui->scrollArea);


    QFrame* frame = new QFrame(ui->scrollArea);
    frame->setLayout(scrollWidgetLayout);


    ImageLabel2 *label = new ImageLabel2(ui->scrollAreaWidgetContents);
    //label->setSizePolicy(QSizePolicy::Maximum,QSizePolicy::Maximum);
    label->setPixmap(QPixmap::fromImage(QImage("1.jpg")));

    scrollWidgetLayout->addWidget(label);
    ImageLabel2 *label2 = new ImageLabel2(ui->scrollAreaWidgetContents);
    //label->setSizePolicy(QSizePolicy::Maximum,QSizePolicy::Maximum);
    label2->setPixmap(QPixmap::fromImage(QImage("1.jpg")));
    scrollWidgetLayout->addWidget(label2);

    ImageLabel2 *label3 = new ImageLabel2(ui->scrollAreaWidgetContents);
    //label->setSizePolicy(QSizePolicy::Maximum,QSizePolicy::Maximum);
    label3->setPixmap(QPixmap::fromImage(QImage("1.jpg")));
    scrollWidgetLayout->addWidget(label3);


    ui->scrollArea->setWidget(frame);

« Последнее редактирование: Сентябрь 13, 2014, 19:10 от PavelIgorevich » Записан
Hrundel
Гость
« Ответ #1 : Сентябрь 12, 2014, 15:28 »

Не совсем понятно, в чем именно проблема?  В скалировании картинок, или в scrollArea?
Записан
PavelIgorevich
Гость
« Ответ #2 : Сентябрь 12, 2014, 15:41 »

Не совсем понятно, в чем именно проблема?  В скалировании картинок, или в scrollArea?

Суть в том, что изображения вроде бы скалируются нормально, но не появляется скролл, а из-за этого уже начинаются проблемы в скалировании - то есть, когда высота картинки достигает 1/6 высоты виджета, то скалирование прекращается, так как при переопределении paintEvent я использую KeepAspectRatio.

В коде ui->scrollArea - перенесенный в GUI QScrollWidget через Qt Designer, а ui->scrollAreaWidgetContents - создался автоматически

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



« Последнее редактирование: Сентябрь 12, 2014, 16:03 от PavelIgorevich » Записан
Hrundel
Гость
« Ответ #3 : Сентябрь 12, 2014, 16:05 »

Я бы, пожалуй, вместо скорола QGraphicsView использовал.
Записан
PavelIgorevich
Гость
« Ответ #4 : Сентябрь 12, 2014, 16:54 »

Я бы, пожалуй, вместо скорола QGraphicsView использовал.

Мало опыта у меня с ним. Думаю, будет много сложнее разобраться
Записан
vregess
Гость
« Ответ #5 : Сентябрь 12, 2014, 17:31 »

Я бы использовал классы MVC:
 - QListView/QListWidget  - для отображения списка картинок (прокрутка будет работать автоматически)
- свой делегат QItemDelegate/QStyledItemDelegate - для задания геометрии и рисования картинки

Основная работа будет в делегате.

Код
C++ (Qt)
virtual void QStyledItemDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const
 
отрисовывет картинку


Код
C++ (Qt)
virtual QSize QStyledItemDelegate::sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index) const
 
возвращает пропорциональные размеры в зависимости от размера QListView/QListWidget
Записан
Hrundel
Гость
« Ответ #6 : Сентябрь 12, 2014, 18:12 »

Я бы использовал классы MVC:
 - QListView/QListWidget  - для отображения списка картинок (прокрутка будет работать автоматически)
- свой делегат QItemDelegate/QStyledItemDelegate - для задания геометрии и рисования картинки

Основная работа будет в делегате.

Тоже хороший вариант. Поддерживаю.
Записан
PavelIgorevich
Гость
« Ответ #7 : Сентябрь 13, 2014, 02:54 »

Спасибо большое за ответы - сделал через QListWidget и переопределение делегата


Вот так рисуем наш Item
Код:
class MyDelegate : public QStyledItemDelegate  {
    public:
    MyDelegate(QObject *parent=0) : QStyledItemDelegate (parent)
    {

    }

    void paint ( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const
    {
        if(option.state & QStyle::State_Selected){
            painter->fillRect(option.rect, option.palette.color(QPalette::Highlight));
            return;
        }
        QVariant v = index.data(Qt::UserRole);
        QImage img = v.value<QImage>();
        painter->drawImage(option.rect,img);
    }

    QSize sizeHint ( const QStyleOptionViewItem & option, const QModelIndex & index ) const
    {

        QImage img = index.data(Qt::UserRole).value<QImage>();
        int heigth = img.height();
        int width = img.width();
        return QSize(((QListWidget*)parent())->width(), ((QListWidget*)parent())->width()*heigth/width);
    }
};
Кстати, доп.вопрос: нормально ли то, как я получаю ширину родительского виджета QListWidget через приведение типов? ((QListWidget*)parent())->width(). Может, есть нормальные практики?


Вот так заполняем QListWidget новыми item'ами. Передаю картинку как data в item, чтобы можно было получить ее соотношение сторон и собственно, само изображение

Код:
QImage image1("1.jpg");
    QListWidgetItem *item1 = new QListWidgetItem();
    item1->setData(Qt::UserRole, image1);
    ui->listWidget->addItem(item1);

    QImage image2("2.jpg");
    QListWidgetItem *item2 = new QListWidgetItem();
    item2->setData(Qt::UserRole, image2);
    ui->listWidget->addItem(item2);

    QImage image3("3.jpg");
    QListWidgetItem *item3 = new QListWidgetItem();
    item3->setData(Qt::UserRole, image3);
    ui->listWidget->addItem(item3);

    QImage image4("4.jpg");
    QListWidgetItem *item4 = new QListWidgetItem();
    item4->setData(Qt::UserRole, image4);
    ui->listWidget->addItem(item4);

    ui->listWidget->setItemDelegate(new MyDelegate(ui->listWidget));

Картинки действительно начинают отлично скейлиться, однако, при этом не скейлятся сами QListWidgetItem'ы - высота не меняется - приложил скриншоты. Плохо соображаю уже в 4 утра - их, получается, тоже нужно переопределять? Если да, то как это сделать? Или, может, нужно просто поиграться с настройками QListWidget?

Записан
Hrundel
Гость
« Ответ #8 : Сентябрь 13, 2014, 10:26 »

Код
C++ (Qt)
items[itemsCounter]->setSizeHint(QSize(items[itemsCounter]->sizeHint().width(), newHeight)); // При условии, что они хранятся в векторе
« Последнее редактирование: Сентябрь 13, 2014, 12:24 от Hrundel » Записан
PavelIgorevich
Гость
« Ответ #9 : Сентябрь 13, 2014, 13:08 »

Код
C++ (Qt)
items[itemsCounter]->setSizeHint(QSize(items[itemsCounter]->sizeHint().width(), newHeight)); // При условии, что они хранятся в векторе

Тут немного непонятно - это нужно отлавливать resize-сигнал для QListWidget и внутри него устанавливать sizeHint для каждого item'а?
Записан
Hrundel
Гость
« Ответ #10 : Сентябрь 13, 2014, 13:38 »

Ну, я бы так попробовал сделать.
Записан
PavelIgorevich
Гость
« Ответ #11 : Сентябрь 13, 2014, 13:54 »

Ну, я бы так попробовал сделать.

тут тогда получается еще одна проблема - реализовать доступ к тому самому желаемому параметру newHeigth - мы же его динамически вычисляем в делегате, а как его передать в resizeEvent?
Записан
Hrundel
Гость
« Ответ #12 : Сентябрь 13, 2014, 14:22 »

Ну какой-то указатель заделать. Я ведь не вижу твою архитектуру, говорю наобум. Может быть, можно высоту и вне события менять. Например, непосредственно перед самим событием.
Записан
PavelIgorevich
Гость
« Ответ #13 : Сентябрь 13, 2014, 14:49 »

Вобщем, как подсказал Hrundel, я чуть пошаманил с QListWidget, а именно переопределил его resizeEvent вот так:

Код:
class MyQListWidget:public QListWidget
{
    void resizeEvent(QResizeEvent *e)
    {
        for(int i = 0; i < this->count(); i++)
        {
            QListWidgetItem*item = this->item(i);
            QImage img = item->data(Qt::UserRole).value<QImage>();
            item->setSizeHint(QSize(this->width(),(this->width()*img.height())/img.width()));
        }
    }
};

Все стало работать отлично! Костыльный метод, но проблему решил!

Спасибо Всем за ответы и помощь!
Записан
vregess
Гость
« Ответ #14 : Сентябрь 14, 2014, 20:49 »

Есть способ лучше

Код
C++ (Qt)
void QAbstractItemDelegate::sizeHintChanged(const QModelIndex & index)
 

Resize отлавливать придется, но можно все сделать в делегате:

Код
C++ (Qt)
 
class MyDelegate: public QStyledItemDelegate
{
   Q_OBJECT
public:
   MyDelegate(QAbstractItemView *view)
       : QStyledItemDelegate(view), m_view(view)
   {
       m_view->installEventFilter(this);
   }
 
   virtual bool eventFilter(QObject *watched, QEvent *event)
   {
       if (watched == m_view && event->type() == QEvent::Resize && m_view->model())
       {
           QResizeEvent *ev = static_cast<QResizeEvent*>(event);
           if (ev->oldSize().width() != ev->size().width())
           {
               QAbstractItemModel *model = m_view->model();
               int count = model->rowCount();
               for (int i = count; i > 0; --i)
                   emit sizeHintChanged(model->index(i, 0));
           }
       }
 
       return QStyledItemDelegate::eventFilter(watched, event);
   }
 
   virtual void paint(
       QPainter *painter,
       const QStyleOptionViewItem &option,
       const QModelIndex &index) const
   {
       ...
   }
 
   QSize sizeHint(
       const QStyleOptionViewItem &option,
       const QModelIndex &index) const
   {
       ...
   }
 
private:
   QAbstractItemView *m_view;
};
 

Будет работать с любым View-классом.
Записан
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.156 секунд. Запросов: 20.