PDA

View Full Version : Need some help porting from Qt3 to Qt4



psadhukhan
25th November 2008, 08:04
Hi,

I am a newbie to Qt world. We have a product which follows Java AWT model (which is multithread enabled in that from any Java thread, the AWT libs will try to make GUI calls) and was working in Qt3 but it seems that the new Qt4 model is single threaded and wants all GUI calls to be only made from the main thread [the thread that calls exec()] as the tutorial seems to imply
Note that QCoreApplication::exec() must always be called from the main thread (the thread that executes main()), not from a QThread. In GUI applications, the main thread is also called the GUI thread because it's the only thread that is allowed to perform GUI-related operations.

Also, there is one more change in Qt4 w.r.t Qt3
When implementing custom widgets in Qt 3, it was possible to use QPainter to draw on a widget outside paint events
In Qt 4, it is only possible to paint on a widget from within its paintEvent() handler function. This restriction simplifies Qt's interaction with native window systems, improves the performance of applications by reducing the number of redraw operations, and also enables features to be implemented to improve the appearance of widgets, such as a backing store. Generally, we recommend redesigning applications to perform all painting operations in paintEvent() functions, deferring actual painting until the next time this function is called. Applications can post paint events to trigger repaints, and it may be possible to examine your widget's internal state to determine which part of the widget needs to be repainted

so, in Qt3, we did not have any paintEvent() method and whenever AWT lib calls any GUI related function like drawLine, drawRect, drawFont, drawImage it used to draw the shapes in the Qt window. One such example:


AWT_drawLine(int x1, int y1, int x2, int y2)
{
AWT_QT_LOCK; {
QPainter p(QtImageDescPool[QtGraphDescPool[qtGraphDesc].qid].qpd);
#ifdef QT_VER_4
p.setCompositionMode(QtGraphDescPool[qtGraphDesc].rasterOp);
#else
p.setRasterOp(QtGraphDescPool[qtGraphDesc].rasterOp);
#endif
p.setPen(*((QPen *)(QtGraphDescPool[qtGraphDesc].qp)));
setPainterClip(&p, qtGraphDesc);
p.drawLine(x1, y1, x2, y2);

printf("DrawLine called\n");
if(maskPainter(qtGraphDesc, p))
p.drawLine(x1, y1, x2, y2);
}
AWT_QT_UNLOCK;
}

In Qt3 [when QT_VER_4] was not defined, it used to work as paintEvent was not needed. But, I guess in Qt4, this way of execution will not display anything. I have created a native application which does the drawing in paintEvent() and the application work provided paintEvent is present. But, it's difficult to satisfy our AWT model if we have to draw in paintEvent()

I was thinking of the thread that executes exec() to draw all the GUI operations on offscreen surface[back buffer] and in paintEvent, periodically display the back buffer into the Qt screen.
Will that be a good way of doing it in Qt4? If not, can you please suggest some way?
If yes, can you please tell me how to create an offscreen [back buffer] [will that be a Qwidget] and how can we flush the back buffer into Qt screen?
Can you please tell me how can I modify the attached application so that drawline() function will draw the line in back buffer and paintEvent will take the back-buffer and flush on the Qt screen?

Thanks in advance.
Regards
Prasanta

The native application:


#include <Qt>
#include <QtCore>
#include <QtGui>
#include <QPainter>
#include <QWidget>

class QPen;
class QBrush;

#define qtApp ((QtApplication *)qApp)
void drawline(QPaintDevice *qpd);

class QtWindow : public QWidget
{
QBitmap transMask;

public:
QtWindow(int flags,
const char *name = "Sun AWT/Qt",
QWidget *parent = NULL) ;
virtual void paintEvent(QPaintEvent *);

};

class QtApplication : public QApplication {
public :
QtApplication(int &argc, char **argv);
int exec(); //overloadded exec from QApplication
};

QtApplication::QtApplication(int &argc, char **argv) : QApplication(argc, argv) {
}

int
QtApplication::exec(void) {
printf("QApplication exec called\n");
QApplication::exec();
printf("QApplication exec done\n");
}

QtWindow::QtWindow(int flags, const char *name, QWidget *parent) :
QWidget(parent, (Qt::WindowFlags)flags)
{
setMouseTracking(true);
}

void
QtWindow::paintEvent(QPaintEvent *event)
{
#if 0
printf("paintEvent called\n");
QPen *qp = new QPen();
QBrush *qb = new QBrush();
QPainter p(this);
p.setCompositionMode(QPainter::CompositionMode_Sou rceOver);
p.setPen(*qp);
p.setClipRect(0,0,800,600);
p.drawLine(30,135,790,135);
#endif
}

