QT: changing the tab traversal order on runtime? - qt

you might be able to tell that I'm pretty new to QT...
My program contains a window with several Widgets in a QGridLayout. Some of these Widgets have a layout and child widgets themselves. Pressing the Tab key moves the focus like I expect it to, in the order I created the widgets.
Problems occur when a widget changes it's content (I do that by deleting all child widgets and then constructing new ones.) If I do that, new widgets are moved to the end of the tab chain (that indicates to me, that tab order is kind of global for a window). I have tried to use QWidget::setTabOrder() to reorder all widgets (I tried both, setting tab order for only the contends of the main window and setting it for the children too) but the actual order doesn't change. I did this by emitting a signal when the contend of a widget changes and connecting that to a slot on my main Window.
I think I understand the way the setTabOrder() function should work. I do something similar to this:
QWidget* a = firstWidget;
QWidget* b = secondWidget;
QWidget::setTabOrder(a,b);
for (int i = 0; i < widgets.size(); ++i) {
a = b;
b = widgets[i];
QWidget::setTabOrder(a,b);
}
Is there anything special one has to do when changing the tab order?
I also tried to reimplement focusNextPrevChild(bool next) and focusInEvent(QFocusEvent* e) similar to what can be found at this site. 1
I managed to mess up tab order a lot more like this... is this approach a step in the right direction?
I'm sorry if this is something simple that I managed to miss, but I'm struggling for a while now and I can't find a solution.
Any help is very appreciated.

I had the same problem, and resolved it using the info Tim Meyer provided in the comments.
Tab order is not hierarchical - calling setTabOrder on a parent widget that doesn't accept focus will not cause the focus to be passed to the child. You will need to fetch the appropriate children from the widget and set the order on them
In my case, a dynamically constructed QTreeWidget contained editable widgets, and I needed to reset setTabOrder to account for widgets being created out-of-order.
The following code is (most) of the implementation
// Perform a depth-first gather of the child widgets of this item
void gatherTabWidgets( QObject* item, QWidgetList& tabWidgets )
{
if (item->isWidgetType())
{
QWidget* itemWidget = static_cast<QWidget*>(item);
if (itemWidget->focusPolicy() & Qt::TabFocus)
tabWidgets.push_back( itemWidget );
}
const QObjectList& children = item->children();
for (QObjectList::const_iterator itr = children.begin(); itr != children.end(); itr++)
{
gatherTabWidgets( const_cast<QObject*>(*itr), tabWidgets );
}
}
// Perform a depth-first gather of the child items widgets;
void gatherTabWidgets( QTreeWidgetItem* item, QWidgetList& tabWidgets )
{
QWidget* itemWidget = fetchWidgetForItem( item );
gatherTabWidgets( itemWidget, tabWidgets );
for (int i = 0; i < item->childCount(); i++)
{
gatherTabWidgets( item->child( i ), tabWidgets );
}
}
void VETreeWidget::sortTree()
{
// Ensure ordering is maintained.
sortItems( 0, Qt::AscendingOrder );
// Once the items have been re-ordered, re-create the tab ordering
QTreeWidgetItem* baseItem = topLevelItem( 0 );
QWidgetList tabWidgets;
gatherTabWidgets( baseItem, tabWidgets );
if (!tabWidgets.empty())
{
QWidget* lastWidget = tabWidgets.first();
// Connect this treeview to the first widget. This ensures
// if the treeview is tabbed-to, it will tab to the correct sub-widget
setTabOrder( this, lastWidget );
for (QWidgetList::iterator itr = tabWidgets.begin() + 1; itr != tabWidgets.end(); itr++)
{
setTabOrder( lastWidget, *itr );
lastWidget = *itr;
}
}
}

Related

How to take my widget in QListWidget, using currentItemChanged signal?

I have QListWidget pointer (pListWidget). A have my custom (QCustomItem) widget, inerited from QWidget and containing some data. I adding some widgets into the list:
int iMyData = 555;
...
QListWidgetItem *pListWidgetItem = new QListWidgetItem();
QCustomItem *pCustomWidget = new QCustomItem(iMyData);
pListWidget->addItem(pListWidgetItem);
pListWidget->setItemWidget(pListWidgetItem, pCustomWidget);
...
This part of code may be in cycle (adding few widgets).
Also I detecting item changing by connection:
connect(pListWidget, SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)), this, SLOT(slotItemChanged(QListWidgetItem*,QListWidgetItem*)) );
And slot realization:
void QSomeForm::slotItemChanged(QListWidgetItem* pCurrent, QListWidgetItem* pPrevious)
{
QCustomItem *pCustomWidget = // And this is my question: how to take my custom widget in this block code using pListWidget and pCurrent?
// Processing pCustomWidget
int iMyData = pCustomWidget->iMyData(); // In current example I want to see 555 here
}
How I can extract my widget from QListWidget ? Thanks! I hope on your help!
Read the doc. If you have found QListWidget::setItemWidget() you can find:
QWidget * QListWidget::itemWidget(QListWidgetItem * item) const

