Last empty column to fill QTableView - qt

In a QTableView I need the last (rightmost) column to be empty, expandable but not movable. The goal is that the table not to suddenly end (for I use alternate color for rows) or to ugly expand to the right. In QHeaderView there is a setFirstSectionMovable(bool); I need something similar for the last section, letting the rest of them movable. (in other words: Fill the rest of the table with an empty not movable column). Any clue how to acheive this?
I did override mousePressEvent() in a subclass of QHeaderView to skip the last section but it still can be moved by moving other column in its place and I don't know how to prevent this.

AMOQ:
Be HeaderView a subclass of QHeaderView and have
setSectionsMovable(true);
setStretchLastSection(true);
Treat the sectionMoved signal:
connect(this, &QHeaderView::sectionMoved, [this](int, int, int newVisual) {
if(newVisual == count()-1) { moveSection(newVisual, newVisual-1); }
});
Override mousePressEvent:
void HeaderView::mousePressEvent(QMouseEvent* event) {
int logicalIdx = logicalIndexAt(event->pos());
if(logicalIdx != count()-1) { QHeaderView::mousePressEvent(event); }
}

Related

Qt, How can I sort QTableWidget column if the cells are widgets

I have a QTableWidget, some columns are filled with text, some with numbers, these columns are fine to sort. But I also have a column with custom widgets, how should I enable sorting for these?
My first thought was of course to overload the '<' method on the QTableWidgetItem, but there are no QTableWidgetItem. So what would be the best way to solve this?
QTableWidget is not ideal for this case, consider using QTableView. Anyway, I will show you how to sort a QProgressBar widget in a QTableWidget.
As you already said, you can overload the <() operator for this case. Let's say you have a QProgressBar in column number 4. You have to overload the <() operator.
You have to subclass QProgressBar and QTableWidgetItem.
class CustomTableWidgetItem : public QProgressBar, public QTableWidgetItem
{
Q_OBJECT
public:
CustomTableWidgetItem( QWidget* parent ) : QProgressBar( parent )
{
}
CustomTableWidgetItem(const QString txt = QString("0"))
:QTableWidgetItem(txt)
{
}
bool operator <(const QTableWidgetItem& other) const
{
if(other.column() == 0 /* numeric cell */) {
return QTableWidgetItem::text().toInt() < other.text().toInt();
}
else if(other.column() == 4 /* progress bar */) {
const QProgressBar *p = dynamic_cast<const QProgressBar *>(&other);
if(p != 0) {
if(this->value() < p->value())
return true;
}
}
return false;
}
};
And then you can insert your QProgressBar like this in cell number 4.
Insert QProgressBar like this
ui->tableWidget->insertRow(ui->tableWidget->rowCount());
CustomTableWidgetItem *pgbar = new CustomTableWidgetItem(this);
ui->tableWidget->setCellWidget(ui->tableWidget->rowCount()-1, 4, pgbar);
ui->tableWidget->setItem(ui->tableWidget->rowCount()-1, 4, pgbar);
Let's say you want to have simple text at cell 1 use QTableWidgetItem.
ui->tableWidget->setItem(ui->tableWidget->rowCount()-1, 1, new QTableWidgetItem("Hello"));
If you want to sort numbers as well, for example in cell 0 use CustomTableWidgetItem, since we have implemented an if-statement as you can see above for sorting numbers and progressbar.
ui->tableWidget->setItem(ui->tableWidget->rowCount()-1, 0, new CustomTableWidgetItem(QString::number(ui->tableWidget->rowCount())));
You can use this approach with other widgets as well, just subclass the correct widget, but in general it's better to use QTableView (MVC approach).
Here is a screenshot with QTableWidget and QProgressBar sorting.

hide QLabel based on text-size