int main(int argc, char **argv)
{
printf("argc %d, argv %s\n",argc, *argv);
int m_x = 0,m_y=0,m_width=800,m_height=600;

new QtApplication(argc, argv);
QtWindow *m_window = new QtWindow(Qt::FramelessWindowHint|Qt::Window);
QBitmap bitmap(m_width, m_height);
qtApp->setActiveWindow(m_window);

QPaintDevice *qpd = m_window;
#if 1
drawline(qpd);
#endif
printf("exec called\n");
m_window->show();
qtApp->exec();
}

void
drawline(QPaintDevice *qpd)
{
QPen *qp = new QPen();
QBrush *qb = new QBrush();
QPainter p(qpd);
p.setCompositionMode(QPainter::CompositionMode_Sou rceOver);
p.setPen(*qp);
p.setClipRect(0,0,800,600);
p.drawLine(30,135,790,135);
}

jacek
26th November 2008, 23:26
was working in Qt3 but it seems that the new Qt4 model is single threaded and wants all GUI calls to be only made from the main thread [the thread that calls exec()] as the tutorial seems to imply
Qt3 has single-threaded GUI as well. It was working just because of those lock/unlock macros.


so, in Qt3, we did not have any paintEvent() method and whenever AWT lib calls any GUI related function like drawLine, drawRect, drawFont, drawImage it used to draw the shapes in the Qt window.
I wonder how your application behaves when the user drags another window over it.

Anyway, to solve your problem you might consider event-driven solution. You can make all those AWT_drawSomething functions emit a signal through a queued connection to the main thread that will draw something on a pixmap and schedule an update.

srikanthch
27th November 2008, 08:52
Hi


I am new to the Qt environment.i am developing an application in Qt 4.4 under windows platform.i want my application to run when my system reboots or restarts.for this i have to load my application(.exe or .ini files) into windows registry.can i have some code snippets for this.

Thanks & regards,
Srikanthch
srikanthc49@gmail.com

psadhukhan
27th November 2008, 10:02
Thanks jacek
I was trying an approach whereby I will use QImage [cannot use QPixmap as I guess painting into QPixmap is not allowed from nonGUI thread] as my offscreen buffer and tried painting into that and in paintEvent trying to flush QImage into the Qt window.
Do you think this approach will work?

I am attaching the native application I tried to modify to use this approach

#include <pthread.h>
#include <Qt>
#include <QtCore>
#include <QtGui>

#include <QPainter>
#include <QWidget>
#include <QImage>

class QPen;
class QBrush;
class QImage;

#define qtApp ((QtApplication *)qApp)
void threadfunc(void *arg);
void AWT_drawline(int x1, int y1, int x2, int y2);
void getQtColorModel();
void Qtexec();


QImage img(800,600,QImage::Format_ARGB32);

class QtWindow : public QWidget
{
QBitmap transMask;

public:
QtWindow(int flags,
const char *name = "Sun AWT/Qt",
QWidget *parent = NULL) ;
virtual void paintEvent(QPaintEvent *);

};

class QtApplication : public QApplication {
public :
QtApplication(int &argc, char **argv);
int exec(); //overloadded exec from QApplication
};

class QtScreen
{
protected :
int m_x;
int m_y;
int m_width;
int m_height;
QtWindow *m_window;
bool m_bounds_restricted;
/* virtual char **getArgs(int *argc);
virtual void computeBounds();
virtual void createQtWindow();
virtual void windowShown();
*/

public :
QtScreen();
bool init();
/* void close();
void showWindow();
void setMouseCursor(int cursor);
void beep();
int width();
int height();
int dotsPerInch();
QtWindow *window();
int x();
int y();
*/
};

class QtScreenFactory
{
static QtScreen *theScreen;
public:
static QtScreen *getScreen();
};


QtWindow *m_window;
QtScreen *QtScreenFactory::theScreen = NULL;
QtApplication::QtApplication(int &argc, char **argv) : QApplication(argc, argv) {
}

int
QtApplication::exec(void) {
printf("QApplication exec called\n");
QApplication::exec();
printf("QApplication exec done\n");
}

QtWindow::QtWindow(int flags, const char *name, QWidget *parent) :
QWidget(parent, (Qt::WindowFlags)flags)
{
setMouseTracking(true);
}

void
QtWindow::paintEvent(QPaintEvent *event)
{
printf("paintEvent called\n");
#if 0
QPen *qp = new QPen();
QColor c(0xffff0000);
qp->setColor(c);
QBrush *qb = new QBrush();
QPainter p(this);
p.setCompositionMode(QPainter::CompositionMode_Sou rceOver);
p.setPen(*qp);
p.setClipRect(0,0,800,600);
p.drawLine(30,135,790,135);
#endif
QPainter p(m_window);
QPoint pt(0,0);
p.drawImage(pt, img);
}

