Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. QListWidget - Setting default row background color
Forum Updated to NodeBB v4.3 + New Features

QListWidget - Setting default row background color

Scheduled Pinned Locked Moved Solved General and Desktop
9 Posts 4 Posters 2.1k Views 2 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • H Offline
    H Offline
    hbatalha
    wrote on last edited by hbatalha
    #1

    I have a QListWidget that I use to store file paths for a media player. Below I have its configuration:

        this->setAcceptDrops(true);
        this->setDragEnabled(true);
        this->setSelectionMode( QAbstractItemView::ExtendedSelection );
        this->setContextMenuPolicy( Qt::CustomContextMenu );
        this->setDragDropMode( QAbstractItemView::InternalMove );
        this->setSelectionBehavior( QAbstractItemView::SelectRows );
        this->setAlternatingRowColors( true );
        this->setDropIndicatorShown(true);
    

    The problem I am having is that when I select a row to play a media, I want to change that row so it is visible to user which row in the playlist is currently playing.

    I can do that by setting background to a different color. But when a media is changed I need it restore the background it had before.

    Right now I use this:

    this->item(previousSelectedRow)->setBackground(QColor());
    

    But the problem with this approach is that it will destroy the setAlternatingRowColors behavior that is meant to be true .

    I am on Windows using Qt 6.2.0 with MingW.
    I hope the question is clear.

    JonBJ 1 Reply Last reply
    0
    • H hbatalha

      @mrjj Thanks that's the perfect solution for this problem.

      However for some reason it takes too long to update the list when I change the media, most of the time I have to give the list focus by clicking it or clicking in any item so the new playing row is highlighted.

      mrjjM Offline
      mrjjM Offline
      mrjj
      Lifetime Qt Champion
      wrote on last edited by
      #8

      @hbatalha

      Hi
      It's my fault.
      Using an int for the row has the side effect that the model and the view know
      nothing about we change the Play row so it doesn't update before you focus it.
      Normally one can have model issues an update but cant do that here so I didn't find a good
      way to let it know the row was changed so the delegate would be asked to repaint.

      So we could try use the model and a UserRole

      class PlayingDelegate: public QStyledItemDelegate
      {
          Q_OBJECT
      public:
          using QStyledItemDelegate::QStyledItemDelegate;
          void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
          {
              QStyledItemDelegate::paint(painter, option, index);
              if (! index.isValid() ) return;
              const QAbstractItemModel *model = index.model();
              if (!model) return;
              bool isPlay=model->data(index,Qt::UserRole).toBool();
              if ( isPlay ) {
                  painter->setBrush(QColor(0,255,0,100));
                  painter->drawRect( option.rect.adjusted(0, 0, -1, -1) );
              }
          }       
      };
      

      and then we set the playRow like

      int row= ui->listWidget->currentRow();
      auto model = ui->listWidget->model();
      model->setData(model->index(row,0),true, Qt::UserRole);  // true to set it, false to clear it
      

      This will update instant.

      alt text

      This has one side effect though. You need to keep track of last playRow so you can clear it again.

      Or if the playlist is not that super long, you could just have a clear function that reset all.

      void ResetPlayRow(QAbstractItemModel *model)
      {
          for (int var = 0; var < model->rowCount(); ++var) {
              model->setData(model->index(var, 0), false, Qt::UserRole);
          }
      }
      

      and then clear all before you set new. /just like you must stop the old song playing)

       int row = ui->listWidget->currentRow();
       auto model = ui->listWidget->model();
       ResetPlayRow(model); // reset old
       model->setData(model->index(row, 0), true, Qt::UserRole);
      

      Then it works quite well but if you have many hundreds of rows, its not an optimal solution.

      alt text

      H 1 Reply Last reply
      2
      • H hbatalha

        I have a QListWidget that I use to store file paths for a media player. Below I have its configuration:

            this->setAcceptDrops(true);
            this->setDragEnabled(true);
            this->setSelectionMode( QAbstractItemView::ExtendedSelection );
            this->setContextMenuPolicy( Qt::CustomContextMenu );
            this->setDragDropMode( QAbstractItemView::InternalMove );
            this->setSelectionBehavior( QAbstractItemView::SelectRows );
            this->setAlternatingRowColors( true );
            this->setDropIndicatorShown(true);
        

        The problem I am having is that when I select a row to play a media, I want to change that row so it is visible to user which row in the playlist is currently playing.

        I can do that by setting background to a different color. But when a media is changed I need it restore the background it had before.

        Right now I use this:

        this->item(previousSelectedRow)->setBackground(QColor());
        

        But the problem with this approach is that it will destroy the setAlternatingRowColors behavior that is meant to be true .

        I am on Windows using Qt 6.2.0 with MingW.
        I hope the question is clear.

        JonBJ Offline
        JonBJ Offline
        JonB
        wrote on last edited by
        #2

        @hbatalha
        I would expect you to achieve this by setting the selection color, which should be independent of any coloring and apply only to the row while it is selected. You can also do that from stylesheet instead of code.

        H 1 Reply Last reply
        0
        • JonBJ JonB

          @hbatalha
          I would expect you to achieve this by setting the selection color, which should be independent of any coloring and apply only to the row while it is selected. You can also do that from stylesheet instead of code.

          H Offline
          H Offline
          hbatalha
          wrote on last edited by
          #3

          @JonB I already have selection color set, I use it to select rows that are meant to be moved, deleted or played. I want a behavior like the one on VLC Player, I am going through its source code but haven't found how they do that yet.

          which should be independent of any coloring and apply only to the row while it is selected

          please elaborate more.

          1 Reply Last reply
          0
          • Christian EhrlicherC Offline
            Christian EhrlicherC Offline
            Christian Ehrlicher
            Lifetime Qt Champion
            wrote on last edited by
            #4

            Return the appropriate color in QAbstractItemView::data() or use a stylesheet for QListView. Since you don't use a custom model you must go with the stylesheet.

            Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
            Visit the Qt Academy at https://academy.qt.io/catalog

            H 1 Reply Last reply
            0
            • Christian EhrlicherC Christian Ehrlicher

              Return the appropriate color in QAbstractItemView::data() or use a stylesheet for QListView. Since you don't use a custom model you must go with the stylesheet.

              H Offline
              H Offline
              hbatalha
              wrote on last edited by
              #5

              @Christian-Ehrlicher I don't understand how to do that to solve my problem

              1 Reply Last reply
              0
              • mrjjM Offline
                mrjjM Offline
                mrjj
                Lifetime Qt Champion
                wrote on last edited by mrjj
                #6

                Hi
                For such effect, you can also consider at Delegate.

                alt text
                However, since it uses a private ListModel subclass we cant access in any easy way,
                its not optimal as we just use row index to color it and if you added or remove items over it,
                It stays at the same row index so you have to handle that.

                class PlayingDelegate: public QStyledItemDelegate
                {
                    Q_OBJECT
                public:
                    using QStyledItemDelegate::QStyledItemDelegate;
                    void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
                    {
                        QStyledItemDelegate::paint(painter, option, index);
                        if (! index.isValid() ) return;
                        const QAbstractItemModel *model = index.model();
                        if (!model) return;
                        if ( playRow == index.row() ) {
                            painter->setBrush(QColor(0,255,0,100));
                            painter->drawRect( option.rect.adjusted(0, 0, -1, -1) );
                        }
                    }
                
                    void setPlayRow(int newPlayRow)
                    {
                        playRow = newPlayRow;
                    }
                
                private:
                    int playRow = -1;
                };
                

                have a
                PlayingDelegate * delegate;
                as a member of main window so we can address it from other functions

                Then in constructor, init it
                delegate = new PlayingDelegate;
                ui->listWidget->setItemDelegate(delegate );

                Then to set a playline. (like play button did )
                int row= ui->listWidget->currentRow();
                delegate->setPlayRow(row);

                to clear it , call it with ( like stop did)
                delegate->setPlayRow(-1);

                This said, it would be better with a listView and std.model and then we can
                set it via the model and a UserRole and it would move correctly around with delete and insert.
                If you consider this, do know its not much different using listVIEW + a model as
                you then just create model items (versus QListWidget items) , insert to the model and then set the model to the view.

                H 1 Reply Last reply
                1
                • mrjjM mrjj

                  Hi
                  For such effect, you can also consider at Delegate.

                  alt text
                  However, since it uses a private ListModel subclass we cant access in any easy way,
                  its not optimal as we just use row index to color it and if you added or remove items over it,
                  It stays at the same row index so you have to handle that.

                  class PlayingDelegate: public QStyledItemDelegate
                  {
                      Q_OBJECT
                  public:
                      using QStyledItemDelegate::QStyledItemDelegate;
                      void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
                      {
                          QStyledItemDelegate::paint(painter, option, index);
                          if (! index.isValid() ) return;
                          const QAbstractItemModel *model = index.model();
                          if (!model) return;
                          if ( playRow == index.row() ) {
                              painter->setBrush(QColor(0,255,0,100));
                              painter->drawRect( option.rect.adjusted(0, 0, -1, -1) );
                          }
                      }
                  
                      void setPlayRow(int newPlayRow)
                      {
                          playRow = newPlayRow;
                      }
                  
                  private:
                      int playRow = -1;
                  };
                  

                  have a
                  PlayingDelegate * delegate;
                  as a member of main window so we can address it from other functions

                  Then in constructor, init it
                  delegate = new PlayingDelegate;
                  ui->listWidget->setItemDelegate(delegate );

                  Then to set a playline. (like play button did )
                  int row= ui->listWidget->currentRow();
                  delegate->setPlayRow(row);

                  to clear it , call it with ( like stop did)
                  delegate->setPlayRow(-1);

                  This said, it would be better with a listView and std.model and then we can
                  set it via the model and a UserRole and it would move correctly around with delete and insert.
                  If you consider this, do know its not much different using listVIEW + a model as
                  you then just create model items (versus QListWidget items) , insert to the model and then set the model to the view.

                  H Offline
                  H Offline
                  hbatalha
                  wrote on last edited by
                  #7

                  @mrjj Thanks that's the perfect solution for this problem.

                  However for some reason it takes too long to update the list when I change the media, most of the time I have to give the list focus by clicking it or clicking in any item so the new playing row is highlighted.

                  mrjjM 1 Reply Last reply
                  0
                  • H hbatalha

                    @mrjj Thanks that's the perfect solution for this problem.

                    However for some reason it takes too long to update the list when I change the media, most of the time I have to give the list focus by clicking it or clicking in any item so the new playing row is highlighted.

                    mrjjM Offline
                    mrjjM Offline
                    mrjj
                    Lifetime Qt Champion
                    wrote on last edited by
                    #8

                    @hbatalha

                    Hi
                    It's my fault.
                    Using an int for the row has the side effect that the model and the view know
                    nothing about we change the Play row so it doesn't update before you focus it.
                    Normally one can have model issues an update but cant do that here so I didn't find a good
                    way to let it know the row was changed so the delegate would be asked to repaint.

                    So we could try use the model and a UserRole

                    class PlayingDelegate: public QStyledItemDelegate
                    {
                        Q_OBJECT
                    public:
                        using QStyledItemDelegate::QStyledItemDelegate;
                        void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
                        {
                            QStyledItemDelegate::paint(painter, option, index);
                            if (! index.isValid() ) return;
                            const QAbstractItemModel *model = index.model();
                            if (!model) return;
                            bool isPlay=model->data(index,Qt::UserRole).toBool();
                            if ( isPlay ) {
                                painter->setBrush(QColor(0,255,0,100));
                                painter->drawRect( option.rect.adjusted(0, 0, -1, -1) );
                            }
                        }       
                    };
                    

                    and then we set the playRow like

                    int row= ui->listWidget->currentRow();
                    auto model = ui->listWidget->model();
                    model->setData(model->index(row,0),true, Qt::UserRole);  // true to set it, false to clear it
                    

                    This will update instant.

                    alt text

                    This has one side effect though. You need to keep track of last playRow so you can clear it again.

                    Or if the playlist is not that super long, you could just have a clear function that reset all.

                    void ResetPlayRow(QAbstractItemModel *model)
                    {
                        for (int var = 0; var < model->rowCount(); ++var) {
                            model->setData(model->index(var, 0), false, Qt::UserRole);
                        }
                    }
                    

                    and then clear all before you set new. /just like you must stop the old song playing)

                     int row = ui->listWidget->currentRow();
                     auto model = ui->listWidget->model();
                     ResetPlayRow(model); // reset old
                     model->setData(model->index(row, 0), true, Qt::UserRole);
                    

                    Then it works quite well but if you have many hundreds of rows, its not an optimal solution.

                    alt text

                    H 1 Reply Last reply
                    2
                    • mrjjM mrjj

                      @hbatalha

                      Hi
                      It's my fault.
                      Using an int for the row has the side effect that the model and the view know
                      nothing about we change the Play row so it doesn't update before you focus it.
                      Normally one can have model issues an update but cant do that here so I didn't find a good
                      way to let it know the row was changed so the delegate would be asked to repaint.

                      So we could try use the model and a UserRole

                      class PlayingDelegate: public QStyledItemDelegate
                      {
                          Q_OBJECT
                      public:
                          using QStyledItemDelegate::QStyledItemDelegate;
                          void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
                          {
                              QStyledItemDelegate::paint(painter, option, index);
                              if (! index.isValid() ) return;
                              const QAbstractItemModel *model = index.model();
                              if (!model) return;
                              bool isPlay=model->data(index,Qt::UserRole).toBool();
                              if ( isPlay ) {
                                  painter->setBrush(QColor(0,255,0,100));
                                  painter->drawRect( option.rect.adjusted(0, 0, -1, -1) );
                              }
                          }       
                      };
                      

                      and then we set the playRow like

                      int row= ui->listWidget->currentRow();
                      auto model = ui->listWidget->model();
                      model->setData(model->index(row,0),true, Qt::UserRole);  // true to set it, false to clear it
                      

                      This will update instant.

                      alt text

                      This has one side effect though. You need to keep track of last playRow so you can clear it again.

                      Or if the playlist is not that super long, you could just have a clear function that reset all.

                      void ResetPlayRow(QAbstractItemModel *model)
                      {
                          for (int var = 0; var < model->rowCount(); ++var) {
                              model->setData(model->index(var, 0), false, Qt::UserRole);
                          }
                      }
                      

                      and then clear all before you set new. /just like you must stop the old song playing)

                       int row = ui->listWidget->currentRow();
                       auto model = ui->listWidget->model();
                       ResetPlayRow(model); // reset old
                       model->setData(model->index(row, 0), true, Qt::UserRole);
                      

                      Then it works quite well but if you have many hundreds of rows, its not an optimal solution.

                      alt text

                      H Offline
                      H Offline
                      hbatalha
                      wrote on last edited by hbatalha
                      #9

                      @mrjj Working like a charm, thank you very much.

                      1 Reply Last reply
                      1

                      • Login

                      • Login or register to search.
                      • First post
                        Last post
                      0
                      • Categories
                      • Recent
                      • Tags
                      • Popular
                      • Users
                      • Groups
                      • Search
                      • Get Qt Extensions
                      • Unsolved