I would like to have QAbstractItemView that will allow multi-selection of items only with ctrl button toggle. I can't use QAbstractItemView::ExtandedSelection because it also allow multiple items selection by dragging the mouse over them.
I assume you are using a QTableView
You can override the QTableView and then use mouseMoveEvent cleverly to ensure that user can't make multiple selections by dragging!
If user enters into mouseMoveEvent with the left mouse button pressed, you can choose to eat the event without passing it to the QTableView that will remove the possibility of multiple selection.
e.g.
void
TableView::mouseMoveEvent( QMouseEvent * inEvent )
{
// Deliberately commented to not to pass this event to parent to avoid multiple selection
// QTableView( inEvent );
inEvent->accept();
}
This might work for you, you may also have to be careful in the mouseMoveEvent, when you do above to mousePressEvent you have to do the same for mouseReleaseEvent as well.
Though this is just a theory , but is should work!
Related
I am creating a node graph and I want to be able to click the empty space in a scene and middle-mouse-drag to navigate without deselecting the currently selected items in the scene. Any suggestions?
I can block the middle click in the view's mousePressEvent and get the right behavior but then I no longer have middle-mouse click events working on items in the scene. I don't mind a middle-click resulting in a single selection when clicking on an item in the scene, but if I middle-click the empty space in the scene I don't want the selection altered.
This didn't cover the more complex behavior I am looking for: PyQt. How to block clear selection on mouse right click?
I didn't try using an eventFilter as I assume the issue would be the same
I am using PyQt/PySide, FWIW.
Before I roll my own workaround I thought I'd post here for the correct way or at least other workaround ideas.
Some workaround ideas:
Block the mousePressEvent to the scene but iterate over child items to deliver it directly
Restore selection while still in the mousePressEvent in the scene. Probably bad for performance at scale but simple I suppose.
Any feedback would be great!
[Edit:]
Here is my python version of the answer. Code tested. In my QGraphicsScene derived class:
def mousePressEvent(self, event):
# Prevent the QGraphicsScene default behavior to deselect-all when clicking on
# empty space by blocking the event in this circumstance.
item_under_the_mouse = self.itemAt(event.scenePos())
if event.button() == QtCore.Qt.MidButton and not item_under_the_mouse:
event.accept()
else:
super(GraphScene, self).mousePressEvent(event)
In your QGraphicsScene::mousePressEvent derived implementation, if it's a middle mouse click, check for items under the mouse click. If there aren't any, then accept the event and don't call the base class implementation. If something is under the click, then just call the base implementation; you don't have to try to reimplement that yourself. I think this is the general idea:
void MyScene::mousePressEvent (QGraphicsSceneMouseEvent *evt)
{
if ((evt->buttons () & Qt::MidButton) && items (evt->scenePos ().count ())
{
QGraphicsScene::mousePressEvent (evt);
}
else
{
evt->accept ();
}
}
I'm not sure if the accept is required in this case or not. I haven't compiled or tested this, but hopefully, it's helpful to get you going in the right direction.
I have a program that creates a grid of buttons using PyQt (all QPushButtons in a QGridLayout), like this:
I was wondering how I can drag my mouse while holding mouse1 down to run each button's function for each one that the mouse is dragged over. Right now, when dragging and holding, only the button the click was started on is triggered, and no other buttons can be selected until mouse release. Is there a function that just runs it on press or release, or unselects the button after the function is ran?
For reference, my code for the currently selected button is as follows:
self.button.pressed.connect(lambda: self.click_func(self_global, x, y,
btn_id))
The function isn't important, and would just take up more room here, but I hope you get the point.
You could use an eventFilter. This allows you to send events to another QObject (usually the parent widget of the buttons) to decide what to do with them.
class MyWidget(QtGui.QWidget):
def createButton(self):
button = QtGui.QPushButton()
button.setMouseTracking(True)
button.installEventFilter(self)
return button
def eventFilter(self, obj, event):
if isinstance(obj, QtGui.QPushButton):
if event.type() == QtCore.QEvent.Enter:
if event.buttons() & QtCore.Qt.LeftButton:
print 'Mouse Pressed Over Button'
return False
Looking at your example, you may want to also consider using a QGraphicsView and QGraphicsScene. They are good building blocks for creating truly custom widgets. It's possible you may be able to hack QPushButtons to behave in a way they weren't intended, but for custom drawn widgets, QGraphicsViews are the way to go.
I'm trying to use QTreeWidget for a file browser. I want to have separate concepts of selection (items to which operations are applied) and cursor (that can be used for extending selection). Like in Total Commander: red is selection, moving cursor doesn't affect selection unless Shift is pressed.
Is there any neat way to implement this with QTreeWidget? Or not neat but at least working and not requiring to manually handle half the events?
If you set the widget's selection mode to QAbstractItemView::ExtendedSelection then an user will be able to navigate through items without changing selection using arrow keys with pressed Ctrl. If you want to change this behavior, you should reimplement keyPressEvent as follows:
void MyWidget::keyPressEvent(QKeyEvent *event) {
if (event->key() == Qt::Key_Down ||
event->key() == Qt::Key_Up) {
event->setModifiers(Qt::ControlModifier);
}
QTreeWidget::keyPressEvent(event);
}
Now when user press Up or Down key, the current item is changed and the selection remains unchanged. I've tested this solution.
Note that there are also pageup, pagedown, home, end (and may be other) keys that change the selection by default. You may need to process these events too.
I have a QListWidget and I have set it to accept drops, the mode to be DragDrop etc and I can move QListWidgetItems all around the place (inside the QListWidget of course).
I like this behavior and I want it to let it as is, but I also want my QListWidget to accept drops from a QTreeWidget as well. If I attempt to reimplement the dropEvent() of my QListWidget then I lose the default behavior.
Is there any way to just extend the current behavior so as to have both drag-drop between listwidget items and drag-drop from the treewidget to the listwidget or I have to completely re-write both behaviors in my dropEvent() reimplementation?
Thanks a lot for any answers :)
Two ways:
implement a standalone event filter and make it act upon QEvent::Drop. Install it on your original QListWidget. Return false so that the original dropEvent() handler is called afterwards.
inherit from QListWidget, reimplement dropEvent(evt) and as a last statement, call QListWidget::dropEvent(evt);. Your original behavior will be retained.
No.
Subclass QListWidget, reimplement
virtual void QListWidget::dropEvent ( QDropEvent * event )
and explicitly call
QListWidget::dropEvent(event);
whenever you need the default behavior.
How to call a parent class function from derived class function?
In QListView, i'd like to disable mouse drag multiple selection - that is, mous down on a row, drag the mouse down and select the rows below it while dragging.
I'd still like row selection using CTRL-mouse click.
Is that possible?
It seems you've set the list view's selection mode to QAbstractItemView::MultiSelection. Try setting it to QAbstractItemView::ExtendedSelection with:
listView->setSelectionMode( QAbstractItemView::ExtendedSelection );
and see if that helps.
I think the easiest way to do it would be to create a derived class from the QListView and then override its mouseMoveEvent function. This function in the Qt Code for the QListView looks for a dragging state and creates a rectangle. I think something like this may work, but I didn't test it:
void DerivedListView::mouseMoveEvent(QMouseEvent *e) {
if (state() != DragSelectingState)
QListView::mouseMoveEvent(e);
}