QTreeWidget drag and drop for reordering selects wrong item - qt

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);
}

Related

in Qt setFocus() for QGraphicsItemGroup

How to be given setFocus() properties into QGraphicsItemGroup item?
I have 3 GraphicsRectItem in QGraphicsItemGroup with bondingrect()
and I want to give setfocus() preporty this QGraphicsItemGroup to can be controled with keyPressEvent
Although your question does not provide enough details, but it seems that you have some graphics items inside a group, and you want to control the items through the keyboard, right?
If so, let's assume that you have some QGraphicsItem items in a QGraphicItemGroup and you want to change the active item by using the Tab key and then do some actions on the active item by using other keys (e.g., rotate it or so on).
My solution is as follows:
define a QGraphicsItem *activeItem pointer which will point to the active item in the group.
overriding the group's keyPressedEvent function as follows:
void MainWindow::keyPressEvent(QKeyEvent *event)
{
//Which key is pressed? ---> <QKeyEvent> must be included
if (event->key() == Qt::Key_Tab) {
/* set activeItem as you desire.
* For example, find the index of the
* item that currently activeItem is pointing
* to and then go to the next item.
*/
//then return
}
if (event->key() == Qt::Key::YourDesiredKey) {
//do your actions and then return
}
}

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

QT: changing the tab traversal order on runtime?

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;
}
}
}

trigger mouseMoveEvent of several QGraphicsItem at the same time

When I select several QGraphicsItem (with Ctrl key) I can move them together, but the mouseMoveEvent is triggered only for the item that actually receives the event. Is there a way to make every selected items receive the event ? I can't find it in Qt's doc.
Could I group selected items together and handle it within QGraphicsView's mouseMoveEvent ?
Thanks a lot for any help :)
No there is no default way to do what you want as far as I know. Something you could do is the following:
Subclass QGraphicsScene and implement the mouseMoveEvent
In the mouse move event check if there is an item at the event position using the itemAt function
If there is an item and it is selected (isSelected), get all selected items of the scene.
For all selected items call the same function you would call.
Sample code follows:
void mouseMoveEvent(QGraphicsSceneMouseEvent * mouseEvent)
{
QPointF mousePosition = mouseEvent->scenePos();
QGraphicsItem* pItem = itemAt(mousePosition.x(), mousePosition.y());
if (pItem == NULL)
{
QGraphicsScene::mouseMoveEvent(mouseEvent);
return;
}
if (pItem->isSelected() == false)
{
QGraphicsScene::mouseMoveEvent(mouseEvent);
return;
}
// Get all selected items
QList<QGraphicsItem *> items = selectedItems();
for (unsinged i=0; i<items.count(); i++)
// Do what you want to do when a mouse move over a selected item.
items[i]->doSomething();
QGraphicsScene::mouseMoveEvent(mouseEvent);
}
I'm reading between the lines of your question a little, but it sounds like you might be better served by implementing QGraphicsItem::itemChange on your QGraphicsItem class(es). This will get called whenever the position changes--whether by mouse, keyboard, programmatic, etc. You can even cancel the change if you want to.
http://doc.qt.io/qt-5/qgraphicsitem.html#itemChange

resizing row's height in QTreeWidget/QTreeView

I have some problems with sizing row's height in QTreeWidget. I use QStyledItemDelegate with QPlainTextEdit. During editing text in QPlainTextEdit i check for changes with a help of:
rect = self.blockBoundingRect(self.firstVisibleBlock())
and if text's height changes i resize editor size and need row in QTreeWidget also resizing. But i don't know how to inform TreeWidget or a Delegate about changes. I tried to initialize editor with index, that i could use in a future, but Delegate creates new editor every time and i failed to use signals. Also I used following function to catch resize event, but it' doesn't:
bool QAbstractItemDelegate::editorEvent ( QEvent * event, QAbstractItemModel * model, const QStyleOptionViewItem & option, const QModelIndex & index )
How can i bound editor's size changes with TreeWidget?
And one more thing, by default all items (cells) in TreeWidget have -1 or some big value as default width. I need whole text in cell to be visible, so how can i limit cells width only by visible range and make it expand in height? I want for it the same behavior as in instance a table in MSWord.
Thank you in advance,
Serge
I believe you would have to notify model about the data change without closing editor, this should force treeview to recalculate its row height according to the new content of the field it's showing. What you could do is override eventFilter method in your delegate and try to emit commitData signal on key press; smth like this:
bool YourStyledItemDelegate::eventFilter(QObject* object, QEvent* event)
{
bool result = QStyledItemDelegate::eventFilter(object, event);
QWidget* editor = qobject_cast<QWidget*>(object);
if (editor)
{
if (event->type() == QEvent::KeyPress)
{
emit commitData(editor); //<- this should force row to recalculate its size
}
}
return result;
}
hope this would give you an idea on how to proceed, regars

Resources