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. QStyledItemDelegate highlighting Indentation space on focus?
Forum Updated to NodeBB v4.3 + New Features

QStyledItemDelegate highlighting Indentation space on focus?

Scheduled Pinned Locked Moved Solved General and Desktop
qt c++delegatehelpproblemqtreeview
19 Posts 5 Posters 2.2k 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.
  • Chris KawaC Chris Kawa

    @StudentScripter For painting the selection I simply meant this, without adjusting the area at all:

    if (selectionModel()->isSelected(index)) {
       painter->fillRect(options.rect, QColor(173, 216, 230));
    }
    

    The widget (or the picture of it) in the item is displayed "above" the row, so it covers whatever the view painted for the row. If you'd like to go with the approach that only view paints selection then make sure that the widget (and the picture of it) has transparent background, so the underlying row is visible.

    S Online
    S Online
    StudentScripter
    wrote on last edited by
    #10

    @Chris-Kawa Yes thank you very much, i have done this now. Sadly and i don't know why, suddenly the intendation space isn't painted highlighted anymore:

    69fd9d57-0953-423b-bd21-0bc3195f3162-image.png

    void ViewLayerList::drawRow(QPainter *painter, const QStyleOptionViewItem &options, const QModelIndex &index) const
    {
       
        if (selectionModel()->isSelected(index)) {
        
        // Zeichnen Sie den Einzugsbereich (Indentation) mit Ihrer gewünschten Farbe
                QRect indentRect = visualRect(index);
                painter->fillRect(indentRect, QColor(173, 216, 230)); // "lightblue" Hervorhebungsfarbe*/
                
           
                
            
        }
    
        
         QTreeView::drawRow(painter, options, index); // Rufen Sie die Basisimplementierung auf, um die Standardzeichnung durchzuführen
    }
    
    

    it only works when adding the stylesheet solution to the treeview constructor, but i guess thats not what you meant:
    d18bde15-6933-492c-94db-c7d1549a60d2-image.png

    
    ViewLayerList::ViewLayerList(CustomGraphicsScene *scene, QWidget *parent)
        : QTreeView{parent}, scene_durchgereicht(scene)
    {
    
    // Setzen Sie das Stylesheet für den ausgewählten Zweig hier
        setStyleSheet("QTreeView::item:selected { background-color: lightblue; }");
    
    Chris KawaC 1 Reply Last reply
    0
    • S StudentScripter

      @Chris-Kawa Yes thank you very much, i have done this now. Sadly and i don't know why, suddenly the intendation space isn't painted highlighted anymore:

      69fd9d57-0953-423b-bd21-0bc3195f3162-image.png

      void ViewLayerList::drawRow(QPainter *painter, const QStyleOptionViewItem &options, const QModelIndex &index) const
      {
         
          if (selectionModel()->isSelected(index)) {
          
          // Zeichnen Sie den Einzugsbereich (Indentation) mit Ihrer gewünschten Farbe
                  QRect indentRect = visualRect(index);
                  painter->fillRect(indentRect, QColor(173, 216, 230)); // "lightblue" Hervorhebungsfarbe*/
                  
             
                  
              
          }
      
          
           QTreeView::drawRow(painter, options, index); // Rufen Sie die Basisimplementierung auf, um die Standardzeichnung durchzuführen
      }
      
      

      it only works when adding the stylesheet solution to the treeview constructor, but i guess thats not what you meant:
      d18bde15-6933-492c-94db-c7d1549a60d2-image.png

      
      ViewLayerList::ViewLayerList(CustomGraphicsScene *scene, QWidget *parent)
          : QTreeView{parent}, scene_durchgereicht(scene)
      {
      
      // Setzen Sie das Stylesheet für den ausgewählten Zweig hier
          setStyleSheet("QTreeView::item:selected { background-color: lightblue; }");
      
      Chris KawaC Offline
      Chris KawaC Offline
      Chris Kawa
      Lifetime Qt Champion
      wrote on last edited by
      #11

      @StudentScripter You're painting the wrong rectangle. Look at the code you posted and the one I did.

      S 1 Reply Last reply
      0
      • Chris KawaC Chris Kawa

        @StudentScripter You're painting the wrong rectangle. Look at the code you posted and the one I did.

        S Online
        S Online
        StudentScripter
        wrote on last edited by
        #12

        @Chris-Kawa Well, what a pitty, i indeed missed this. 😅 Thanks for your patience with me.

        May can i bother you again with another question related to this widget:
        I know about the setEditTriggers() but still when i set:

        setEditTriggers(QAbstractItemView::AllEditTriggers);
        

        cc121d67-2642-4c81-99ea-903f2190f42d-image.png
        Like it is now i have to doubleclick to create the editor and click a third time again to focus the line edit, thats pretty user unfriendly and wierd.

        Instead i want to double click onto the position where my lineedit is to select and focus/write into it.
        Also with the checkbox i would like to click on it and it should be ticked instantly on the first click. I guess i have to implement this logic somehow myself into QTreeView mousePressEvent, but i have no good idea on how to do that? Maybe with openpersitentEditor and than somehow set focus to the widget depending on what is under the clicked mouse position?

        Also can't say it often enough thanks for your help and the help of the others. I really doubt i would have gotten this far without. :D Also have to admit this photoeditor clone is my first big project that i set myself, of course only with basic features, but i really want to tweak them to be useful in a real world scenario.

        1 Reply Last reply
        0
        • SGaistS Offline
          SGaistS Offline
          SGaist
          Lifetime Qt Champion
          wrote on last edited by
          #13

          No, check the editorEvent function.

          Interested in AI ? www.idiap.ch
          Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

          S 2 Replies Last reply
          0
          • SGaistS SGaist

            No, check the editorEvent function.

            S Online
            S Online
            StudentScripter
            wrote on last edited by
            #14

            @SGaist thanks that seems the right direction, however i cant get the mouse position to be into the checkbox. Visually it looks like i click into the check box but my debug says its not clicking into the checkbox. I tried mapping to global in order to align both positions but i had no luck with this:

            
            
            bool ViewLayerItemDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index)
            {
                if(event->type() == QEvent::MouseButtonPress)
                {
                    QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
                    
                    QWidget *parentWidget = qobject_cast<QWidget*>(parent());
                    if(parentWidget)
                    {
                        
                        QWidget *editor = createEditor(parentWidget, option, index);
                       
                            LineEditCheckBoxWidget *widget = qobject_cast<LineEditCheckBoxWidget*>(editor);
                            if(widget)
                            {   
                           
                                
                            QPoint mousePosition = widget->checkBox->mapFromGlobal(mouseEvent->globalPos());
                            QRect checkboxGeometry = widget->checkBox->geometry();
                            QRect globalCheckboxGeometry;
                            globalCheckboxGeometry.setTopLeft(widget->checkBox->mapToGlobal(checkboxGeometry.topLeft()));
                            globalCheckboxGeometry.setBottomRight(widget->checkBox->mapToGlobal(checkboxGeometry.bottomRight()));
            
            
                               qDebug() << "Checkboxdata: " << globalCheckboxGeometry;
            
                               qDebug() << "MousePostion: " << mousePosition;
            
            
                                /*
                                 //IGNORE: later i want to check here if the positon 
                                 //is the same in order to perform further actions
                                bool checked = widget->checkBox->isChecked();
                                widget->checkBox->setChecked(!checked);
                                setModelData(editor, model, index);
                                return true; 
                                */
                            }
                    }
            
                  
               
                }
                
                return QStyledItemDelegate::editorEvent(event, model, option, index);
            }
            
            

            I get for example:
            Checkboxdata: QRect(1095,302 100x30)
            MousePostion: QPoint(215,21)

            1 Reply Last reply
            0
            • SGaistS SGaist

              No, check the editorEvent function.

              S Online
              S Online
              StudentScripter
              wrote on last edited by
              #15

              @SGaist Bumping this up. Guess you are busy but may you have a second the next days or so to may answer my last question. Sorry for the interuption. :)

              Chris KawaC 1 Reply Last reply
              0
              • S StudentScripter

                @SGaist Bumping this up. Guess you are busy but may you have a second the next days or so to may answer my last question. Sorry for the interuption. :)

                Chris KawaC Offline
                Chris KawaC Offline
                Chris Kawa
                Lifetime Qt Champion
                wrote on last edited by
                #16

                @StudentScripter There's multiple problems in your code.

                First this is wrong: QWidget *parentWidget = qobject_cast<QWidget*>(parent());.
                A delegate can be shared between multiple views and there's no requirement that any of those views was a parent of that delegate. There's also no requirement that the parent of the delegate is a QWidget or that the delegate even has any parent. In case of sharing delegate between views the parent might not even be the widget the event occurred in.
                The proper way to retrieve the widget the event happened in is through option.widget parameter.

                Next, you should not call createEditor. This is a callback method that the view calls when editing of an item is requested via view->edit(index). The way you have it you create a new widget every time an item is clicked. you never show or delete that widget, but you do give it a parent, so after 1000 clicks you have 1000 instances of that widget created, invisible and living until you the view is destroyed.

                The event gives you press position in view's viewport coordinates. To convert it to item's coordinates you can get the item coords in the viewport and do the math e.g.

                if(event->type() == QEvent::MouseButtonPress)
                {
                    QMouseEvent* mouseEvt = static_cast<QMouseEvent*>(event);
                    
                    if ( const QTreeView* view = qobject_cast<const QTreeView*>(option.widget))
                    {
                        const QRect itemRect = view->visualRect(index);
                        QPointF itemPos = mouseEvt->position() - itemRect.topLeft(); // press position in item's coords
                

                You should not create and destroy a widget every time you want to paint or check a position in it. That's an enormous overhead that defeats the whole point of delegates.

                Calculate where your checkbox is without instantiating a widget. For example you can assume it's always in a right aligned square of your item, so you can calculate its position from the item dimensions.

                When painting the item you also shouldn't instantiate a widget and take a screenshot of it like you're doing now. Use style()->drawControl(...) to draw a picture of a checkbox or line edit without actually instantiating them. That's the idea of delegates - provide a lightweight proxy for the items and only instantiating an actual widget when editing of an item starts.

                When painting an item don't load any resources e.g. QPixmap iconPixmap("://resource/quick.png"); in the paint event. That is reading from disk and can stall and make your app unresponsive if you have many items or slow disk access. Load the image once e.g. in the delegate's constructor and store it in a class member. Also if you're doing any transformation on it, like scaled(...) do it once on load and store and reuse the result. Keep in mind that the paining is called every time a user moves a mouse over an item, so it's potentially hundreds of events a second. Painting should be as speedy as possible.

                Don't use stylesheets to draw a colored rectangle. It's like shooting a fly with a canon. Just use a QPainter and paint a rectangle.

                S 3 Replies Last reply
                3
                • Chris KawaC Chris Kawa

                  @StudentScripter There's multiple problems in your code.

                  First this is wrong: QWidget *parentWidget = qobject_cast<QWidget*>(parent());.
                  A delegate can be shared between multiple views and there's no requirement that any of those views was a parent of that delegate. There's also no requirement that the parent of the delegate is a QWidget or that the delegate even has any parent. In case of sharing delegate between views the parent might not even be the widget the event occurred in.
                  The proper way to retrieve the widget the event happened in is through option.widget parameter.

                  Next, you should not call createEditor. This is a callback method that the view calls when editing of an item is requested via view->edit(index). The way you have it you create a new widget every time an item is clicked. you never show or delete that widget, but you do give it a parent, so after 1000 clicks you have 1000 instances of that widget created, invisible and living until you the view is destroyed.

                  The event gives you press position in view's viewport coordinates. To convert it to item's coordinates you can get the item coords in the viewport and do the math e.g.

                  if(event->type() == QEvent::MouseButtonPress)
                  {
                      QMouseEvent* mouseEvt = static_cast<QMouseEvent*>(event);
                      
                      if ( const QTreeView* view = qobject_cast<const QTreeView*>(option.widget))
                      {
                          const QRect itemRect = view->visualRect(index);
                          QPointF itemPos = mouseEvt->position() - itemRect.topLeft(); // press position in item's coords
                  

                  You should not create and destroy a widget every time you want to paint or check a position in it. That's an enormous overhead that defeats the whole point of delegates.

                  Calculate where your checkbox is without instantiating a widget. For example you can assume it's always in a right aligned square of your item, so you can calculate its position from the item dimensions.

                  When painting the item you also shouldn't instantiate a widget and take a screenshot of it like you're doing now. Use style()->drawControl(...) to draw a picture of a checkbox or line edit without actually instantiating them. That's the idea of delegates - provide a lightweight proxy for the items and only instantiating an actual widget when editing of an item starts.

                  When painting an item don't load any resources e.g. QPixmap iconPixmap("://resource/quick.png"); in the paint event. That is reading from disk and can stall and make your app unresponsive if you have many items or slow disk access. Load the image once e.g. in the delegate's constructor and store it in a class member. Also if you're doing any transformation on it, like scaled(...) do it once on load and store and reuse the result. Keep in mind that the paining is called every time a user moves a mouse over an item, so it's potentially hundreds of events a second. Painting should be as speedy as possible.

                  Don't use stylesheets to draw a colored rectangle. It's like shooting a fly with a canon. Just use a QPainter and paint a rectangle.

                  S Online
                  S Online
                  StudentScripter
                  wrote on last edited by StudentScripter
                  #17
                  This post is deleted!
                  1 Reply Last reply
                  0
                  • Chris KawaC Chris Kawa

                    @StudentScripter There's multiple problems in your code.

                    First this is wrong: QWidget *parentWidget = qobject_cast<QWidget*>(parent());.
                    A delegate can be shared between multiple views and there's no requirement that any of those views was a parent of that delegate. There's also no requirement that the parent of the delegate is a QWidget or that the delegate even has any parent. In case of sharing delegate between views the parent might not even be the widget the event occurred in.
                    The proper way to retrieve the widget the event happened in is through option.widget parameter.

                    Next, you should not call createEditor. This is a callback method that the view calls when editing of an item is requested via view->edit(index). The way you have it you create a new widget every time an item is clicked. you never show or delete that widget, but you do give it a parent, so after 1000 clicks you have 1000 instances of that widget created, invisible and living until you the view is destroyed.

                    The event gives you press position in view's viewport coordinates. To convert it to item's coordinates you can get the item coords in the viewport and do the math e.g.

                    if(event->type() == QEvent::MouseButtonPress)
                    {
                        QMouseEvent* mouseEvt = static_cast<QMouseEvent*>(event);
                        
                        if ( const QTreeView* view = qobject_cast<const QTreeView*>(option.widget))
                        {
                            const QRect itemRect = view->visualRect(index);
                            QPointF itemPos = mouseEvt->position() - itemRect.topLeft(); // press position in item's coords
                    

                    You should not create and destroy a widget every time you want to paint or check a position in it. That's an enormous overhead that defeats the whole point of delegates.

                    Calculate where your checkbox is without instantiating a widget. For example you can assume it's always in a right aligned square of your item, so you can calculate its position from the item dimensions.

                    When painting the item you also shouldn't instantiate a widget and take a screenshot of it like you're doing now. Use style()->drawControl(...) to draw a picture of a checkbox or line edit without actually instantiating them. That's the idea of delegates - provide a lightweight proxy for the items and only instantiating an actual widget when editing of an item starts.

                    When painting an item don't load any resources e.g. QPixmap iconPixmap("://resource/quick.png"); in the paint event. That is reading from disk and can stall and make your app unresponsive if you have many items or slow disk access. Load the image once e.g. in the delegate's constructor and store it in a class member. Also if you're doing any transformation on it, like scaled(...) do it once on load and store and reuse the result. Keep in mind that the paining is called every time a user moves a mouse over an item, so it's potentially hundreds of events a second. Painting should be as speedy as possible.

                    Don't use stylesheets to draw a colored rectangle. It's like shooting a fly with a canon. Just use a QPainter and paint a rectangle.

                    S Online
                    S Online
                    StudentScripter
                    wrote on last edited by
                    #18

                    @Chris-Kawa thank you very much. That definitely helped a lot. :)

                    I have done the manual positioning now, so all delegates line up with the actual widget. :)

                    
                    void ViewLayerItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
                    {
                        
                        
                        QStyleOptionViewItem opt = option;
                        initStyleOption(&opt, index);
                    
                    
                        // Überprüfen Sie, ob der aktuelle Index bearbeitet wird
                            if (index == currentlyEditedIndex) {
                               
                                return;
                            }
                    
                    
                    
                        // Setzen Sie die Werte der SpinBox und CheckBox basierend auf den Modellwerten
                        QString lineEditvalue = index.model()->data(index, Qt::EditRole).toString();
                        bool checkBoxValue = index.model()->data(index, Qt::CheckStateRole).toBool();
                    
                        // Laden Sie das Icon und skalieren Sie es
                        QPixmap iconPixmap("://resource/quick.png"); // Ersetzen Sie dies durch den Pfad zu Ihrer Icon-Datei
                        QPixmap scaledPixmap = iconPixmap.scaled(32, 32, Qt::KeepAspectRatio, Qt::SmoothTransformation);
                        
                        // Berechnen Sie die Position für das Icon
                        int centerY = option.rect.top() + option.rect.height() / 2;
                        int iconY = centerY - scaledPixmap.height() / 2;
                        QPoint iconPos = QPoint(option.rect.left() + 10, iconY);
                        
                        // Zeichnen Sie das Pixmap mit dem QPainter
                        painter->drawPixmap(iconPos, scaledPixmap);
                    
                    
                        // Berechnen Sie die Position und Größe für das LineEdit
                        QRect lineEditRect = option.rect;
                        lineEditRect.setLeft(iconPos.x() + scaledPixmap.width() + 10); // Adjust as needed
                        lineEditRect.setRight(option.rect.right() - 10); // Adjust as needed
                    
                        // Erstellen Sie ein QStyleOptionFrame für das LineEdit
                        QStyleOptionFrame lineEditOption;
                        lineEditOption.lineWidth = 1; // Setzen Sie die Liniendicke auf 1
                        lineEditOption.rect = lineEditRect;
                    
                        // Zeichnen Sie das LineEdit
                        QApplication::style()->drawControl(QStyle::CE_ShapedFrame, &lineEditOption, painter);
                    
                        // Zeichnen Sie den Text des LineEdits
                        painter->drawText(lineEditOption.rect.adjusted(2,0,0,0), Qt::AlignLeft | Qt::AlignVCenter, lineEditvalue);
                    
                        // Berechnen Sie die Position und Größe für die CheckBox
                        QRect checkBoxRect = option.rect;
                        checkBoxRect.setLeft(lineEditRect.right() - 22); // Adjust as needed
                        checkBoxRect.setRight(option.rect.right() - 10); // Adjust as needed
                    
                        // Erstellen Sie ein QStyleOptionButton für die CheckBox
                        QStyleOptionButton checkBoxOption;
                        checkBoxOption.state = checkBoxValue ? QStyle::State_On : QStyle::State_Off;
                        checkBoxOption.rect = checkBoxRect;
                    
                        // Zeichnen Sie die CheckBox
                        QApplication::style()->drawControl(QStyle::CE_CheckBox, &checkBoxOption, painter);
                    }
                    
                    1 Reply Last reply
                    0
                    • Chris KawaC Chris Kawa

                      @StudentScripter There's multiple problems in your code.

                      First this is wrong: QWidget *parentWidget = qobject_cast<QWidget*>(parent());.
                      A delegate can be shared between multiple views and there's no requirement that any of those views was a parent of that delegate. There's also no requirement that the parent of the delegate is a QWidget or that the delegate even has any parent. In case of sharing delegate between views the parent might not even be the widget the event occurred in.
                      The proper way to retrieve the widget the event happened in is through option.widget parameter.

                      Next, you should not call createEditor. This is a callback method that the view calls when editing of an item is requested via view->edit(index). The way you have it you create a new widget every time an item is clicked. you never show or delete that widget, but you do give it a parent, so after 1000 clicks you have 1000 instances of that widget created, invisible and living until you the view is destroyed.

                      The event gives you press position in view's viewport coordinates. To convert it to item's coordinates you can get the item coords in the viewport and do the math e.g.

                      if(event->type() == QEvent::MouseButtonPress)
                      {
                          QMouseEvent* mouseEvt = static_cast<QMouseEvent*>(event);
                          
                          if ( const QTreeView* view = qobject_cast<const QTreeView*>(option.widget))
                          {
                              const QRect itemRect = view->visualRect(index);
                              QPointF itemPos = mouseEvt->position() - itemRect.topLeft(); // press position in item's coords
                      

                      You should not create and destroy a widget every time you want to paint or check a position in it. That's an enormous overhead that defeats the whole point of delegates.

                      Calculate where your checkbox is without instantiating a widget. For example you can assume it's always in a right aligned square of your item, so you can calculate its position from the item dimensions.

                      When painting the item you also shouldn't instantiate a widget and take a screenshot of it like you're doing now. Use style()->drawControl(...) to draw a picture of a checkbox or line edit without actually instantiating them. That's the idea of delegates - provide a lightweight proxy for the items and only instantiating an actual widget when editing of an item starts.

                      When painting an item don't load any resources e.g. QPixmap iconPixmap("://resource/quick.png"); in the paint event. That is reading from disk and can stall and make your app unresponsive if you have many items or slow disk access. Load the image once e.g. in the delegate's constructor and store it in a class member. Also if you're doing any transformation on it, like scaled(...) do it once on load and store and reuse the result. Keep in mind that the paining is called every time a user moves a mouse over an item, so it's potentially hundreds of events a second. Painting should be as speedy as possible.

                      Don't use stylesheets to draw a colored rectangle. It's like shooting a fly with a canon. Just use a QPainter and paint a rectangle.

                      S Online
                      S Online
                      StudentScripter
                      wrote on last edited by StudentScripter
                      #19
                      This post is deleted!
                      1 Reply Last reply
                      0
                      • S StudentScripter has marked this topic as solved on

                      • Login

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