3

I have a custom QTreeWidget subclass that I'm using to display track names/etc. in my MIDI editor project (https://github.com/waddlesplash/ragingmidi). I'd like to add another column to this tree widget, but with one widget taking up the whole column and not per-item widgets.

Is this possible or will I have to figure out some other solution?

EDIT: I'm trying to accomplish something like this: http://www.anvilstudio.com/compose.jpg - see the last "column" in the header view (3rd after "L/R Balance") showing all the lines/notes (which is entirely custom, and written in VB.NET and closed-source anyway).

EDIT 2: You can't see it, but the last column scrolls without the other columns scrolling in the above picture. In their method, you have to scroll using the mouse. I want a scrollbar.

waddlesplash
  • 743
  • 1
  • 6
  • 25
  • Is there any reason why you can't just put the QtreeWidget to the left of a QWidget in a QHBoxLayout ? – Sir Digby Chicken Caesar Mar 15 '13 at 02:11
  • I need the QWidget to be aligned with the QTreeWidget so that I can draw my own rows for the column. So scrolling is an issue here. – waddlesplash Mar 15 '13 at 13:43
  • 1
    Out of curiousity: Why do you need a single widget instead of per-row widgets that would maybe communicate? – Tilman Vogel Mar 15 '13 at 18:42
  • I'm also curious why you chose this solution? I mean, one widget won't look good if your treewidget will be tall and also it is not good choice if it makes so much troubles. – Blood Mar 15 '13 at 18:53
  • Why does it have to be a single widget, instead of one custom widget per row? It's not clear from your description. – jmk Mar 16 '13 at 03:26
  • Because you can't see it, but the last column scrolls **without the rest of the columns scrolling**. How do you do that and have the column stay in sync? – waddlesplash Mar 16 '13 at 15:34

3 Answers3

2

Looking at the Qt documentation, there seems to be a few options to accomplish this, however there are a few important factors to address before you can decide what approach best suits your needs.

  1. Is the content being displayed in this custom tree column static or dynamic?
  2. Is there a one to one mapping of rows from your QTreeWidget to your custom tree column?

If your custom tree column content IS static and there IS a one to one mapping of rows , use of the QTreeWidget::setItemWidget ( QTreeWidgetItem * item, int column, QWidget * widget ) function should suffice.

However, if the content of your custom tree column is dynamic OR there is not a one to one mapping of rows, this will require a more complex approach.

As described in the documentation for QTreeWidget; "If you want to display custom dynamic content or implement a custom editor widget, use QTreeView and subclass QItemDelegate. "

QItemDelegate, and its sub classes, perform all drawing facilities for items inserted into Qt item views (like QTreeView, QListView, QTableView, etc..). This essentially allows you to control ALL drawing operations for any item inserted into a QTreeView class, letting you draw dynamic content in addition to being able to extend content across multiple rows.

Having implemented a similar approach for a QListWidget, I recommend using QStyledItemDelegate in lieu of QItemDelegate as it allows you to more easily integrate this widget with your application's style layout. As you did not detail the exact use of this custom QWidget, you also might need the additional facilities provided by QItemEditorCreator, QItemEditorCreatorBase and QItemEditorFactory. I would post the similar widget I developed here if I could, but sadly it is part of a proprietary software suite.

Sir Digby Chicken Caesar
  • 3,063
  • 1
  • 23
  • 31
  • It's dynamic content. And I'm already using setItemWidget() to add sliders. See https://github.com/waddlesplash/ragingmidi/blob/master/src/Gui/Widgets/TracksEdit.cpp#L66 for all the code as-is, without the column I want to add. – waddlesplash Mar 15 '13 at 21:31
0

This is not completely pretty, because it got it's problems when the custom widget is in the right-most column and the column is made narrow, but it's a start:

#include <QtGui>

class TreeWidget : public QTreeWidget
{
    Q_OBJECT
public:
    TreeWidget();

    QRect columnRect(int column) const;

private slots:
    void repositionColumnWidget();

private:
    QPushButton * mColumnWidget;
};

TreeWidget::TreeWidget()
    : mColumnWidget(new QPushButton("Custom Column Button", viewport()))
{
    const int COLUMN_COUNT = 6;
    setColumnCount(COLUMN_COUNT);
    for (int row = 0; row < 400; ++row)
    {
        QStringList columns;
        for (int column = 0; column < COLUMN_COUNT; ++column)
        {
            columns << QString("row %1, column %2").arg(row + 1).arg(column + 1);
        }
        addTopLevelItem(new QTreeWidgetItem(columns));
    }
    for (int column = 0; column < COLUMN_COUNT; ++column)
    {
        resizeColumnToContents(column);
    }
    repositionColumnWidget();
    mColumnWidget->show();
    connect(header(), SIGNAL(sectionResized(int,int,int)), this, SLOT(repositionColumnWidget()));
    connect(header(), SIGNAL(sectionMoved(int,int,int)), this, SLOT(repositionColumnWidget()));
    connect(verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(repositionColumnWidget()));
    connect(horizontalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(repositionColumnWidget()));
}

QRect TreeWidget::columnRect(int column) const
{
    int itemCount = topLevelItemCount();
    if (!itemCount)
    {
        return QRect();
    }
    int columnX = header()->sectionViewportPosition(column);
    int columnY = visualItemRect(topLevelItem(0)).top();
    int columnWidth = header()->sectionSize(column);
    int columnHeight = visualItemRect(topLevelItem(itemCount-1)).bottom() - columnY + 1;

    return QRect(columnX, columnY, columnWidth, columnHeight);
}

void TreeWidget::repositionColumnWidget()
{
    mColumnWidget->setGeometry(columnRect(3));
}

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    a.setQuitOnLastWindowClosed(true);

    TreeWidget treeWidget;

    treeWidget.resize(800, 600);
    treeWidget.show();

    return a.exec();
}

#include "main.moc"
Tilman Vogel
  • 9,337
  • 4
  • 33
  • 32
0

The though that has come to my mind after a week is to hijack the H-scrollbar for the QTreeWidget, and then make that scrollbar scroll only the final column. Because right now, all the columns fit when the window is 620x670px, and who has a screen that small anymore?

Unless anyone has a better solution or objections as to why this is a bad idea, this is the way I'm going to do it.

waddlesplash
  • 743
  • 1
  • 6
  • 25
  • Honestly, I don't think, this in fact answers your original question. And I still think, I provided an acceptable solution that works very well with Qt 4.8.4 and should be easy to port to Qt 5. I consider this part your job. I will think twice to answer your questions next time. Sorry. – Tilman Vogel Mar 30 '13 at 19:38
  • I tried your solution, and pasted the error. I checked that there was a "main.moc" (which I generated using `moc main.cpp -o main.moc`) and then used `g++` 4.7.2 to attempt to compile it. My Qt is not broken; I've ported other apps (including mine) to Qt5 and none gave me that problem. I thought of this solution that seems better than yours to me. If you see an actual flaw, please tell me, but I don't. – waddlesplash Mar 31 '13 at 00:02
  • How does hijacking the horizontal scroll bar help with displaying a custom widget across all rows of a column? Also, if you have a problem with that `main.moc` stuff, just split the example app into a `h` and a `cpp` file as for any other Qt based class. And as for your compile error: That `connect()` method is inherited from QObject and is used all across Qt based apps. So, there must be something completely wrong in the way you are compiling here. I am sorry I cannot tell you what it is from my crystal ball. – Tilman Vogel Apr 02 '13 at 20:00
  • Hijacking the scrollbar helps because I can then have per-row widgets in the last column *but they all scroll at the same time*. – waddlesplash Apr 02 '13 at 23:08