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
}
}
Related
I would like to know the best way to achieve the following thing in QML:
I have a ListView with droppable elements and a Grid initialy filled with DropArea. The ListView uses a model derived from QAbstractItemModel. I would like to drop an element on the grid and interact with it (rename it for instance). For now, any modifications in the ListView update the model, but how modifications of the element in the grid could update the model ?
There can be multiple items dropped in the grid corresponding to a subset of ListView's model. I do not know how can I achieve this. The Package can not be used because the Grid is not a GridView and Items must be moved/set at specific positions. So I tried to:
create a ListView displayed on the dropped item, using the same model as the source ListView used to drag items,
set the same rootIndex, then the same index
I am close to a solution but I think it is not the best way to do this.
Any clue ?
Thanks
I would like to have different visual representation of the same model
item in a ListView and in a component in a Grid. So, a modification of
the item in the ListView should update the item in the Grid and
vice-versa.
If your model's data is not QObject derived with NOTIFY and properties, notifications of changes can only be made through the model.
And since you won't be using the model for the grid, that means your model has to use underlying QObject instances. A generic model object might come in handy.
When you have that you only need to reference the underlying QObject model item and use bindings to set up your list view delegate. Then when you drag and drop, you only need to pass a reference to the QObject to that other visual representation which you will be creating inside the grid.
Lastly, take care not to be left with dangling references when you remove items from the model, as that will most likely hard-crash your application. Use the onDestruction attached signal to clear elements from the grid as the model item objects are destroyed.
I finally found a solution by create a C++ type which inherits from QObject an which can be embedded in a QML object. This type has read/write properties and is initialized with the same model as the ListView. The interesting methods are:
/* writing to a property **from QML** goes here */
void ModelItem::setName(const QString& name)
{
setModelData(GroupMemberModel::Nom, name);
}
/* then here */
bool ModelItem::setModelData(GroupMemberModel::Role role, const QVariant& value)
{
return m_model->setData(m_modelIndex, value, role);
}
/* any changes in the model fall here (signals/slots mecanism)*/
void ModelItem::dataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight, const QVector<int>& roles)
{
if(m_modelIndex.row() < topLeft.row() || m_modelIndex.row() > bottomRight.row())
return;
if(m_modelIndex.column() < topLeft.column() || m_modelIndex.column() > bottomRight.column())
return;
//Index is modified, emit signal
foreach(int role, roles) {
emitDataChanged(role);
}
}
/* **notify QML** by emit signal on property */
void ModelItem::emitDataChanged(int role) const
{
if(role < (Qt::UserRole+1))
role+=Qt::UserRole+1;
switch(role)
{
case GroupMemberModel::Nom:
emit nameChanged();
break;
default:
qDebug() << "ModelItem::dataChanged, unknown role";
break;
}
}
This works as needed and is simplier than what I thought.
I have a QListView with the ViewMode set to IconMode. I would like to achieve the following DnD behavior:
If a list view item is dragged inside the view, only the items position in the view is changed. This is the same as setting DragDropMode equal to InternalMove.
If a list item is moved out of the view, it can be copied to another external view. In this case, DragDropMode is equal to DragOnly.
How do I mix the two modes in such a way that both behaviors are supported by the view?
You might be able to do this by overriding the dropEvent of your view like this:
void MyListView::dropEvent( QDropEvent* e )
{
if( e->source() != this )
{
// something comes from the outside
// what to do? return?
return;
}
else
{
// event comes from the view itself, let's do some stuff
// for example call the base class default event
QAbstractItemView::dropEvent(e);
}
}
I guess the correct flag would be QAbstractItemView::DragDrop to do this.
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
I am attempting to create a model/view application in Qt 4.7.1. I am a very new Qt developer.
Summary of what I am attempting to do:
I have a treeview that is organized as a rectangular table of rows and columns. One column of items contains a button. By default this button is to be transparent and disabled. A given button is to become visible and enabled when the mouse is hovering over its row.
The approach I am pursuing is to
find the model index for the cell that the mouse is hovering over, and
obtain a pointer to the widget associated with the widget, and
using this pointer manipulate the visibility of the button within said widget.
I cannot get a valid pointer to the widget.
my current code looks like this:
void HistoryTreeView::mouseMoveEvent(QMouseEvent *event)
{
QAbstractItemModel *m(model());
// Only do something when a model is set.
if (m)
{
QModelIndex index = indexAt(event->pos());
if (index.isValid())
{
// if the mouse has moved to another row
if (index.row() != m_currentRow)
{
m_currentRow = index.row();
QMessageBox::information( this, "HistoryTreeView", QString("index(%1)").arg(index.row()));
QWidget * item = indexWidget(index);
Q_ASSERT(item != NULL );
}
}
else // model is invalid
{
m_currentRow = -1;
}
}
QTreeView::mouseMoveEvent(event);
}
The symptoms:
I expected the call to indexWidget() to return a valid pointer to the widget the mouse is over. Instead it unexpectedly returns a NULL pointer.
Commentary:
The variable named 'index' is acting as I expected because the QMessageBox shows the correct row value. Consequently I do not think there is anything wrong with the value I am providing to indexWidget().
This is just debug code. It is missing things like code that selects the column that holds the buttons.
OK, Here is the nature of my error as I understand it.
I had incorrectly understood that every item in a view is its own widget. I now understand that the view itself is a widget, but that individual items within the view are not widgets, per se.
Because I had misunderstood that view items were widgets I believed that could:
obtain an index from a given element in a model,
use indexWidget() to obtain a Widget * to the view item associated with the model element
and then use this pointer to manipulate the view item as though it was a widget.
indexWidget() simply returned a NULL because view items are not widgets.
I am implementing QAbstractTableModel and I would like to insert a QPushButton in the last column of each row. When users click on this button, a new window is shown with more information about this row.
Do you have any idea how to insert the button? I know about delegating system but all examples are only about "how to edit color with the combo box"...
You can use
QPushButton* viewButton = new QPushButton("View");
tableView->setIndexWidget(model->index(counter,2), viewButton);
The model-view architecture isn't made to insert widgets into different cells, but you can draw the push button within the cell.
The differences are:
It will only be a drawing of a pushbutton
Without extra work (perhaps quite a bit of extra work) the button won't be highlighted on mouseover
In consequence of #1 above, you can't use signals and slots
That said, here's how to do it:
Subclass QAbstractItemDelegate (or QStyledItemDelegate) and implement the paint() method. To draw the pushbutton control (or any other control for that matter) you'll need to use a style or the QStylePainter::drawControl() method:
class PushButtonDelegate : public QAbstractItemDelegate
{
// TODO: handle public, private, etc.
QAbstractItemView *view;
public PushButtonDelegate(QAbstractItemView* view)
{
this->view = view;
}
void PushButtonDelegate::paint(
QPainter* painter,
const QStyleOptionViewItem & option,
const QModelIndex & index
) const
{
// assuming this delegate is only registered for the correct column/row
QStylePainter stylePainter(view);
// OR: stylePainter(painter->device)
stylePainter->drawControl(QStyle::CE_PushButton, option);
// OR: view->style()->drawControl(QStyle::CE_PushButton, option, painter, view);
// OR: QApplication::style()->drawControl(/* params as above */);
}
}
Since the delegate keeps you within the model-view realm, use the views signals about selection and edits to popup your information window.
You can use setCellWidget(row,column,QWidget*) to set a widget in a specific cell.