[Solved] - QTableView - custom selection indicator (QRubberBand)
-
Hi,
AFAIK no there's not. You could use e.g. a QRubberBand for the additional "selection" effect
Hope it helps
-
Thanks SGaist, I would have never found this usefull class myself!
With this I think I can achieve what I want!So far I got a working example drawing a QRubberBand on every row of my QtableView when I click a "repeat button". It felt a bit hacky to get the QTableView total columns width, maybe i'm missing a function in QTableView? I'm thinking using this code instead of a complex delegate I did to get mouse hover row effect in a QtableView, you guys could implements this easily with a QRubberband :) and add a QTableView row mouseOver effect with stylesheet?
I will create a custom QRubberBand with a QLabel at the middle right position, and write the number I want there.
Here is the code:
@WorkoutCreator::WorkoutCreator(QWidget *parent) : QWidget(parent), ui(new Ui::WorkoutCreator)
{
ui->setupUi(this);rubberBand = new QRubberBand(QRubberBand::Rectangle, ui->tableView->viewport()); totalWidthColumms = 0; for (int i=0; i<ui->tableView->colorCount(); i++) { totalWidthColumms+= ui->tableView->columnWidth(i); }
}
void WorkoutCreator::on_pushButton_repeat_clicked()
{
QModelIndexList lstIndex = ui->tableView->selectionModel()->selectedRows();if (lstIndex.size() < 1) { return; } QRect recFirstSelection(ui->tableView->visualRect(lstIndex.at(0))); QRect recLastSelection(ui->tableView->visualRect(lstIndex.at(lstIndex.size()-1))); QRect rectCompleteSelection(recFirstSelection.topLeft(), recLastSelection.bottomRight() ); rectCompleteSelection.setWidth(totalWidthColumms); rubberBand->move(rectCompleteSelection.topLeft()); rubberBand->resize(rectCompleteSelection.size());
// rubberBand->setGeometry(rectCompleteSelection);
rubberBand->show();
}
@ -
I'm struggling to add text in the QRubberBand paintEvent, I can add a frame around the selection, but for some reason the drawText is not displaying., investigating..
@void myRubberBand::paintEvent(QPaintEvent *event)
{
QRect rec1(event->rect());qDebug() << "-----------------\nRECT ORIGINAL" << rec1; // QStylePainter painter(this); // QStyleOptionFocusRect option; // option.initFrom(this); QStylePainter painter(this); QStyleOptionRubberBand option; initStyleOption(&option); QPen pen(Qt::red, 2); pen.setStyle(Qt::SolidLine); // painter.setBrush(Qt::transparent); painter.setPen(pen); /// Find the middle right area // QPoint bottomRight(rec1.bottomRight()); // bottomRight.setX(bottomRight.x() + 70); // QRect rectText(rec1.topRight(), bottomRight); // qDebug() << "RECT NEW TEXT" << rectText; painter.drawText(rec1.topRight(), "abc"); painter.drawRect(rec1); // painter.end();
}@
-
May be related to this warning: I get a bunch when I launch my program, haven't found the cause :
@QPaintDevice::metrics: Device has no metric information@
[Edit: this error what due to bad code here :
for (int i=0; i<ui->tableView->colorCount(); i++)
I simply misread colorCount for columnCount -
Aren't you drawing the rect above the text ?
The metrics error is indeed surprising, what OS are you running on ?
-
Hi,
I am using Windows 8.1 x64 Enterprise
I want to draw 2 object ;
1- Rectangle highlighting with my row(s) selections
2- Text showing a number at the right side of this rectangle (this one is not showing even if I comment the first objectYou can see my current interface here :
https://www.dropbox.com/s/puzz9jwx09fwyp9/interface111.png
What is missing is the black box where I need to draw textI updated my current QRubberBand paintEvent up here
-
Since you are using the event rect which probably is the current QRubberBand geometry, painting on the top right corner makes you paint outside your widget
-
You are right, the text is not painting because it is outside of the rectangle, I tried with this code and I can paint part of the text. Now is there a possibility to draw outside of the event.rect ? If not, I'm thinking maybe add a dummy column at the far right that will serve as place to draw the text. not really clean but could work...
Current result with code below :
https://www.dropbox.com/s/kumzp59opq9vuhg/currentREsult.png@void myRubberBand::paintEvent(QPaintEvent *event)
{
QRect rec1(event->rect());// qDebug() << "-----------------\nRECT ORIGINAL" << rec1;
QStylePainter painter(this);
QStyleOptionRubberBand option;
initStyleOption(&option);QPen pen(Qt::red, 2); pen.setStyle(Qt::SolidLine); // painter.setBrush(Qt::transparent); painter.setPen(pen); /// Find the middle right area of rectangle QPointF pointMiddleRight(rec1.center()); pointMiddleRight.setX(pointMiddleRight.x() + 280); painter.drawText(pointMiddleRight, "abc"); painter.drawRect(rec1);
}@
-
No there's not.
Another way to do it, is to add a transparent (mouse event and color transparent) widget over your table view and do all the painting there.
Or a second rubber band for the text part that you keep in sync with the original
Or make the rubber band larger to also have the space to write the text.
-
All good solutions, working perfectly!, Qt is very flexible
Thanks again SGaist -
Hey it's me again, I know I dragged this post a lot maybe I should start a new :)
Just wondering if something like this would be possible :
https://www.dropbox.com/s/xhczf5cg7pu8fxw/editableQRubberBand.png
(Editable QComboBox inside the QRubberBand)I know how to draw the text, but not sure the editable QcomboBox is possible? Painting possible, but finding where the signals of the QComboBox is coming from?, maybe giving a unique name to the QComboBox, just tell me if you foresee this as possible before I waste too much time, thanks!
-
Contextual menu ?
-
Contextual menu? This is a right click menu right? IMO this doesn't really feel natural for this function, maybe for removing, copying rows I will use this but for what I need I would like a separate widget that is always displaying inside the QTableView, one widget per Repeat box, like shown in the pic. Not sure it is possible, I can draw it inside the QRubberBand, but it would be static and not usable afterwards. Still trying to use my imagination but I have limited Qt experience :/
-
Yes and you have a point. Then you could have a QComboBox as member of your QRubberBand and handle its position yourself
-
Hi SGaist,
Finally I used the solution of a custom QWidget over the QTableView, I needed a layout and it's easier to manage:Here is the result interface :
https://www.dropbox.com/s/40osxyrdgxkuaf6/finalUi.pngI post the code in the next post if someone wants to do something similar
<Thread Solved for real now!>Thank you
-
RepeatWidget :
@RepeatWidget::RepeatWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::RepeatWidget)
{
ui->setupUi(this);id = 0; firstRow = 0; lastRow = 0; numberRepeat = 0;
}
void RepeatWidget::on_pushButton_delete_clicked()
{
emit deleteSignal(id);
}void RepeatWidget::on_comboBox_repeat_currentIndexChanged(const QString &arg1)
{
this->numberRepeat = arg1.toInt();
emit updateSignal(id);
}@ParentUiClass with QListView:
@WorkoutCreator::~WorkoutCreator()
{
delete ui;
qDeleteAll(lstRepeatWidget);
lstRepeatWidget.clear();
}WorkoutCreator::WorkoutCreator(QWidget *parent) : QWidget(parent), ui(new Ui::WorkoutCreator)
{
ui->setupUi(this);[...]
connect(ui->tableView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(tableViewSelectionChanged(QItemSelection,QItemSelection))); totalWidthColumms = 0; for (int i=0; i<ui->tableView->colorCount(); i++) { totalWidthColumms += ui->tableView->columnWidth(i); } ui->pushButton_delete->setEnabled(false); ui->pushButton_copy->setEnabled(false); ui->pushButton_repeat->setEnabled(false);
}
void WorkoutCreator::tableViewSelectionChanged(QItemSelection selected, QItemSelection deselected) {
/// default behavior = disabled ui->pushButton_delete->setEnabled(false); ui->pushButton_copy->setEnabled(false); ui->pushButton_repeat->setEnabled(false);
// QModelIndexList lstIndex = selected.indexes();
QModelIndexList lstIndex = ui->tableView->selectionModel()->selectedRows();
qDebug() << lstIndex.size();if (lstIndex.size() < 1) { return; } ui->pushButton_delete->setEnabled(true); ui->pushButton_copy->setEnabled(true); QModelIndex firstIndex = lstIndex.at(0); QModelIndex lastIndex = lstIndex.at(lstIndex.size()-1); /// --- Check All overlappign possibilities (see if we can enable repeat button) foreach (RepeatWidget *wid, lstRepeatWidget) { if (firstIndex.row() >= wid->firstRow && firstIndex.row() <= wid->lastRow ) { qDebug() << "1 REFUSE"; return; } if (lastIndex.row() <= wid->lastRow && lastIndex.row() >= wid->firstRow) { qDebug() << "2 REFUSE"; return; } if (firstIndex.row() < wid->firstRow && lastIndex.row() > wid->lastRow) { qDebug() << "2 REFUSE"; return; } } ui->pushButton_repeat->setEnabled(true);
}
void WorkoutCreator::on_pushButton_repeat_clicked()
{QModelIndexList lstIndex = ui->tableView->selectionModel()->selectedRows(); if (lstIndex.size() < 1) { return; } QModelIndex firstIndex = lstIndex.at(0); QModelIndex lastIndex = lstIndex.at(lstIndex.size()-1); QRect recFirstSelection(ui->tableView->visualRect(firstIndex)); QRect recLastSelection(ui->tableView->visualRect(lastIndex)); QRect rectCompleteSelection(recFirstSelection.topLeft(), recLastSelection.bottomRight() ); rectCompleteSelection.setWidth(totalWidthColumms); RepeatWidget *repeatWidget = new RepeatWidget(ui->tableView->viewport()); repeatWidget->id = idRepeatWidget; repeatWidget->firstRow = firstIndex.row(); repeatWidget->lastRow = lastIndex.row(); repeatWidget->move(rectCompleteSelection.topLeft()); repeatWidget->resize(rectCompleteSelection.size().width() + 190, rectCompleteSelection.size().height()); repeatWidget->show(); connect(repeatWidget, SIGNAL(deleteSignal(int)), this, SLOT(deletedRepeatWidget(int)) ); connect(repeatWidget, SIGNAL(updateSignal(int)), this, SLOT(updatedRepeatWidget(int)) ); /// Add widget to QList idRepeatWidget ++; lstRepeatWidget.append(repeatWidget);
}
void WorkoutCreator::on_pushButton_add_clicked()
{
intervalModel->insertRow(intervalModel->rowCount());
}void WorkoutCreator::on_pushButton_copy_clicked()
{QModelIndexList lstIndex = ui->tableView->selectionModel()->selectedRows(); foreach (QModelIndex index, lstIndex) { qDebug() << "going to copy row:" << index.row(); intervalModel->copyRows(index.row()); }
}
void WorkoutCreator::on_pushButton_delete_clicked()
{QModelIndexList lstIndex = ui->tableView->selectionModel()->selectedRows(); if (lstIndex.size() < 1) { return; } int startRow = lstIndex.at(0).row(); intervalModel->removeRows(startRow, lstIndex.size(), QModelIndex());
}
void WorkoutCreator::deletedRepeatWidget(int id) {
qDebug() << "deletedRepeatWidget" << id; RepeatWidget *widgetToDelete; /// Better way to delete without iterating over QList? Array worth it? small data set.. foreach (RepeatWidget *wid, lstRepeatWidget) { if (wid->id == id) { wid->setVisible(false); widgetToDelete = wid; break; } } lstRepeatWidget.removeOne(widgetToDelete);
}
void WorkoutCreator::updatedRepeatWidget(int id) {
qDebug() << "updatedRepeatWidget" << id; /// TODO: update graph with new values
}
@