My application is designed in that way that different plugins can set the central widget of the main windows to show the desired content.
This works so far.
But if I set a QQuickView-WindowContainer as central widget, the app does not quit when I close the main window.
If I set a "normal" widget like QPushButton as central widget the appliation quits just fine. Why is that?
This is the code of a minimal example which shows this behaviour (MainWindow is a class created from the QtCreator wizard):
class AppCore : public QObject
{
Q_OBJECT
public:
explicit AppCore(QObject *parent = 0);
signals:
public slots:
void showMainWindow();
private:
MainWindow *m_mainWindow;
};
AppCore::AppCore(QObject *parent) :
QObject(parent)
{
}
void AppCore::showMainWindow()
{
QQuickView *view;
QWidget *container;
view = new QQuickView();
container = QWidget::createWindowContainer(view);
view->setSource(QUrl("qrc:/main.qml"));
m_mainWindow = new MainWindow();
//m_mainWindow->setCentralWidget(new QPushButton("Button"));
m_mainWindow->setCentralWidget(container);
m_mainWindow->show();
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
AppCore appCore;
appCore.showMainWindow();;
return a.exec();
}
This looks like a bug. I see a dead lock in debugger: v8::internal::RuntimeProfiler::WaitForSomeIsolateToEnterJS and QQmlDataLoader::shutdownThread wait for each other. I can't find a good workaround for this issue.
I found a dirty hack that solved the issue. If container is deleted a bit earlier, all works ok:
void MainWindow::closeEvent(QCloseEvent *e) {
QMainWindow::closeEvent(e);
if (e->isAccepted() && centralWidget()) {
delete centralWidget();
}
}
You probably should send a bug report. Note that m_mainWindow is not needed to reproduce the issue. Using container->show(); gives the same result.
Related
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.
I am using Qt5 with new signal/slot syntax.
I don't know why the following code doesn't work:
QWidget *widget = new QWidget();
connect(pipeline, &Pipeline::NewFrame, widget, &QWidget::update);
I get the error:
no matching member function for call to 'connect' why?
Pipeline class inherits from QObject and NewFrame signal has the same signature as QWidget::update
class Pipeline
: public QObject
{
Q_OBJECT
Q_DISABLE_COPY(Pipeline)
public:
Pipeline(QObject *parent);
signals:
void NewFrame();
};
I am using QtCreator on Arch Linux with g++.
TL;DR: The pipeline should be signaling an image, and the widget should have a SetImage method:
class Pipeline : public QObject {
Q_OBJECT // important
public:
Q_SIGNAL void NewFrame(const QImage &);
...
};
class Viewer : public QWidget {
Q_OBJECT // important
QImage m_image;
public:
Q_SLOT void SetImage(const QImage &image) {
m_image = image;
update();
}
...
};
This is how you'd be using it - note that Viewer knows nothing about Pipeline, because it shouldn't: it just shows new frames, wherever they come from.
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
Pipeline pipeline;
Viewer viewer;
QObject::connect(&pipeline, &Pipeline::NewFrame, &viewer, &Viewer::SetImage);
return app.exec();
}
Connecting anything to QWidget::update directly, especially from external sources, is usually a sign of bad design.
To satisfy your curiosity, you can use a lambda or qOverload to specify what you're connecting to, to fix the very error you're seeing - caused by ambiguity of the the type of the method pointer. Any of the following will work:
connect(…, widget, qOverload<>(&QWidget::update));
or
auto constexpr update = qOverload<>(&QWidget::update));
connect(…, widget, update);
or
connect(…, widget, [widget]{ widget->update(); });
I have a QML app in which I have subclassed QApplication to create my main screen with QML. The issue i have is on clicking Close button the application closes as intended, but I want to handle a situation where if some services are running I want to override close button behaviour.
I tried overriding closeEvent() without any luck. Can anyone point me to some ways I can handle this?
UPDATE : This is the code snippet I tried
class SingleApplication : public QApplication {
Q_OBJECT
public:
SingleApplication(int &argc, char **argv);
void closeEvent ( QCloseEvent * event )
{
event->ignore();
}
}
MAIN.cpp
#include "view.h"
#include <QDebug>
#include <QDesktopWidget>
#include "SingleApplication.h"
int main(int argc, char *argv[])
{
SingleApplication app(argc, argv);
if(!app.isRunning()) {
app.processEvents();
View view(QUrl("qrc:/qml/main.qml"));
#ifdef Q_OS_LINUX
view.setFlags(Qt::WindowMinimizeButtonHint|Qt::WindowCloseButtonHint);
#endif
view.setMaximumSize(QSize(1280,700));
view.setMinimumSize(QSize(1280,700));
// Centering the App to the middle of the screen
int width = view.frameGeometry().width();
int height = view.frameGeometry().height();
QDesktopWidget wid;
int screenWidth = wid.screen()->width();
int screenHeight = wid.screen()->height();
view.setGeometry((screenWidth/2)-(width/2),(screenHeight/2)-(height/2),width,height);
view.show();
return app.exec();
}
return 0;
}
There is no QApplication::closeEvent. Such virtual function belongs to QWidget.
Use of QApplication indicated that you have normal QWidget container for your QML UI (as you say UI is based on QML though). You should rather override that widget closeEvent e.g.:
class MyMainWidget : public QWidget // or is it QMainWindow?
{
// snip
private:
void closeEvent(QCloseEvent*);
}
void MyMainWidget::closeEvent(QCloseEvent* event)
{
// decide whether or not the event accepted
if (condition())
event->accept();
}
And if your container widget is not overridden yet (simply QWidget?), well, now you have to do so.
And you did not say whether or not you want to keep app window running. I assume you want that as well.
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);
/EDIT: solved, see my comment in the 1st answer!/
I am currently building an application which only has a tray icon displayed, so it doesn't have any windows.
Well, in the tray icon I've included a QAction so as to close the application. The thing is, that I get seg fault when I call exit(0); from that function. This is some example code:
//I have a reason for setting it to be a QTimer, please don't even comment on this
class Boot_Timer : public QTimer {
Q_OBJECT
public:
explicit Boot_Timer(QObject *parent = 0) : QTimer(parent) {
}
public Q_SLOTS:
void set_up_command_line_tray(){
//Setting up the tray Icon.
QSystemTrayIcon *trayIcon_cmd = new QSystemTrayIcon(this);
trayIcon_cmd->setIcon(QIcon(":/icons/Pictures/myapp.png"));
trayIcon_cmd->setToolTip("My tray tooltipp");
QMenu *changer_menu = new QMenu;
QAction *Quit_action = new QAction(tr("&Quit"), this);
Quit_action->setIconVisibleInMenu(true);;
connect(Quit_action, SIGNAL(triggered()), this, SLOT(close_application()));
changer_menu->addAction(Quit_action);
trayIcon_cmd->setContextMenu(changer_menu);
trayIcon_cmd->show();
}
void close_application(){
//HERE I GET SEG FAULT
exit(0);
}
};
Boot_Timer boottimer;
#include "main.moc"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
//making some checks (code omitted)
...
boottimer.set_up_command_line_tray()
return app.exec();
}
So, the tray icon is shown normally and perfectly, but when I choose to Quit the application using the menu I've added to the tray icon, I get a seg fault. I guess that I cannot quit the application using exit(int state) outside main() function and its functions that don't have a parent...
What is the correct way to quit my application, then?
Thanks in advance for any answers!
Try to call
qApp->quit(0);
instead of
exit(0);
Remember to #include <QApplication>.
Thanks, that didn't solve it. For some reason, the thing that solved it was to do the following: QSystemTrayIcon *trayIcon_cmd = new QSystemTrayIcon(0); instead of QSystemTrayIcon *trayIcon_cmd = new QSystemTrayIcon(this)