In our target device, we run our QtE app with -qws argument. To rotate the screen, we specify "-display transformed:rot90" as the app argument and it works well.
However, we have a feature to rotate screen inside the app, so we try below API documented in QScreen:
QWSDisplay::setTransformation(QTransformedScreen::Rot90, 0);
But this API doesn't work at all. It's no error message in the console output.
Does anyone know what's going on about this API? Do we need to enable something else?
Contrary to other qt documentation, documentation for the embedded part of qt is indeed poor. After few days of fiddling with it, I finally managed to solve it.
First thing to do is to compile the library with -qt-gfx-transformed option (along with whatever you need).
Then you compile your application, and start it with the option you already used to activate the transformation driver. I actually started like this :
export QWS_DISPLAY=Transformed:Rot90:0
./app
As a test, I implemented this, to test whether the rotation works :
class X : public QObject
{
Q_OBJECT
public :
X() :
QObject()
{
QTimer *t = new QTimer( this );
connect( t, SIGNAL(timeout()), this, SLOT(OnTimerEvent()));
t->start( 2500 );
}
public slots :
inline void OnTimerEvent()
{
static int v = 0;
++v;
QWSDisplay::setTransformation(v%4);
std::cout<<v<<std::endl;
}
};
So, in the timer slot, I am changing the orientation with QWSDisplay::setTransformation function.
Related
I am fairly new to coding and especially to C++ but usually with enough googling and breaking problems down to simpler blocks I can figure things out. This issue makes no sense to me though and the solution I just happened to come up with on accident makes even less sense to me.
I am writing a program for ESP32-S in vscode with platformio and broke this down to isolate what was causing the error and found this issue with class/object declaration:
This code will compile but I get a link error twice that says
undefined reference to `point::point()'
#include <Arduino.h>
class point {
public:
point();
point(uint day){
this->_day = day;
}
uint _day;
};
class channel {
public:
channel(String _color, byte _pin){
}
point _points[64];
};
channel red("red", 0);
void setup() {}
void loop() {}
Meanwhile this code with one seemingly unrelated change compiles and links without issue:
#include <Arduino.h>
class point {
public:
point();
point(uint day){
this->_day = day;
}
uint _day;
};
class channel {
public:
channel(){ // <--- Removing the arguments from channel constructor fixes it?
}
point _points[64];
};
channel red(); // <--- And here of course
void setup() {}
void loop() {}
I don't know why that fixes it, and I have a workaround for now if this is what I have to do, but I want to understand. Thanks.
You've declared a constructor point::point(), but not defined it (i.e. it has no body). This is not OK with the linker and that's what you're being told.
There are three ways to fix this.
Don't declare the constructor (compiler generates a default constructor which may or may not initialize the member _day to 0, depending on compiler and platform). Note that you also have the interesting alternative of deleting the default constructor.
Declare and define it to do whatever you consider appropriate.
Give your constructor point::point(uint day) a default argument value, e.g.: point::point(uint day = 0).
Side note on C style arrays of C++ objects like here:
point _points[64];
This is a dangerous combination, unless you know exactly what you're doing. See the C++ FAQ
In QML you can use Animator type to "animate on the scene graph's rendering thread even when the UI thread is blocked."
How can I achieve the same thing for Qt Widgets?
Basically, I want something like:
1) start loading screen / splash-screen
2) start GUI-blocking operation
3) stop loading screen / splash-screen
It is not possible to move the ui-blocking operation to a separate thread (Widgets are being created). I cannot modify this part.
I tried QQuickWidget and QQuickView containing the splash-screen scene with an Animator inside but it didn't work - they got blocked as well.
EDIT: In the separate thread I read the file containing the UI description. Then I recursively create hundreds of Widgets (including QQuickWidgets, QLabels for images, Web views etc.).
Point of the question was to see if there is a "workaround" for that (e.g. displaying the aforementioned QML scene in some separate window with an own event loop). Unfortunately at this point not much more can be done about the overall design of the above.
Probably the widgets you're creating do too much work. You have to specify exactly how many widgets you're creating, and how. Show some example code. In general, the GUI thread is for cooperative multitasking - if you have something that "blocks", break it down into tiny chunks. For example, suppose that you're processing some XML or json file to build the UI. You could have that task do it one widget at a time, and be invoked each time the event loop is about to block (i.e. use a zero-duration "timer" and invert control).
You should also do the maximum possible amount of work outside of the gui thread. I.e. the UI description should be read and converted to an efficient representation that encapsulates the work to be done in the main thread. This conversion has to be done asynchronously.
The simplest way to accomplish that is to encapsulate each widget's creation in a lambda that refers to some context object. Such a lambda would have the signature [...](BatchContext &ctx). The vector of those lambdas would be kept by the CreationContext object as well:
class BatchContext : public QObject {
Q_OBJECT
public:
using Op = std::function<void(CreationContext &)>;
using QObject::QObject;
// useful for Op to keep track of where things go
void push(QWidget *w) { m_stack.push_back(w); }
QWidget *pop() { return m_stack.isEmpty() ? nullptr : m_stack.takeLast(); }
QWidget *top() const { return m_stack.isEmpty() ? nullptr : m_stack.last(); }
int stackSize() const { return m_stack.size(); }
bool stackEmpty() const { return m_stack.isEmpty(); }
Q_SLOT void startExec() {
if (m_execIndex < ops.size())
m_execTimer.start(0, this);
}
template <typename F>
void addOp(F &&op) { m_ops.push_back(std::forward<F>(op)); }
...
private:
QVector<Op> m_ops;
QVector<QWidget *> m_stack;
QBasicTimer m_execTimer;
int m_execIndex = 0;
void timerEvent(QTimerEvent *ev) override {
if (ev->timerId() == m_execTimer.timerId())
if (!exec())
m_execTimer.stop();
}
/// Does a unit of work, returns true if more work is available
bool exec() {
if (m_execIndex < m_ops.size())
m_ops.at(m_execIndex++)(*this);
return m_execIndex < ops.size();
}
};
After the context is created asynchronously, it can be passed to the main thread where you can then invoke startExec() and the widgets will be created one-at-a-time. The stack is but an example of how you might implement one aspect of widget creation process - the tracking of what widget is the "current parent".
Is there any way to setTabOrder in QMessageBox without subclassing it or writing my own? In cases when you already got big project - this might be useful.
Is there any way to setTabOrder in QMessageBox without subclassing it
or writing my own? In cases when you already got big project - this
might be useful.
There is a way to use setTabOrder in QMessageBox. All you need is QWidget* pointers to 'from' and 'to' tabs.
class MyApp
{
// ...
void tabOrdering();
QMessagebox* m_pMsgBox;
}
void MyApp::tabOrdering()
{
auto* pSaveBn = m_pMsgBox->addButton(QMessagebox::Save);
m_pMsgBox->setTabOrder(m_pMsgBox->defaultButton(), pSaveBn);
}
You may also consider using QObject::findChild method for finding tab widget stops.
In our target device, we run our QtE app with -qws argument. To rotate the screen, we specify "-display transformed:rot90" as the app argument and it works well.
However, we have a feature to rotate screen inside the app, so we try below API documented in QScreen:
QWSDisplay::setTransformation(QTransformedScreen::Rot90, 0);
But this API doesn't work at all. It's no error message in the console output.
Does anyone know what's going on about this API? Do we need to enable something else?
Contrary to other qt documentation, documentation for the embedded part of qt is indeed poor. After few days of fiddling with it, I finally managed to solve it.
First thing to do is to compile the library with -qt-gfx-transformed option (along with whatever you need).
Then you compile your application, and start it with the option you already used to activate the transformation driver. I actually started like this :
export QWS_DISPLAY=Transformed:Rot90:0
./app
As a test, I implemented this, to test whether the rotation works :
class X : public QObject
{
Q_OBJECT
public :
X() :
QObject()
{
QTimer *t = new QTimer( this );
connect( t, SIGNAL(timeout()), this, SLOT(OnTimerEvent()));
t->start( 2500 );
}
public slots :
inline void OnTimerEvent()
{
static int v = 0;
++v;
QWSDisplay::setTransformation(v%4);
std::cout<<v<<std::endl;
}
};
So, in the timer slot, I am changing the orientation with QWSDisplay::setTransformation function.
Am I doing it right?
A client of mine has a group where I'm developing Qt-based client-server stuff with a lot of fun widget stuff and sockets.
Another group within the company wants to use a wrapped version of the QTcpSocket-based client data provider classes. (Which does basically what it sounds like, provides data from the server to the client displays)
However, that group has a huge application built mostly with MFC, and that is simply not going to change any time soon. The Qt-based DLL is also delay-loading so that it can be deployed without this feature in certain configurations.
I've got it working, but it's a little hacky. Here's my solution at the moment:
The DLL wrapper class constructor calls QCoreApplication::instance() to see if it's NULL or not. If it's NULL, it assumes it's in a non-Qt app, and creates a QCoreApplication instance of it's own:
if (QCoreApplication::instance() == NULL)
{
int argc = 1;
char* argv[] = { "dummy.exe", NULL };
d->_app = new QCoreApplication(argc, argv); // safe?
}
else
d->_app = NULL;
It then will set up a windows timer to occasionally call processEvents():
if (eventTimerInterval > 0)
{
// STATE: start a timer to occasionally process the Qt events in the event queue
SetTimer(NULL, (UINT_PTR)this, eventTimerInterval, CDatabaseLayer_TimerCallback);
}
The callback simply calls the processEvents() function using the timerID as a pointer to the class instance. The SetTimer() docs say when HWND is NULL it ignores the timerID, so this appears to be perfectly valid.
VOID CALLBACK BLAHBLAH_TimerCallback(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
{
((BLAHBLAH*)idEvent)->processEvents(); // basically just calls d->_app->processEvents();
}
I then destroy the QCoreApplication instance as the very last thing in the destructor.
BLAHBLAH::~BLAHBLAH()
{
.. other stuff
QCoreApplication* app = d->_app;
d->_app = NULL;
delete d;
if (app != NULL)
delete app;
}
If the hosting application wishes to time the calls to processEvents() itself, it can pass 0 in for eventTimerInterval and call BLAHBLAH::processEvents() itself.
Any thoughts on this? Porting that app to Qt is not an option. It's not ours.
It appears to work, but there are probably several assumptions being broken here. Can I just construct a QCoreApplication with dummy arguments like that? Is the event queue safe to operate in this manner?
I don't want this blowing up in my face later. Thoughts?
Studying the Qt code it seems QCoreApplication is needed to dispatch system-wide messages such as timer events. Things like signal/slots and even QThreads do not depend on it unless they are related to those system-wide messages. Here is how I do this in a shared library (in a cross platform way using Qt itself) and I actually do call exec, because processEvents() alone does not process everything.
I have a global namespace:
// Private Qt application
namespace QAppPriv
{
static int argc = 1;
static char * argv[] = {"sharedlib.app", NULL};
static QCoreApplication * pApp = NULL;
static QThread * pThread = NULL;
};
I have an OpenApp method in a QObject (that is moc'ed) like this:
// Initialize the app
if (QAppPriv::pThread == NULL)
{
// Separate thread for application thread
QAppPriv::pThread = new QThread();
// Direct connection is mandatory
connect(QAppPriv::pThread, SIGNAL(started()), this, SLOT(OnExec()), Qt::DirectConnection);
QAppPriv::pThread->start();
}
And here is OnExec slot:
if (QCoreApplication::instance() == NULL)
{
QAppPriv::pApp = new QCoreApplication(QAppPriv::argc, QAppPriv::argv);
QAppPriv::pApp->exec();
if (QAppPriv::pApp)
delete QAppPriv::pApp;
}
So far it seems to be working fine, I am not sure if I need to delete the app at the end, I will update my answer if I find something.
The Qt Documentation for 4.5.2 says that the arguments to QCoreApplication need to have lifetimes as long as the application object - so you shouldn't really use local variables.
Apart from that little thing:
I'm grappling with the same problem, and everything seems to work for me too. I would recommend being very careful at unload / exit time, however, since if you're using the event loop from another application and that event loop is stopped before your library is unloaded then all sorts of crashy nastiness can happen when you try to close() sockets and delete QObjects.