int main(int argc, char **argv)
{
printf("argc %d, argv %s\n",argc, *argv);
QtScreen *screen = QtScreenFactory::getScreen();
// img = new QImage(800,600,QImage::Format_ARGB32);
}

QtScreen *
QtScreenFactory::getScreen() {
if ( QtScreenFactory::theScreen == NULL ) {
QtScreenFactory::theScreen = new QtScreen();
if(QtScreenFactory::theScreen != NULL) {
QtScreenFactory::theScreen->init();
}
}
return QtScreenFactory::theScreen;
}

QtScreen::QtScreen()
{
}

void getQtColorModel()
{
int depth = 0;
int amask=0,rmask=0,gmask=0,bmask=0;

QWidget *d = QApplication::desktop();
depth = d->depth();
printf ("Depth = %d\n", depth);
switch(depth) {
case 32:
amask = 0xff000000;
case 24:
rmask = 0x00ff0000;
gmask = 0x0000ff00;
bmask = 0x000000ff;
break;
}
}

void Qtexec()
{
qtApp->exec();
}

bool
QtScreen::init()
{
pthread_t tid;
int m_x = 0,m_y=0,m_width=800,m_height=600;

int argc = 1;
char *argv[3] = {"a.out", "-qws", NULL};
new QtApplication(argc, argv);
m_window = new QtWindow(Qt::FramelessWindowHint|Qt::Window);
QBitmap bitmap(m_width, m_height);
qtApp->setActiveWindow(m_window);

QPaintDevice *qpd = m_window;
printf("exec called\n");
m_window->show();
getQtColorModel();
#if 1
pthread_create(&tid, NULL, (void *(*)(void *))threadfunc, NULL);
#endif
Qtexec();
}


void threadfunc(void *arg)
{
printf("threadfunc called\n");
AWT_drawline(30,135,790,135);
}

void
AWT_drawline(int x1, int y1, int x2, int y2)
{
#if 0
QPen *qp = new QPen();
QBrush *qb = new QBrush();
QPainter p(qpd);
p.setCompositionMode(QPainter::CompositionMode_Sou rceOver);
p.setPen(*qp);
p.setClipRect(0,0,800,600);
p.drawLine(30,135,790,135);
#endif
QPainter p(&img);
p.setCompositionMode(QPainter::CompositionMode_Sou rceOver);
QPen *qp = new QPen();
QColor c(0xffff0000);
qp->setColor(c);
QBrush *qb = new QBrush();
p.setPen(*qp);
p.setClipRect(0,0,800,600);
p.drawLine(x1, y1, x2, y2);
m_window->update(0,0,800,600);
}

But while executing this, I got a segv
(gdb) run
Starting program: /export/WorkSpaces/a.out
[Thread debugging using libthread_db enabled]
[New Thread -1208498480 (LWP 3723)]
argc 1, argv /export/WorkSpaces/a.out
Qt: gdb: -nograb added to command-line options.
Use the -dograb option to enforce grabbing.
exec called
Depth = 24
[New Thread -1212621936 (LWP 3727)]
QApplication exec called
threadfunc called

Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread -1212621936 (LWP 3727)]
0x0077021d in QWidget::testAttribute (this=0x0,
attribute=Qt::WA_WState_Visible)
at ../../include/QtGui/../../src/gui/kernel/qwidget.h:986
986 return data->widget_attributes & (1<<attribute);
(gdb) bt
#0 0x0077021d in QWidget::testAttribute (this=0x0,
attribute=Qt::WA_WState_Visible)
at ../../include/QtGui/../../src/gui/kernel/qwidget.h:986
#1 0x007702e3 in QWidget::isVisible (this=0x0)
at ../../include/QtGui/../../src/gui/kernel/qwidget.h:948
#2 0x009632ee in QWidget::update (this=0x0, r=@0xb7b8d338)
at painting/qbackingstore.cpp:1203
#3 0x0804b2d7 in QWidget::update (this=0x0, ax=0, ay=0, aw=800, ah=600)
at /usr/local/Trolltech/QtDebug-4.4.3/include/QtGui/qwidget.h:945
#4 0x0804a8bb in AWT_drawline (x1=30, y1=135, x2=790, y2=135)
at drawline.cpp:214
#5 0x0804a924 in threadfunc (arg=0x0) at drawline.cpp:190
#6 0x0050044b in start_thread () from /lib/libpthread.so.0
#7 0x0047780e in clone () from /lib/libc.so.6
(gdb)

Can you please tell me as to what might be wrong in the application?

If you feel this is not the right approach, then can you please tell me how to use the event-driven approach. Which class to use and how in the above appln.?

Thanks in advance