QEvent::Drop event is never generated for QQuickView window - qt

I'm struggling with an issue that QEvent::Drop event is never generated for my QQuickView window.
I need to implement a drag'n'drop functionality, to drop files from explorer to the QQuickView.
As described in this post, i've istalled an eventfilter for the QQuickView objet, and in eventFilter() method trying to catch required events. The QEvent::DragMove is being generated as expected, as i drag a file over the view. But when i drop the file on the view, the QEvent::Drop is not being generated. Instead, the QEvent::DragLeave is generated.
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
Filter f;
QQuickView *view = new QQuickView;
view->installEventFilter(&f);
view->show();
return a.exec();
}
And here is a (Event)Filter class code:
(header)
class Filter : public QObject
{
Q_OBJECT
public:
Filter(){};
virtual bool eventFilter(QObject *watched, QEvent *event) Q_DECL_OVERRIDE;
};
(source)
bool Filter::eventFilter(QObject *watched, QEvent *event)
{
if(event->type() == QEvent::DragMove)
qDebug() << "it's a drag";
if(event->type() == QEvent::Drop)
qDebug() << "it's a drop"; // <<-- Never reaches here
return QObject::eventFilter(watched, event);
}

My colleagues helped me out with this question.
Apparently you have to add a DropArea item to your QML root file, and after that the QQuickView will start receiving QEvent::Drop events.
I can not find any documentation on this case though, and i also wonder what would be more general solution for this, if you had a QWindow class instead.
Anyway, I'm closing this question.

Related

Is it possible to change default behavior of a checkable QGroupBox?

