I created TableView (by inhereting from QTableView) and Model (by inhereting from QAbstractTableModel) and implemented all their functions that I needed, but now I have to add strange feature - those objects (stored in models) have to be able to cause table view to select "their" row.
It comes from the fact that there is always graphic object related to them and whenever I click on a scene on a certain object, I wish to center on its representation in table view. Can I do this?
You model could implement a signal emitted every time you want to change the selection. Something like this:
void CMyModel::sigUpdateSelection(const QItemSelection & selection, QItemSelectionModel::SelectionFlags flags);
And than you could connect this signal to the QItemSelectionModel of your table view. This is how you get the selection model:
QTableView* view = new QTableView(parent);
QItemSelectionModel* selectionModel = view->selectionModel();
QItemSelectionModel has a slot select(). This is where you will connect your signal.
And this is how you will emit:
// Add to current selection
emit sigUpdateSelection(QItemSelection(indexLeft, indexRight), QItemSelectionModel::Select);
// Clear current selection and select new one
emit sigUpdateSelection(QItemSelection(indexLeft, indexRight), QItemSelectionModel::ClearAndSelect);
Related
I am making a qt application with PyQt. I have a custom QTableModel that i show in a QTableView. Each row in this model can be checked, when a row is checked I want it to appear in another QTableView. To do this I made a custom proxymodel subclassing from QSortFilterProxyModel.
In my custom model I reimplemented the filterAcceptsRow method to only accept rows that are checked:
def filterAcceptsRow(self, pos, index):
model = self.sourceModel() # The sourcemodel is a 2d array of QStandardItem
row = model.row(pos) # This method returns the row at index pos
if row[0].checkState(): # The first QStandardItem has a checkbox
return True
return False
This works once at the start of my program (I assume when i call setSourceModel) but doesn't update when the checked items change. How do I make sure this function gets called when the checkstate changes in my base model? Or is there another way to do this?
I have the following (simplified) code to add QStandardItem's to a QStandardItemModel, attach the model to a QListView, and connect signals of my choice from the model to a function of my choice:
// MyUIContainer derives from QWidget
MyUIContainer::SetItems()
{
// Inside a member function where model/items are added to a QListView...
// This code does not show deletion of existing ItemSelectionModel
QStandardItemModel * model = new QStandardItemModel(ui->listView);
// In the real code, data is set in each QStandardItem
model->setItem( 0, new QStandardItem() );
model->setItem( 1, new QStandardItem() );
model->setItem( 2, new QStandardItem() );
connect(model,
SIGNAL(itemChanged(QStandardItem*)),
this,
SLOT(ReceiveChange(QStandardItem*)));
// I have also tried connecting to this signal - same problem described below
//connect(model, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &, const QVector<int>)), this, SLOT(ReceiveChange(const QModelIndex &, const QModelIndex &, const QVector<int>)));
ui->listView->setModel(model);
}
MyUIContainer::ReceiveChange(QStandardItem * item)
{
// Problem! This slot is called *three times*
// whenever the check state of an item changes
}
As the code comment indicates, there is a problem: The itemChanged() \ ReceiveChange() signal \ slot combination is called three times whenever the check state of a checkbox in the QListView changes once.
I understand that I can create my own Model class that is derived from QAbstractItemModel, and (if I understand correctly) handle mouse clicks in the view myself, emitting signals such as, for example, the itemChanged signal with a proper user-defined role so that a change in checkbox state can be handled only once.
However, the latter approach requires me to be careful to handle all possible scenarios of mouse clicks & keyboard events that could result in a changed checkbox state. I was hoping, and assuming, that this logic has been handled inside the Qt library itself, so that I don't have to - so that I can simply receive a signal (or some other message) when the check state of a checkbox changes.
Note that it seems that QListWidget (rather than QListView) also has exactly the same problem.
So, my question is: Without writing my own Model class derived from QAbstractItemModel - with its requirement that I myself write code to handle every possible way that a check state can change - how do I capture a changed check state (via a signal or otherwise) such that I can unambiguously capture every time a check state changes once and only once per check state change?
ADDENDUM
With apologies - as I took great care in ruling everything else out - Riateche's comment made it clear to me that the behavior is not supposed to work in the way this question highlighted. Finally, I have indeed tracked down the problem to the fact that I was calling connect() multiple times.
I have QTableView which has QComboBox in one of the columns. The combobox is displaying data from a vector which get updates when I click a button.
When I start the application the combobox displays all the items in vector. Now I press the button (which adds more items to the vector) but the combobox doesn't reflect new data in vector. It still shows old data. I am also emitting dataChanged() once the vector is updated but I don't see any change. data() function does get call in the model which does return all the elements of the vector, but setEditorData doesn't get call in delegate.
Am I missing something.
Thanks,
Dev
Then you need to do something like this function:
void updateComboBox(QComboBox *comboToUpdate, const QStringList & list )
{
QString curentText = comboToUpdate->currntText();
comboToUpdate->clear();
comboToUpdate->insertItems(list);
comboToUpdate->setCurrentIndex(comboToUpdate->findText(currentText));
}
Lines
QString currentText = comboToUpdate->currentText();
...
comboToUpdate->setCurrentIndex(comboToUpdate->findText(currentText));
are optional and used to don't change currentItem after selection.
I have a QTableView, populated with a QStandardItemModel.
I update the model frequently over network and the model is also updated by user directly via the QTableView.
Now I like to call a method when the user is changing some data, so i did:
connect(model, SIGNAL(itemChanged(QStandardItem*)), this, SLOT(dataChanged(QStandardItem*)));
The Problem now is, that my dataChanged method is called, also when the item is updated over the network.
model->setData(index, new_val);
Is there another signal which is only emitted if, the user is changing something inside the QTableview ???
No, AFAIK there is no such signal but you there is a way to hack it.
When editing an item from the QTableView the activated signal will have been emited. The idea is to catch this signal and connect it to a slot that will store the last manually changed item.
connect(view, SIGNAL(activated(QModelIndex), this, SLOT(manuallyActivated(QModelIndex)));
void manuallyActivated(QModelIndex index)
{
// This variable should be in your header file...
lastManuallyModifiedIndex = index;
}
Now simply modify your dataChanged slot to check if the item that changed corresponds to the last modified item.
void dataChanged(QStandardItem* item)
{
// If it is invalid simply ignore it...
if (lastManuallyModifiedIndex.isValid() == false)
return;
// only if it is modified manually we process it
if (item->index() == lastManuallyModifiedIndex)
{
// make last modified index invalid
lastManuallyModifiedIndex = QModelIndex();
doSomething();
}
}
You could block the table signals when an update comes in from your network.
QObject::blockSignals(bool block)
or you could listen for click and edit event in succession.
I have a Qt Form that contains 2 combo box messages. The second combobox message depends on the first combo box message. I mean that the dates from the second combobox message depends on the element that I select in the first combobox.
In this moment I have different dates in the first combobox. But the second combobox is not working. I need to creare a connect method or what?
Thx! APpreciate!
Could someone give me a short example?
It's fairly simple. A combobox emits the currentIndexChanged signal that also tells you the new index. Write a method that accepts an integer and changes the second combobox according to the integer (which is the index of the selection in combobox 1).
Here are some code sniplets from a working example.
Method declaration in your window/whatever class header:
public slots:
void setI1(int index);
Filling combobox 1, connecting the signal, e.g. in the constructor:
i1Box->addItem("Neutral", 0);
i1Box->addItem("2,856 K (Illuminant A, light bulb)", 2856);
// ...
connect(i1Box, SIGNAL(currentIndexChanged(int)),
this, SLOT(setI1(int)));
Implementation of the method:
void ViewerWindow::setI1(int index) {
// either use index directly, or, as in this case we have items holding an int:
int i1 = i1Box->itemData(index).value<int>();
// use the value to change second combobox here
}
If it does not work as expected, it is always helpful to print some debug output inside the method that should be called to see where it goes wrong in the chain.
Reference: http://doc.qt.nokia.com/latest/signalsandslots.html