QTreeWidget drag and drop for reordering selects wrong item

After having followed the recommendations given here : QTreeWidget reordering child items by dragging, the dragged item is not selected.
So, quite naturally, I tried to get the dragged item and then call setSelected() on it.
The result is that the item before the correct on is selected.
I subclass QTreeWidget to override dropEvent like this -
QTreeWidgetItem *pItem;
QModelIndex dropIndex = indexAt(pEvent->pos());
if(dropIndex.isValid() == false)
{
pEvent->setDropAction(Qt::IgnoreAction);
pEvent->accept();
return;
}
pItem = this->itemAt(pEvent->pos());
QTreeWidget::dropEvent(pEvent);
How can I get the pointer to the correct QTreeWidgetItem which has been dropped ?
Since the dropped item can "fall" above or below of the target item, you need to manage both situations and calculate the correct index of the moved item. For example:
[..]
virtual void dropEvent(QDropEvent * event)
{
QModelIndex droppedIndex = indexAt( event->pos() );
if( !droppedIndex.isValid() )
return;
QTreeWidget::dropEvent(event);
DropIndicatorPosition dp = dropIndicatorPosition();
if (dp == QAbstractItemView::BelowItem) {
droppedIndex = droppedIndex.sibling(droppedIndex.row() + 1, // adjust the row number
droppedIndex.column());
}
selectionModel()->select(droppedIndex, QItemSelectionModel::Select);
}

How to get list of visible QModelIndex in QAbstractItemView

Is there any way to get a list of currently visible items in QAbstractItemView? And, if it possible, to receive any notifications about changing of this list.
Upd:
I'm asking exactly about QAbstractItemView or QTreeView with non-plain structure, not QTableView.
Upd2:
I'm implementing tree view model with checkboxes. I want next behavior (same for checking/uncheking):
If one of checkbox is checked - then all childs must be checked
If all child checkboxes are checked - then parent check box should be checked too. And same for parent of parent, and so on...
Check state are monitored/modified by external data source, so I need a mechanism to update all changed children/parents. dataChanged signal is not enough for me because it is very expansive to build a list of all changed QModelIndex for updating. And it is not necessary at all, because all fresh data will be picked from QAbstractItemModel::data.
I found next dirty hack to update all items: emit dataChanged( QModelIndex(), QModelIndex() ); but it's undocumented for invalid indexes.
So, I need a way to force all visible items redraw they content with fresh data.
You can get the topleft and bottom right cell by calling:
tableview->indexAt(tableview->rect().topLeft())
tableview->indexAt(tableview->rect().bottomRight())
To get notification of change, reimplement the virtual function of qabstractscrollarea
scrollContentsBy
This function is called when the view port is scrolled.
call QTableView::scrollContentsBy and then do whatever you need to.
For QTreeView, the list of visible items can be traversed like this:
QTreeView& tv (yourTreeView);
// Get model index for first visible item
QModelIndex modelIndex = tv.indexAt(tv.rect().topLeft());
while (modelIndex.isValid())
{
// do something with the item indexed by modelIndex
...
// This navigates to the next visible item
modelIndex = tv.indexBelow(modelIndex);
}
I think that there are no cases where list of visible items is requred. In case of correct model implementation all items are updated automatically.
Hard part of implementation - force children and parents to update. I wrote following code:
bool TreeModel::setData( const QModelIndex &index, const QVariant &value, int role )
case Qt::CheckStateRole:
{
TreeItemList updateRangeList; // Filled with items, in which all childred must be updated
TreeItemList updateSingleList; // Filled with items, which must be updated
item->setCheckState( value.toBool(), updateRangeList, updateSingleList ); // All magic there
foreach ( TreeAbstractItem *i, updateRangeList )
{
const int nRows = i->rowCount();
QModelIndex topLeft = indexForItem( i->m_childs[0] );
QModelIndex bottomRight = indexForItem( i->m_childs[nRows - 1] );
emit dataChanged( topLeft, bottomRight );
}
foreach ( TreeAbstractItem *i, updateSingleList )
{
QModelIndex updateIndex = indexForItem( i );
emit dataChanged( updateIndex, updateIndex );
}
}
i always update whole QAbstractTableModel with:
emit dataChanged(index(0, 0), index(rowCount(), columnCount()-1)); // update whole view
Method 1
i, j = table.indexAt(table.rect().topLeft()).row(), table.indexAt(table.rect().bottomLeft()).row() - 1
Method 2
i, j = table.rowAt(0), table.rowAt(table.height()) - 1

Check whether a widget is in the layout Qt

