I'm new to QT and I'm having issues when calling QFileDialog from a child window. My app is relatively simple. I have one prompt widget that gets user input and then runs show on its parent.
This is my main.
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
ParentWidjet w(nullptr);
ChildWidget input(&w);
input.show();
return a.exec();
}
This is the relevant section of the child widget:
ChildWidget::ChildWidget(QWidget *parent) :
QDialog(parent),
ui(new Ui::InputPrompt){
ui->setupUi(this);
this->setParent(parent);
}
...
void ChildWidget::on_imagesSelect_clicked() {
inputFilepath = QFileDialog::getExistingDirectory(static_cast<ParentWidget *>(this->parent()), QCoreApplication::translate("main", "Open directory"), "./", QFileDialog::ShowDirsOnly);
ui->inputPath->setPlainText(inputFilepath);
std::cout << "y u exit" << std::endl;
}
//Setup and show the parent
void ChildWidget::on_buttonBox_accepted() {
static_cast<ParentWidjet *>(this->parent())->setup(inputFilepath, outputFilepath);
static_cast<ParentWidjet *>(this->parent())->show();
}
For some reason when QFileDialog is called, closing it with either OK or Cancel closes both the parent and the child. If I don't use it, but click the OK button of the child, which calls the on_buttonBox_accepted() function, the child closes and the parent widget appears as expected. If I don't pass the parent widget to the child widget in the main, QFileDialog no longer closes the child widget when running. I tried changing the parent of QFileDialog to this or to nullptr but that didn't help.
I think my question is similar to QFileDialog closes window when called
or PyQt5 QFileDialog finishes app when called from child window
But i couldn't extract a solution from them that works for me.
Long story short, the second window is not shown, and there is no action waiting to receive the accept call, so my app just dies. This can be prevented if the quit on last window closed property is disabled. Qt forums suggested a solution, together with a few better design choices. This is what I went with in the end.
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
ParentWidget w(nullptr);
ChildWidget input;
if ( input.exec() == QDialog::accepted) // exec() blocks execution until ok/cancel. unlike show()
{
w.setup(input.inputFilepath, input.outputFilepath); // assuming these are public
w.show()
} else
{ // user closed or pressed cancelled
... what u want to do..
}
return a.exec();
}
Related
I am developing a Qt application with multiple windows and want to implement cross-window drag&drop functionality for some elements in my program.
To do so, I attach an event filter to the to-be-dragged QML elements and listen for the MousePress/MouseMove events to start the drag procedure as follows:
QDrag *drag = new QDrag(quickItem);
QMimeData* mimeData = new QMimeData();
mimeData->setText("Test");
drag->setHotSpot(QPoint(0, 0));
drag->setMimeData(mimeData);
drag->exec();
This works fine, but now I would like to show a little tooltip (being a QWidget) while dragging, following the mouse cursor and displaying a short text depending on the element the mouse is currently over (similar to the "Copy to ..." or "Move to..." labels appearing when you drag files around in Windows Explorer).
However, while dragging the element, I don't receive any MouseMove events neither on the QDrag object nor on the quickItem itself, which makes it impossible to track the mouse position. Since the mouse is grabbed during dragging, there should be some event in Qt that frequently reports the mouse position, no matter where on the screen the mouse is.
I am aware of the QDrag::setPixmap method, however this won't allow me to change my tooltip text during dragging and has some other limitations I would like to avoid.
Is there some way to listen to mouse move events while QDrag is running, without using platform-specific system APIs?
Update:
I don't think that there is way of doing this without using OS libraries nor getting the mouse position every X miliseconds. It looks like a really specific problem that Qt framework does not contemplate. You will need to write your own class to control this using win32 for windows, x11 for linux and the equivalent of Mac.
If you want to get the mouse position when your window is active and you are dragging something, check this:
Searching a bit I've found a solution for getting it when your window has the focus using QObject::eventFilter.
Create a class (for example EventListener) that inherits from QObject and overrides eventFilter and a method to set this as your qml window (which inherits from QObject) event filter with installEventFilter.
eventslistener.h:
#include <QEvent>
#include <QObject>
#include <QDebug>
#include <QDropEvent>
class EventsListener : public QObject
{
Q_OBJECT
public:
EventsListener(QObject * ptr) : QObject (ptr) {
}
Q_INVOKABLE void handleEventsOf(QObject *object) {
if (object)
object->installEventFilter(this);
}
bool eventFilter(QObject *object, QEvent *event) override {
if(event->type() == QEvent::DragMove) {
QDragMoveEvent *mouseEvent = static_cast<QDragMoveEvent*>(event);
qDebug() << "Mouse position dragging (x, y): (" << mouseEvent->pos().x() << ", " << mouseEvent->pos().y() << ")";
return false; //this is must return false or drop event will be handled by this method and drag&drop won't work correctly
}
return false;
}
};
Now we need to access to an instance (singleton in this case) of this class with qmlRegisterSingletonType. You may wish to use qmlRegisterType instead to register this eventlistener as a type (instead of a singleton) and use signals to notify directly qml the mouse position.
main.cpp:
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include "eventlistener.h"
static QObject *eventsListenerInstance(QQmlEngine *qmlEngine, QJSEngine *engine)
{
return new EventsListener(engine);
}
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
qmlRegisterSingletonType<EventsListener>("AppEventListener", 1, 0, "EventsListener", eventsListenerInstance);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
main.qml:
import ...
import AppEventListener 1.0
ApplicationWindow {
visible: true
width: 640
height: 480
id: item
property string display
property alias dropEnabled: acceptDropCB.checked
color: dropArea.containsDrag ? "#CFC" : "#EEE"
Component.onCompleted: EventsListener.handleEventsOf(item)
...
}
This was my original question:
I just want to take a screenshot (using the Print key) of my fullscreen QtQuick 2 application. But all I get is a black or sometimes white screenshot. When the application is not started in fullscreen it works.
SOLUTION
I thought I post a really nice solution here,
even though it does not solve the original problem of taking the screenshot with an external tool.
Starting with the suggestion from the accepted answer I did the following:
First I added a signal to my QML main class (in main.qml)
signal takeScreenShot()
This signal is emmited by pressing a certain button.
Then I wrote a C++ / QT class autside the QML code to handle this signal:
class QMLSupplement : public QObject
{
Q_OBJECT
public:
QQuickView* view;
public:
QMLSupplement(QObject* parent = 0);
public slots:
void takeScreenShot();
};
The reference to QQuickView is used to take the screenshot.
void QMLSupplement::takeScreenShot()
{
QString file;
file = QDateTime::currentDateTime().toString("yyyy-MM-dd_hhmmss");
file += ".png";
qDebug() << "taking screenshot, saving here:" << file;
view->grabWindow().save(file);
}
Finally I connect the signal and the slot in main.cpp:
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQuickView view;
view.setSource(QUrl::fromLocalFile("./qml/main.qml"));
view.setResizeMode(QQuickView::SizeRootObjectToView);
QObject* rootObject = (QObject*) view.rootObject();
QMLSupplement supplement;
supplement.view = &view;
QObject::connect(rootObject, SIGNAL(takeScreenShot()),
&supplement, SLOT(takeScreenShot()));
view.show();
// view.showFullScreen();
return app.exec();
}
That's a limitation of the platform where you're running. If you care about this working, you need to implement the functionality yourself. Qt provides you with enough to get the contents of the Qt Quick 2 window and post it to the clipboard as an image.
In your print key handler, if you detect that the window is full-screen, you need to pass the QQuickWindow instance to a helper function:
void grabAndCopy(QQuickWindow * window) {
QApplication::clipboard()->setImage(window->grabWindow());
}
...
if (window->windowState() == Qt::WindowFullScreen) grabAndCopy(window);
I have a Qdialog in which i get some inputs to use on my mainwindow. so it must appear first than mainwindow.
the problem is that my mainwindow does not show up. here's my main.cpp
#include <QtGui/QApplication>
#include "planevolume.h"
#include "dialog.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
Dialog *dialog= new Dialog;
dialog->show();
planevolume mainwindow;
bool dialogcheck = dialog->isHidden();
if (dialogcheck==1)
{
mainwindow.show();
}
else
{
}
return app.exec();
}
I have a pushbutton that when pressed hides the qdialog and if it is hidden than the mainwindow should show up, right?
here's the SLOT i used:
void Dialog::startplanevolume()
{
if (xMax==0 || yMax==0 || zMax==0 || xMMax==0 || yMMax==0 || zMMax==0)
{
ui->label_17->setText("Error: Can't start, invalid measures");
}
else
{
hide();
}
}
the mainwindow can only start after that button is clicked as only then I have the inputs to the main winodw
So the problem here is that calling dialog->show() does not block execution. The minute that call is made, it moves on to the next method. You need to block execution until the user finishes putting input in.
Make your main like this:
QApplication app(argc, argv);
Dialog *dialog= new Dialog;
if ( dialog->exec() ) {
planevolume mainwindow;
mainwindow.show();
return app.exec();
}
return 0;
And in your dialog class, make your method look like:
void Dialog::startplanevolume()
{
if (xMax==0 || yMax==0 || zMax==0 || xMMax==0 || yMMax==0 || zMMax==0)
{
ui->label_17->setText("Error: Can't start, invalid measures");
}
else
{
this->accept(); // close the dialog with a result of 1
}
}
When you press the button, you call your Dialog::startplanevolume, yes, but that's it. You don't go back to the main loop.
If you want to display your mainwindow, you may want to call a planevolume.show() in your Dialog::startplanevolume, just after the hide.
It might be tricky if your objects are in different files, though. So maybe you could define a signal like DialogChecked, emit this signal in your Dialog::startplanevolume (after the hide, of course...), and modify your main so that it would call mainwindow.setVisible(1) when receiving a DialogChecked.
The PushButton action may happen only after app.exec() is called. It makes no sense testing dialog properties before the main loop is entered.
The expected behavior may be reached by setting up the components to start sequentially in an asynchronous way. In Qt world, this means using signals and slots.
connect(dialog, SIGNAL(accept()), &mainwindow, SLOT(show()));
I'm just starting out with Qt on a Mac and working through:
http://doc.qt.nokia.com/4.7/gettingstartedqt.html
When I run the second example which has the following code:
#include <QtGui>
int main(int argv, char **args)
{
QApplication app(argv, args);
QTextEdit textEdit;
QPushButton quitButton("Quit");
QObject::connect(&quitButton, SIGNAL(clicked()), qApp, SLOT(quit()));
QVBoxLayout layout;
layout.addWidget(&textEdit);
layout.addWidget(&quitButton);
QWidget window;
window.setLayout(&layout);
window.show();
return app.exec();
}
The application starts up fine and works fine. The only issue occurs when I click the "Quit" button. When I do that the crash reporter is invoked and osx says the app quit unexpectedly.
Anything obvious I'm doing wrong?
Thanks
The problem is the order of the delete if you declare variables on the stack. The best it to give your object parents so that they can destroy the children.
int main(int argc, char **argv)
{
QApplication app(argc, argv);
QWidget window;
// If window get's destroyed, it will destroy it's children...
QTextEdit textEdit(&window);
QPushButton quitButton("Quit",&window);
QObject::connect(&quitButton, SIGNAL(clicked()), qApp, SLOT(quit()));
QVBoxLayout layout;
layout.addWidget(&textEdit);
layout.addWidget(&quitButton);
window.setLayout(&layout);
window.show();
return app.exec();
}
PS: Did you actually try a debugger to see where it crashes? It will give you an idea ;) My suggestion is to take a working Qt example and play with it.
PS2: The order I created the widgets also prevents the crash...
Try:
QTextEdit * textEdit = new QTextEdit;
QPushButton * quitButton = new QPushButton("Quit");
I'm adding a bunch of QActions to my main window's menus. These actions can also be triggered by the keyboard, and I want the shortcut to be visible in the menu, as usual, e.g.
-----------------
|Copy Ctrl+C|
-----------------
I can do this using QAction.setShortcut(). However, I don't want these QActions to be triggered by the shortcuts; I'm handling all keyboard input separately elsewhere.
Is this possible? Can I disable the shortcut in the QAction but still have the shortcut text (in this example Ctrl + C) in my menus?
EDIT: The way I ended up doing it is connecting to the menu's aboutToShow() and aboutToHide() events, and enabling/disabling the shortcuts so they are only active when the menu is shown. But I'd appreciate a cleaner solution...
You could inherit from QAction and override QAction::event(QEvent*):
class TriggerlessShortcutAction : public QAction
{
public:
...ctors...
protected:
virtual bool event(QEvent* e)
{
if (e->type() == QEvent::Shortcut)
return true;
else
return QAction::event(e);
}
};
This will cause any events of type QEvent::Shortcut sent to your actions to not trigger the 'triggered()' signals.
action.setText("Copy\tCtrl+C");
This will look like an action with a shortcut, but the shortcut is not actually installed.
Here is a full example:
#include <QtGui>
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
QMainWindow win;
QMenu *menu = win.menuBar()->addMenu("Test");
// This action will show Ctrl+T but will not trigger when Ctrl+T is typed.
QAction *testAction = new QAction("Test\tCtrl+T", &win);
app.connect(testAction, SIGNAL(triggered(bool)), SLOT(quit()));
menu->addAction(testAction);
// This action will show Ctrl+K and will trigger when Ctrl+K is typed.
QAction *quitAction = new QAction("Quit", &win);
quitAction->setShortcut(Qt::ControlModifier + Qt::Key_K);
app.connect(quitAction, SIGNAL(triggered(bool)), SLOT(quit()));
menu->addAction(quitAction);
win.show();
return app.exec();
}