Moving complete rows in a QTableView - qt

I have a tab which contains a QtableView and few buttons like "MOVE UP" and "MOVE DOWN". So on "MOVE UP" button press I have to move the entire row of QTableView one step up and the bring the adjacent one, one step down. I want to achieve this without creating complete model again since it may take time to construct the whole model again. Instead I just want to MOVE UP the selected row in the view.
Please let me know the simplest way to achieve this.
Thanks in advance

You don't need any knowledge of the model to do any of this, and most of the commenters are quite mistaken with any mention of the model at all.
You simply need to obtain the view's verticalHeader, and do a section swap of the item you want with the one above it. You might need to tell the verticalHeader to allow for reordering setMovable(true)
One of the main tenants QT and ModelView programming is to understand and appreciate the separation of manipulating a model from manipulating a view of the model.

I made the move up and down functions and this is what my .cpp contains
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
model = new QStandardItemModel(4,2,this);
for(int row = 0; row < 4; row++)
{
for(int col = 0; col < 2; col++)
{
QModelIndex index
= model->index(row,col,QModelIndex());
// 0 for all data
model->setData(index,row);
}
}
ui->tableView->setModel(model);
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_upPushButton_clicked()
{
if(ui->tableView->currentIndex().row()<=0)
{
return;
}
QList<QStandardItem *> list = model->takeRow(ui->tableView->currentIndex().row());
model->insertRow(ui->tableView->currentIndex().row(),list);
}
void Widget::on_downPushButton_clicked()
{
int selectedRow = ui->tableView->currentIndex().row() ;
if(selectedRow == ui->tableView->model()->rowCount()-1)
{
return;
}
QList<QStandardItem *> list = model->takeRow(ui->tableView->currentIndex().row());
model->insertRow(selectedRow+1,list);
}
This is what the ui looks like

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.

(Qt) Rendering scene, different items in the same relative positions

I have a QSqlTableModel model that contains my data.
I have made a QGraphicsScene scene and a QGraphicsView view so the user can move around same myQGraphicsTextItem text items until the desired position.
Something like this:
myQWidget::myQWidget()
{
//these are member of my class
chequeScene = new QGraphicsScene();
chequeView = new QGraphicsView();
model = new QSQLTableModel();
//populate model, inialize things here...
//add predefined items to the scene
setScene();
}
there's a button to show the view and move the textitems of scene. It works well.
there's a button that calls the slot print that belongs to the class. It configures a QPrinter and then calls the following paint method myQWidget::paint(), after that scene->render() is called.
The porpoise of the method below is to print data on a paper that is configured to have the same size than the scene while printing the data in the same relative position the textItem had on the scene. Can't do it with QList it doesn't order the items in the same way I added them to the scene.
Here is my code below, it prints with overlapping of some fields doe to QList order items as they appear on the scene.
void myQWidget::paint()
{
qreal dx = 0;
qreal dy = 0;
QList<QGraphicsItem*> L = chequeScene->items();
for (int j=0; j<model->columnCount(); j++) {
if(!L.isEmpty())
{
//Saves the position on dx, dy
dx = L.first()->scenePos().x();
dy = L.first()->scenePos().y();
chequeScene->removeItem( L.first() );
delete L.first();
L.removeFirst();
}
QString txt("");
//selecting printing formar for each column
switch(j)
{
case COLUMNADEFECHA:
txt = QDate::fromString(model->data(model->index(chequenum,j)).toString(), "yyyy/MM/dd").toString("dd/MM/yyyy");
break;
case COLUMNADECHEQUES:
break;
default:
txt = model->data(model->index(chequenum,j)).toString();
break;
}
//filtering not important columns
if(j!=COLUMNADECHEQUES)
{
//Supposubly item with the desired information is added to the scene
//on the same position it had before. Not working.
GraphicsTextItem *item=new GraphicsTextItem();
item->setPlainText(txt);
item->setPos(dx,dy);
chequeScene->addItem(item);
}
}
}
Any idea on how to get this working?
I think as you are getting the scenePos in dx and dy but are setting it using setPos function.
Also as you are using your GraphicsTextItem and not QGraphicsTextItem maybe a look at your paint method will help in understanding the problem.
Try using item->mapFromScene(dx, dy) and then use those coordinates to set the item position by item->setPos(..).
Hope This Helps..

How can I expand all children of a specific modelindex in a QTreeView?

I try this way:
void MainWindow::expandNode(const QModelIndex &parentIndex, bool expand) {
tree->setExpanded(parentIndex, expand);
for (qint32 rowNum = 0; rowNum < treeModel->rowCount(parentIndex); ++rowNum) {
QModelIndex childIndex = treeModel->index(rowNum, 0, parentIndex);
tree->setExpanded(childIndex, expand);
expandNode(childIndex);
}
}
But after some navigations through the tree it stops working. Furthermore selection model currentIndex keeps another node then I see on the screen.
I've already found some dicision here: https://stackoverflow.com/a/5001013/3316930, but I do need to clarify the matter - what's happening with indexes and selection model?
Thanks in advance!
Solved: it was because of feedback between QTabWidget and QTreeView made by me.

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.

How do I make a QVector of widgets?

How do I make a QVector (or some other container class) of a dynamic number of widgets, such as QPushButton or QComboBox in Qt 4?
I've used the following in my window class's constructor:
QVector<QComboBox*> foo; // Vector of pointers to QComboBox's
And now I want to fill it with some number of controls which can change dynamically:
for(int count = 0; count < getNumControls(); ++count) {
foo[count] = new QComboBox();
}
I've searched for hours trying to find the answer to this. The Qt forums mention making a QPtrList, but that class no longer exists in Qt4.
I'd later try to get the text value from each using array-style indexing or the .at() function.
I would really appreciate an example of declaring, initializing, and populating any data structure of any QWidgets (QComboBox, QPushButton, etc.)
here you go :)
#include <QWidget>
#include <QList>
#include <QLabel>
...
QList< QLabel* > list;
...
list << new QLabel( parent, "label 1" );
..
..
foreach( QLabel* label, list ) {
label->text();
label->setText( "my text" );
}
If you are trying just to get a simple example to work, its important that your widgets have a parent (for context / clean up) purposes.
Hope this helps.
foo[count] = new QComboBox();
This won't affect the size of foo. If there isn't already an item at index count, this will fail.
See push_back, or operator<<, which add an item to the end of the list.
QVector<QComboBox*> foo;
// or QList<QComboBox*> foo;
for(int count = 0; count < getNumControls(); ++count) {
foo.push_back(new QComboBox());
// or foo << (new QComboBox());
}
Later, to retrieve the values:
foreach (QComboBox box, foo)
{
// do something with box here
}

Resources