I would like to write a custom QLabel subclass with some more features for responsive design. In thisexample, I want to write a QLabel which scales the text based on the useable space. This is quite easy but also has some problems because of Qt-intern stuff. (I have to scale the text to 0.9 of the useable space, otherwise resizing the window / widget gets buggy)
Now I wan't to add a way to hide the label completely when the font size is bellow a specific threshold. However, this seems to be quite a complex task.
Here is what I have sofar in the classes resizeEvent(QResizeEvent *event) function.
Right now, my function only sets the text to "" when the size would be bellow the threshold.
void CustomLabel::resizeEvent (QResizeEvent * event ) {
if(autoFontResize) {
this->setSilentText(labelText); // just the normal setText function, I overwrote it for the subclass
QFont f = this->font();
int flags = Qt::TextDontClip|Qt::TextWordWrap;
QRect fontBoundRect = this->fontMetrics().boundingRect(this->rect(), flags, this->text());
float xFactor = (float)event->size().width() / (float)fontBoundRect.width();
float yFactor = (float)event->size().height() / (float)fontBoundRect.height();
float factor = xFactor < yFactor ? xFactor : yFactor;
f.setPointSizeF(f.pointSize()*factor*0.9); //
if(minimumFontSize != 0) { // 0 = no minimum Size for the font
if(f.pointSize() < minimumFontSize) {
if(hideFontOnMinimum) { // either Hide or set to the limit size
this->setSilentText(""); //replace text
} else {
f.setPointSizeF(minimumFontSize);
}
}
}
this->setFont(f);
}
QLabel::resizeEvent(event);
}
By the way, some parts of the code are found on stackoverflow, not mine. ;)
What I would like to do is to completely hide() the label. However the label doesn't know when It can show() again since the resizeEvent doesn't seem to be called after that.
Any ideas?
Thanks!
As you've noticed, if you call hide() on the widget it fails to receive a resize event. Since you're customising the class anyway, rather than calling hide(), you could just set a class variable to note that it's hidden and overload the paintEvent function, not to draw the widget if the variable is set: -
void CustomLabel::paintEvent(QPaintEvent * event)
{
if(m_hideOnMinimum)
return;
QLabel::paintEvent(event);
}
Note that by not painting the label, it will be hidden, but the user may still be able to interact with it, so you will need to disable it or overload keyboard / mouse events too.

how to avoid leading spaces in qtreeview with qfilesystem model header text?

I am using QFileSystem Model with QTreeView, and I am able to change the text by sub classing QFileSystem model like this...
QVariant customFileSystemModel::headerData(int section,
Qt::Orientation orientation,
int role) const
{
switch(role)
{
case(Qt::DisplayRole):
{
return QString("YourText");
}
case(Qt::TextAlignmentRole):
{
return Qt::AlignLeading;
}
default:{}
}
return QFileSystemModel::headerData(section, orientation,role);
}
but I am not able to remove the leading spaces in header text, could any one suggest how to do it.
I don't think it can be easily done. This is definitely not within the scope of your model. This is more a question of the selected style. Maybe something can be done with stylesheets. Or you can change the QHeaderView from your QTreeView. In worst case you have to subclass QHeaderView and override how the label is painted. But I don't think you have to go that far.

Can you set a QWidget to take up an entire column of a QTreeWidget?

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.
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.
Is the content being displayed in this custom tree column static or dynamic?
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.
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"
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.

delete qwidget from layout QT

I have a qwidget (we'll call it qwidget1) inside of a layout of an other qwidget (we'll call it qwidget2), I want to delete everything that is in the layout of qwidget2: I would like to clear the layout so there's nothing in it anymore ..
what I can do so far is remove completely the qwidget2 by doing:
void QCell::deleteMyChildren(){
delete this;
}
but it removes the qwidget2 itself.. that's not what I want.
Please help me remove the items that are inside the layout.
just loop inside the items in the layout and remove item from layout, then delete item :
void QCell::deleteMyChildren() {
while (count() > 0) {
QLayoutItem * item = takeAt(0);
delete item;
}
}

Resources