The question is pretty straight forward: is it possible to change the default behavior of a checkable QGroupBox object? I designed a user interface with many QLineEdit objects inside a checkable QGroupBox, the desired behavior is: when QGroupBox is not checked all of its children are enable and when it is checked all of its children are disable.
As you can see at the oficial QGroupBox documentation, it says:
If the check box is checked, the group box's children are enabled; otherwise, the children are disabled and are inaccessible to the user.
One trick is to modify the painting so that when it is checked the check is not shown and vice versa:
#include <QtWidgets>
class GroupBox: public QGroupBox{
public:
using QGroupBox::QGroupBox;
protected:
void paintEvent(QPaintEvent *){
QStylePainter paint(this);
QStyleOptionGroupBox option;
initStyleOption(&option);
if(isCheckable()){
option.state &= ~(isChecked() ? QStyle::State_On : QStyle::State_Off);
option.state |= (isChecked() ? QStyle::State_Off : QStyle::State_On);
}
paint.drawComplexControl(QStyle::CC_GroupBox, option);
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
GroupBox groupBox;
groupBox.setCheckable(true);
QVBoxLayout *vbox = new QVBoxLayout;
vbox->addWidget(new QLineEdit);
vbox->addWidget(new QLineEdit);
vbox->addWidget(new QLineEdit);
vbox->addStretch(1);
groupBox.setLayout(vbox);
groupBox.show();
return a.exec();
}

QTextEdit refresh after setText()/insertPlainText()

I have a QTextEdit widget in a private slot, which I update regularly with setText() and insertPlainText().
I have found that setText()/insertPlainText() does not update the QTextEdit widget immediately. Instead, the QTextWidget is updated when the slot function returns. To test this, I have put a sleep() just after the setText()/insertPlainText().
class MyWindow : public Widget
{
MyWindow()
{
my_button = new QPushButton(this);
my_edit = new QTextEdit(this);
connect(my_button,
&QPushButton::clicked,
this,
&MyWindow::my_callback);
}
private slots:
void my_callback()
{
my_edit->setText("sample text");
// nothing happens; the QTextEdit
// widget does not show "sample text"
sleep(10);
// the QTextEdit widget will show
// "sample text" AFTER the sleep,
// when my_callback returns.
}
private:
QPushButton* my_button;
QTextEdit* my_edit;
}
This is a problem for me because I need to print a message in my QTextEdit widget BEFORE launching a time-consuming process (using QProcess). Currently, this message is not being printed until after QProcess process has returned.
Does anyone know how I can get the QTextEdit widget to show its contents right after setText()/insertPlainText()?
Using Qt5 on Fedora 29.
Never execute a task that consumes a lot of time in the GUI thread. In general, the solution is to execute that task in another thread, but in your case it indicates that you use QProcess, so I assume that you are using one of the methods waitForFinished(), waitForStarted() or waitForReadyRead(), instead you should use the signals:
#include <QtWidgets>
class Widget: public QWidget{
Q_OBJECT
public:
Widget(QWidget *parent=nullptr):
QWidget(parent)
{
button.setText("Press me");
QVBoxLayout *lay = new QVBoxLayout{this};
lay->addWidget(&button);
lay->addWidget(&textedit);
connect(&button, &QPushButton::clicked, this, &Widget::onClicked);
connect(&process, &QProcess::readyReadStandardError, this, &Widget::onReadyReadStandardError);
connect(&process, &QProcess::readyReadStandardOutput, this, &Widget::onReadAllStandardOutput);
}
private Q_SLOTS:
void onClicked(){
textedit.setText("sample text");
process.start("ping 8.8.8.8");
}
void onReadyReadStandardError(){
textedit.append(process.readAllStandardError());
}
void onReadAllStandardOutput(){
textedit.append(process.readAllStandardOutput());
}
private:
QPushButton button;
QTextEdit textedit;
QProcess process;
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
#include "main.moc"
I wonder if calling
QCoreApplication::processEvents()
right after the ->setText("sample text") would do the trick in your case.

QLabel click event using Qt?

I'm new in Qt and have a question.
I have QLabel and QLineEdit objects, and when QLabel text is clicked on, I want to set this text in QLineEdit.
Also I have read that QLabel has not clicked signal.
Can you explain how can I do this and write code for me ?!
Either style another type of QWidget such as a specific QPushButton to look like a QLabel and use its clicked() signal or inherit QLabel yourself and emit your own clicked() signal.
See this example:
https://wiki.qt.io/Clickable_QLabel
If you choose the latter option you can pass the text in the signal. Then connect the necessary signals/slots up between the QLabel and the QLineEdit like so:
QObject::connect(&label, SIGNAL(clicked(const QString& text)),
&lineEdit, SLOT(setText(const QString& text)));
A simple way to accomplish that, without a need for any subclassing, is a signal source that monitors the events on some object and emits relevant signals:
// main.cpp - this is a single-file example
#include <QtWidgets>
class MouseButtonSignaler : public QObject {
Q_OBJECT
bool eventFilter(QObject * obj, QEvent * ev) Q_DECL_OVERRIDE {
if ((ev->type() == QEvent::MouseButtonPress
|| ev->type() == QEvent::MouseButtonRelease
|| ev->type() == QEvent::MouseButtonDblClick)
&& obj->isWidgetType())
emit mouseButtonEvent(static_cast<QWidget*>(obj),
static_cast<QMouseEvent*>(ev));
return false;
}
public:
Q_SIGNAL void mouseButtonEvent(QWidget *, QMouseEvent *);
MouseButtonSignaler(QObject * parent = 0) : QObject(parent) {}
void installOn(QWidget * widget) {
widget->installEventFilter(this);
}
};
The emit keyword is an empty macro, Qt defines it as follows:
#define emit
It is for use by humans as a documentation aid prefix only, the compiler and moc ignore it. As a documentation aid, it means: the following method call is a signal emission. The signals are simply methods whose implementation is generated for you by moc - that's why we have to #include "main.moc" below to include all the implementations that moc has generated for the object class(es) in this file. There's otherwise nothing special or magical to a signal. In this example, you could look in the build folder for a file called main.moc and see the implementation (definition) of void MouseButtonSignaler::mouseButtonEvent( .. ).
You can then install such a signaler on any number of widgets, such as a QLabel:
int main(int argc, char ** argv) {
QApplication app(argc, argv);
MouseButtonSignaler signaler;
QWidget w;
QVBoxLayout layout(&w);
QLabel label("text");
QLineEdit edit;
layout.addWidget(&label);
layout.addWidget(&edit);
signaler.installOn(&label);
QObject::connect(&signaler, &MouseButtonSignaler::mouseButtonEvent,
[&label, &edit](QWidget*, QMouseEvent * event) {
if (event->type() == QEvent::MouseButtonPress)
edit.setText(label.text());
});
w.show();
return app.exec();
}
#include "main.moc"
You need to create one Custom Label class, which will inherit QLabel. Then you can use MouseButtonRelease event to check clicking of Label and emit your custom signal and catch in one SLOT.
Your .h file will be as below:
class YourLabelClass : public QLabel{
signals:
void myLabelClicked(); // Signal to emit
public slots:
void slotLabelClicked(); // Slot which will consume signal
protected:
bool event(QEvent *myEvent); // This method will give all kind of events on Label Widget
};
In your .cpp file, your constructor will connect signal & slot as below :
YourLabelClass :: YourLabelClass(QWidget* parent) : QLabel(parent) {
connect(this, SIGNAL(myLabelClicked()), this, SLOT(slotLabelClicked()));
}
Remaining event method and SLOT method will be implemented as below:
bool YourLabelClass :: event(QEvent *myEvent)
{
switch(myEvent->type())
{
case(QEvent :: MouseButtonRelease): // Identify Mouse press Event
{
qDebug() << "Got Mouse Event";
emit myLabelClicked();
break;
}
}
return QWidget::event(myEvent);
}
void YourLabelClass :: slotLabelClicked() // Implementation of Slot which will consume signal
{
qDebug() << "Clicked Label";
}
For Changing a Text on QLineEdit, you need to create a Custom Class and share object pointer with custom QLabel Class. Please check test code at this link
In the above example the header needs Q_OBJECT:
class YourLabelClass : public QLabel{
Q_OBJECT
signals:

QListView show text when list is empty

I want to show some text (like "No items") when there are no items in QListView.
I tried to override paintEvent method of QListView, but it doesn't have any effect.
The code below shows a simple way of doing it by overloading the paintEvent method of the view. Painting of the text should probably use the style mechanism to obtain the font and pen/brush, but I'll leave that up for grabs by a keen editor.
It uses Qt 5 and its C++11 features, doing it the Qt 4 or pre-C++11 way would require a QObject-deriving class with a slot to connect to the spin box's valueChanged signal. The implementation of ListView doesn't need to change between Qt 4 and Qt 5.
#include <QtWidgets>
class ListView : public QListView {
void paintEvent(QPaintEvent *e) {
QListView::paintEvent(e);
if (model() && model()->rowCount(rootIndex()) > 0) return;
// The view is empty.
QPainter p(this->viewport());
p.drawText(rect(), Qt::AlignCenter, "No Items");
}
public:
ListView(QWidget* parent = 0) : QListView(parent) {}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWidget window;
QFormLayout layout(&window);
ListView view;
QSpinBox spin;
QStringListModel model;
layout.addRow(&view);
layout.addRow("Item Count", &spin);
QObject::connect(&spin, (void (QSpinBox::*)(int))&QSpinBox::valueChanged,
[&](int value){
QStringList list;
for (int i = 0; i < value; ++i) list << QString("Item %1").arg(i);
model.setStringList(list);
});
view.setModel(&model);
window.show();
return a.exec();
}
If you use a QListView, you probably have a custom model containing data you want to display. This is maybe the best place to returns "No items" when your model is empty.

Adding QTreeView to QMainWidow - strange behavior

Using the TreeModel from this tutorial:
http://qt-project.org/doc/qt-4.8/itemviews-simpletreemodel.html
this version works (shows the tree):
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
MainWindow mainWin;
QFile file("default.txt");
file.open(QIODevice::ReadOnly);
TreeModel model(file.readAll());
file.close();
QTreeView *treeView = new QTreeView(mainWin.splitter);
treeView->setModel(&model);
mainWin.show();
return app.exec();
}
class MainWindow: public QMainWindow {
QSplitter* splitter;
public:
MainWindow() : QMainWindow() {
splitter = new QSplitter(Qt::Horizontal, this);
splitter->setMinimumSize(1000,1000);
}
};
and this one doesn't work:
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
MainWindow mainWin;
mainWin.show();
return app.exec();
}
class MainWindow: public QMainWindow {
QSplitter* splitter;
public:
MainWindow() : QMainWindow() {
splitter = new QSplitter(Qt::Horizontal, this);
splitter->setMinimumSize(1000,1000);
QFile file("default.txt");
file.open(QIODevice::ReadOnly);
TreeModel model(file.readAll());
file.close();
QTreeView *treeView = new QTreeView(splitter);
treeView->setModel(&model);
}
};
What happens here? Is there some API misuse / undefined behavior?
I assume that with "doesn't work" you mean that the tree view stays empty.
You create the TreeModel model on the stack. That means it will be destroyed at the end of the block. In the first case, that's not a problem, because main() won't be exited before the application quits (i.e. app.exec() returns).
In the second case it is a problem, as the model will be destroyed right away at the end of the MainWindow constructor, i.e. right away, before the window is even visible.
You must create the model either on the heap (don't forget about memory management then - pass the main window (this) as parent) or make it a member variable.

Resources