I need to process some data AFTER a drop event has occured on a widget.
i.e a user selects some items in list A and drops them in list B. My program needs to compare the 2 lists after B has added the items selected from A.
Any ideas?
Here's a PyQt script that demonstrates two ways to trap drop events:
from PyQt4 import QtGui, QtCore
class Window(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
widget = QtGui.QWidget(self)
self.setCentralWidget(widget)
layout = QtGui.QVBoxLayout(widget)
self.listA = ListWidget(self)
self.listB = QtGui.QListWidget(self)
self.listB.viewport().installEventFilter(self)
for widget in (self.listA, self.listB):
widget.setAcceptDrops(True)
widget.setDragEnabled(True)
for item in 'One Two Three Four Five Six'.split():
widget.addItem(item)
layout.addWidget(widget)
def eventFilter(self, source, event):
if (event.type() == QtCore.QEvent.Drop and
source is self.listB.viewport()):
self.listB.dropEvent(event)
if event.isAccepted():
print 'eventFilter', self.listB.count()
return True
return QtGui.QMainWindow.eventFilter(self, source, event)
class ListWidget(QtGui.QListWidget):
def __init__(self, parent):
QtGui.QListWidget.__init__(self, parent)
def dropEvent(self, event):
QtGui.QListWidget.dropEvent(self, event)
if event.isAccepted():
print 'dropEvent', self.count()
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
The event you're listening for is QDropEvent. To do some work on receipt of the event you'll need to either:
Reimplement QWidget::dropEvent on the target widget or
Use a separate controller widget as an event filter for the drop target (reimplement QObject::eventFilter on the controller and then install it on the target [installEventFilter]).
EDIT: Going into more depth:
Reimplementing dropEvent
Header:
cDropTarget : public QWidget
{
Q_OBJECT
public:
cDropTarget(QWidget *Parent = 0);
protected:
virtual void dropEvent(QDropEvent *event);
}
Implementation:
cDropTarget::cDropTarget(QWidget *Parent)
: QWidget(Parent)
{
setAcceptDrops(true);
}
void cDropTarget::dropEvent(QDropEvent *event)
{
//check that you want to process the drop event
//i.e. its mimeData()->hasFormat(that you can interpret)
//extract mimeData() from the drop event
//do something with it
}
Using a separate event filter
header:
cDropEventFilter : public QObject
{
...
protected:
virtual bool eventFilter(QObject *watched, QEvent *event);
...
}
Implementation:
bool cDropEventFilter::eventFilter(QObject *watched, QEvent *event)
{
if(event->type() == QEvent::DropEnter)
{
QDropEvent *DropEvent = static_cast<QDropEvent*>(event);
if (DropEvent->mimeData()->hasFormat(MIME_TYPE))
{
DropEvent->acceptProposedAction();
return true;
}
}
else
{
return QObject::eventFilter(watched, event);
}
//Handle all events not matching mimeData format MIME_TYPE
event->ignore();
return true;
}
Got it working!
My code for anyone interested:
class MyList(QListWidget):
def __init__(self , parent = None):
super(MyList, self).__init__(parent)
self.setAlternatingRowColors(True)
self.setDragDropMode( QAbstractItemView.InternalMove )
def dropEvent( self , event ):
# get index to insert at
insertPos = event.pos()
fromList = event.source()
insertRow = fromList.row( fromList.itemAt( insertPos ) )
lowestRow = insertRow
for item in fromList.selectedItems():
name = item.text()
sip.delete( item )
listItem = QListWidgetItem( name )
listItem.setTextAlignment( Qt.AlignHCenter )
self.insertItem( insertRow , listItem )
insertRow += 1
self.emit( SIGNAL("stuffDropped") , lowestRow )
event.accept()
Related
I have a dialog with two buttons in the title bar: the context help button and the close button. How can I find out that the user clicked the context help button to perform my custom action? (I want to show some help page in the browser as in VS dialogs.)
I found a similar question, but how to do this with qt?
Context help button behaviour on CPropertySheet
Update.
Now I use the code like this:
class MyHelper : public QObject
{
Q_OBJECT
public:
explicit MyHelper( QObject * parent = nullptr ) {
qApp->installEventFilter( this );
}
protected:
virtual bool eventFilter( QObject * obj, QEvent * ev ) override {
if ( ev->type() == QEvent::EnterWhatsThisMode ) {
showHelp( QApplication::activeWindow() );
return true;
}
return QObject::eventFilter( obj, ev );
}
private:
void showHelp( QWidget * sender ) {
//TODO
}
};
I believe that QWidget::nativeEvent is what you are looking for.
I would like my QTableWidget to trigger the edition callbacks when pressing Enter while editing item BUT I would like the editor to remain activated – like it would just select all of the item’s content like when you start editing the cell.
What is the best way to do this?
Thanks for having a look here.
You should modify the table's item delegate and use event filters to filter out Enter event and implement custom behavior:
class MyDelegate : public QStyledItemDelegate {
public:
MyDelegate(QObject* parent) : QStyledItemDelegate(parent) {}
QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option,
const QModelIndex& index) const {
QWidget* editor = QStyledItemDelegate::createEditor(parent, option, index);
editor->installEventFilter(const_cast<MyDelegate*>(this));
return editor;
}
bool eventFilter(QObject* object, QEvent* event) {
QWidget* editor = qobject_cast<QWidget*>(object);
if (editor && event->type() == QEvent::KeyPress) {
QKeyEvent* key_event = static_cast<QKeyEvent*>(event);
if (key_event->key() == Qt::Key_Return) {
emit commitData(editor); //save changes
QLineEdit* line_edit = qobject_cast<QLineEdit*>(editor);
if (line_edit) {
line_edit->selectAll();
}
return true;
}
}
return false;
}
};
Usage:
ui->tableWidget->setItemDelegate(new MyDelegate(this));
PyQt5 code looks like this:
class GlyphCellItemDelegate(QStyledItemDelegate):
def eventFilter(self, editor, event):
if (event.type() == QEvent.KeyPress and event.key() == Qt.Key_Return):
self.commitData.emit(editor)
# Don't emit closeEditor, select contents instead
editor.selectAll()
return True
return False
For a QFileDialog, is it possible to have either files or directories selectable, the choice being given to user on the same UI (like the way a user selects different filetypes amongst filters and the filelist updates accordingly)?
I have done some research and with some help from IRC ppl I found an easier solution. Basically adding a widget (checkbox, a suitable for this one) and connecting it to the file dialog does the work.
(It's actually someone else's answer which I have improved & furnished. Thanks to him ;). Posting answer here just for reference if someone else stumbles here).
from sys import argv
from PySide import QtGui, QtCore
class MyDialog(QtGui.QFileDialog):
def __init__(self, parent=None):
super (MyDialog, self).__init__()
self.init_ui()
def init_ui(self):
cb = QtGui.QCheckBox('Select directory')
cb.stateChanged.connect(self.toggle_files_folders)
self.layout().addWidget(cb)
def toggle_files_folders(self, state):
if state == QtCore.Qt.Checked:
self.setFileMode(self.Directory)
self.setOption(self.ShowDirsOnly, True)
else:
self.setFileMode(self.AnyFile)
self.setOption(self.ShowDirsOnly, False)
self.setNameFilter('All files (*)')
def main():
app = QtGui.QApplication(argv)
dialog = MyDialog()
dialog.show()
raise SystemExit(app.exec_())
if __name__ == '__main__':
main()
Yes, it is. Here is one way:
In the header, declare your QFileDialog pointer:
class buggy : public QWidget
{
Q_OBJECT
public:
buggy(QWidget *parent = 0);
QFileDialog *d;
public slots:
void createDialog();
void changeDialog();
};
In your implementation, set the QFileDialog::DontUseNativeDialog option (you must do this on Mac OS, but I haven't tested it elsewhere), then override the appropriate window flags to get your dialog to display as you like it.
Finally, add a button (or check box) that calls a function to change the file mode of your QFileDialog:
buggy::buggy(QWidget *){
//Ignore this, as its just for example implementation
this->setGeometry(0,0,200,100);
QPushButton *button = new QPushButton(this);
button->setGeometry(0,0,100,50);
button->setText("Dialog");
connect( button, SIGNAL(clicked()),this,SLOT(createDialog()));
//Stop ignoring and initialize this variable
d=NULL;
}
void buggy::createDialog(void){
d = new QFileDialog(this);
d->setOption(QFileDialog::DontUseNativeDialog);
d->overrideWindowFlags(Qt::Window | Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint | Qt::MacWindowToolBarButtonHint);
QPushButton *b = new QPushButton(d);
connect(b,SIGNAL(clicked()),this,SLOT(changeDialog()));
b->setText("Dir Only");
switch(d->exec()){
default:
break;
}
delete(d);
d=NULL;
}
//FUNCTION:changeDialog(), called from the QFileDialog to switch the fileMode.
void buggy::changeDialog(){
if(d != NULL){
QPushButton *pb = (QPushButton*)d->childAt(5,5);
if(pb->text().contains("Dir Only")){
pb->setText("File Only");
d->setFileMode(QFileDialog::Directory);
}else{
pb->setText("Dir Only");
d->setFileMode(QFileDialog::ExistingFile);
}
}
}
I have dynamically created buttons(QtoolButton) in gridLayout in pyQT.
How can I get the name of the button clicked in the layout?
I can't know the name before hand.
Is there is any trigger to accomplish the task?
Thanks in advance.
You can call self.sender() in a function connected to your button event to get the object that triggered the event. From there you can call the object's objectName() method to get the name.
Here's a quick example - the widget has 10 buttons and clicking on a button will update the label's text to show the button name.
import sys
from PyQt4.QtGui import QApplication, QWidget, QToolButton, QLabel, QVBoxLayout, QHBoxLayout
class Widget(QWidget):
def __init__(self, parent=None):
QWidget.__init__(self, parent)
self.button_layout = QHBoxLayout()
self.widget_layout = QVBoxLayout()
for button_number in xrange(1, 11):
button = QToolButton()
button.setText(str(button_number))
button.setObjectName('Button%d' % button_number)
button.released.connect(self.button_released)
self.button_layout.addWidget(button)
self.status_label = QLabel('No button clicked')
self.widget_layout.addItem(self.button_layout)
self.widget_layout.addWidget(self.status_label)
self.setLayout(self.widget_layout)
def button_released(self):
sending_button = self.sender()
self.status_label.setText('%s Clicked!' % str(sending_button.objectName()))
if __name__ == '__main__':
app = QApplication(sys.argv)
widget = Widget()
widget.show()
sys.exit(app.exec_())
I think you have to implement your iconlabel class derived from QToolButton:
Like This:
class IconLabel : public QToolButton
{
Q_OBJECT
public:
explicit IconLabel(QWidget *parent = 0);
bool event (QEvent* e );
QString name;
signals:
void clicked_signal(QString);
};
bool IconLabel::event (QEvent* e ) {
if ( e->type() == QEvent::Paint) {
return QToolButton::event(e);
}
if(e->type() == QEvent::MouseButtonPress)
{
emit clicked_signal(name);
return true;
}
return true;
}
connect(iconlabel, SIGNAL(clicked_signal(QString)), this, SLOT(getClickedButtonName(QString)));
In QT: I use a class inherited from QToolButton and rewrite event(QEvent*), now I want to add 'mousePressEvent', but it never gets hit, does event(QEvent*) conflict with mousePressEvent(QMouseEvent *) ? Thank you.
bool IconLabel::event (QEvent* e ) {
if ( e->type() == QEvent::Paint) {
return QToolButton::event(e);
}
return true;
}
void IconLabel::mousePressEvent(QMouseEvent* e)
{
int a = 1;//example
a = 2;// example//Handle the event
}
The class is:
class IconLabel : public QToolButton
{
Q_OBJECT
public:
explicit IconLabel(QWidget *parent = 0);
bool event (QEvent* e );
void mousePressEvent(QMouseEvent* e);
signals:
public slots:
};
All events received by a widget pass through event(..), and then are redirected to the appropriate event handler method. You have made the mistake of not forwarding on any events except paint events, if you just want to add mouse press event handling do this:
bool IconLabel::event (QEvent* e ) {
if ( e->type() == QEvent::Paint ||
e->type() == QEvent::QEvent::MouseButtonPress ) {
return QToolButton::event(e);
}
return true;
}
Also event handler methods should really be in protected, because events are only supposed to be distributed via the event queue (QCoreApplication::postEvent(..), etc.).