How to check whether a widget is in a layout? I have a layout that may contain the widget or not.
Widget name: infoClient
Layer name: verticalLayout_3
Need to check if the widget exists in the layout, then do not add a new one.
And if does not exist, add a new one.
How to do it?
void MainWindow::slotPush1()
{
if <there is no infoClient> ui->verticalLayout_3->addWidget(new infoClient(this));
}
Few points:
The parent of a widget in a layout is the widget containing the top-level layout
Layouts can be nested.
A layout contains items (QLayoutItem), which are either layouts (layout() is not null) or widgets (widget() is not null). In the end you have a tree of layout items.
So You need to do a search from the parent widget (dfs, bfs).
bool checkWidgetInsideLayout(const QWidget* _someWidget){
return _someWidget != NULL &&
_someWidget->parent() != NULL &&
_someWidget->parent()->layout() != NULL &&
foundItem(_someWidget->parent()->layout(), _someWidget );
}
//clumsy dfs
bool foundItem(const QLayout* layout, const QWidget* _someWidget ){
for(int i = 0; i < layout->count(); i++){
QLayoutItem* item = layout->itemAt(i);
if(item->widget() == _someWidget )
return true;
if(item->layout() && foundItem(item->layout(), _someWidget) )
return true;
}
return false;
}
Use QObject::findChild to find a child by its name. For instance:
void MainWindow::slotPush1()
{
if (ui->verticalLayout_3->findChild<QWidget*>("infoClient")) // your code to add it here
}
Note: findChild is a template function. If you're not familiar with template functions, just know that you pass the type of object you want to find (in your example, it looks like you could use ui->verticalLayout_3->findChild<infoClient*>("infoClient")). If you want to find a QWidget, or anything that inherits from QWidget, you can just use findChild<QWidget*>() and you'll be safe.
There is no mechanism within Qt that will perform the check that you are looking for. You will have to implement it yourself:
void MainWindow::slotPush1()
{
if (doesLayoutContainInfoClient(ui->verticalLayout_3))
{
ui->verticalLayout_3->addWidget(new infoClient(this));
}
}
bool MainWindow::doesLayoutContainInfoClient(QLayout* layout)
{
const QString infoClientName("infoClient");
for (int i=0; i<layout->count(); i++)
{
QWidget* layoutWidget = layout->itemAt(i)->widget();
if (layoutWidget)
{
if (infoClientName == layoutWidget->metaObject()->className())
{
return true;
}
}
}
return false;
}
Despite what I've suggested above, I don't really recommend it. It makes more sense to store whether or not you've added infoClient to your layout as an independent boolean within your program somewhere. Querying the contents of layouts in this manner is somewhat unusual, and is messier than just using a bool.
I agree with Tom Panning's solution to find your child with the QObject::findChild() method.
But adding a Widget to QLayout will reparent it to the layout's parent. So you'll have to find it by calling it with the MainWindow object like that:
void MainWindow::slotPush1()
{
if (this->findChild<QWidget*>("infoClient")) {
// ...
}
}
If your infoClient widget was added in the QtDesigner, you won't have problems with this solution. The designer sets per default the object name.
If the infoClient was added to the layout in your code, you have to set the object name explicitly, otherwise you won't be able to find it because its name is empty:
(Assuming, m_client is a member variable of MainWindow)
void MainWindow::createWidgets()
{
if (infoClientShouldBeAdded) {
m_client = new infoClient(this);
m_client->setObjectName("infoClient");
ui->verticalLayout_3->addWidget(m_infoClient);
}
}
Another possibility would be to keep track of the pointers of your created widgets (e.g., by storing them in a std::vector or Qt equivalent). This allows to use indexof(QWidget*):
Searches for widget widget in this layout (not including child layouts).
Returns the index of widget, or -1 if widget is not found.

QListWidget get list of viewed items

Let's say I have a listwidget with lots of items. When the user scrolls to see more items, I want to perform an action to the items being viewed. Is it possible to trigger an event itemsViewedChanged(QListWidgetItems *items) that gives me the currently viewed by the user items?
If no, how can I implement something like this?
Thanks for any answers
I don't know of a pre-existing method, however I've implemented something similar in a QTreeWidget to handle loading of icons for only visible items, the general technique I would use for a QListWidget would be a bit simpler (forgive the syntax, I normally use PyQt, so this could be off in C++):
QList<QListWidgetItem*> MyListWidget::visibleItems() {
QList<QListWidgetItem>* output = new QList<QListWidgetItem*>();
// make sure we have some items
if ( !this->count() ) {
return output;
}
// calculate the beginning and end items in our range
QListWidgetItem* minimumItem = this->itemAt(5, 5);
QListWidgetItem* maximumItem = this->itemAt(5, this->height() - 5);
if ( !minimumItem ) { minimumItem = this->item(0); }
if ( !maximumItem ) { maximumItem = this->item(this->count() - 1); }
// get the start and end rows
int minimum_row = this->indexForItem(minimumItem)->row();
int maximum_row = this->indexForItem(maximumItem)->row();
for (int row = minimum_row; row <= maximum_row; row++) {
output->append(this->item(row));
}
return output;
}
That would get you a list of the visible items for the list widget. To dynamically check and modify things as the user is editing, you can then connect to the valueChanged(int) and rangeChanged() signals for the list widget. That way, as the user scrolls or resizes your view, you are reacting to the signal and collecting the now visible list of items.

Resources