Trolltech Home | Qt-interest Home | Recent Threads | All Threads | Author | Date
All threads index page 4

Qt-interest Archive, October 2007
pixmaps in qgraphicsscene


Message 1 in thread

 I can't figure out how to draw on a pixmap once it has been added to
a
qgraphicsscene. I'd like to do something like:



QGraphicsScene prob_scene;
QPixmap pix(100,100);
pix.fill(Qt::red);
{
QPainter p(pix);
p.drawEllipse(50,50,10,10);
}
QGraphicsPixmapItem pixi=prob_scene.addPixmap(pix);
<get a painter for the pixmap inside prob_scene so I can paint with
it>



I can't figure any direct way of doing this, nor any way that doesn't
involve a deep copy at some point. I think it would be possible to
subclass QGraphicsItem, reimplementing paint() and adding a pixmap
member item such that I could still draw on it, but that seems a shame
when QGraphicsPixmapItem is already there. Or is there some other
technique I should consider?



Thanks!

Bill

--
 [ signature omitted ] 

Message 2 in thread

Im not sure if there is any way of doing it but
I want to spread my script code of multiple files so that each function can
be in its own file.

However, from what I can tell there is no way of including files in the
script itself
Is there a way of having multiple files registered with the engine, and
being able to do this at runtime (i.e. register files from the script)

--
 [ signature omitted ] 

Message 3 in thread

Ben Shaw wrote:
> Im not sure if there is any way of doing it but
> I want to spread my script code of multiple files so that each function can
> be in its own file.
>
> However, from what I can tell there is no way of including files in the
> script itself
> Is there a way of having multiple files registered with the engine, and
> being able to do this at runtime (i.e. register files from the script)
>   

Hi Ben,
There's no built in include function, but if someone were to write one
it might end up looking something like this:

QScriptValue qtscript_include(QScriptContext *context, QScriptEngine
*engine)
{
    QScriptValue arg = context->argument(0);
    if (!arg.isString()) {
        return context->throwError(
            QScriptContext::TypeError,
            QString::fromLatin1("include(): filename expected"));
    }
    QString fileName = arg.toString();
    QFile file(fileName);
    if (!file.open(QFile::ReadOnly)) {
        return context->throwError(
            QString::fromLatin1("include(): could not open `%s'")
            .arg(fileName));
    }
    QTextStream ts(&file);
    QString contents = ts.readAll();
    file.close();
   
context->setActivationObject(context->parentContext()->activationObject());
    return engine->evaluate(contents, fileName);
}


To use it:

QScriptEngine eng;
eng.globalObject().setProperty("include",
eng.newFunction(qtscript_include));

Then you can do e.g. "include('foo.js')" from a script. You can surround
the include by try/catch to check if the include caused an error.
The included code will be evaluated in the context of the calling
function. If you want the included code to be evaluated as global code,
replace the line

context->setActivationObject(context->parentContext()->activationObject());

with

context->setActivationObject(engine->globalObject());
context->setThisObject(engine->globalObject());


Regards,
Kent

--
 [ signature omitted ] 

Message 4 in thread

Kent,

I'm new to JavaScript, so the choice between having the include 
evaluated in the calling
function's context (scope?) and the global context (scope?) is 
mysterious to me. Why would
you choose one over the other? Naively, I would think most users would 
expect the include
to be in the global scope.

Kent Hansen wrote:
> Ben Shaw wrote:
>   
>> Im not sure if there is any way of doing it but
>> I want to spread my script code of multiple files so that each function can
>> be in its own file.
>>
>> However, from what I can tell there is no way of including files in the
>> script itself
>> Is there a way of having multiple files registered with the engine, and
>> being able to do this at runtime (i.e. register files from the script)
>>   
>>     
>
> Hi Ben,
> There's no built in include function, but if someone were to write one
> it might end up looking something like this:
>
> QScriptValue qtscript_include(QScriptContext *context, QScriptEngine
> *engine)
> {
>     QScriptValue arg = context->argument(0);
>     if (!arg.isString()) {
>         return context->throwError(
>             QScriptContext::TypeError,
>             QString::fromLatin1("include(): filename expected"));
>     }
>     QString fileName = arg.toString();
>     QFile file(fileName);
>     if (!file.open(QFile::ReadOnly)) {
>         return context->throwError(
>             QString::fromLatin1("include(): could not open `%s'")
>             .arg(fileName));
>     }
>     QTextStream ts(&file);
>     QString contents = ts.readAll();
>     file.close();
>    
> context->setActivationObject(context->parentContext()->activationObject());
>     return engine->evaluate(contents, fileName);
> }
>
>
> To use it:
>
> QScriptEngine eng;
> eng.globalObject().setProperty("include",
> eng.newFunction(qtscript_include));
>
> Then you can do e.g. "include('foo.js')" from a script. You can surround
> the include by try/catch to check if the include caused an error.
> The included code will be evaluated in the context of the calling
> function. If you want the included code to be evaluated as global code,
> replace the line
>
> context->setActivationObject(context->parentContext()->activationObject());
>
> with
>
> context->setActivationObject(engine->globalObject());
> context->setThisObject(engine->globalObject());
>
>
> Regards,
> Kent
>
> --
> To unsubscribe - send a mail to qt-interest-request@xxxxxxxxxxxxx with "unsubscribe" in the subject or the body.
> List archive and information: http://lists.trolltech.com/qt-interest/
>
>   

--
 [ signature omitted ] 

Message 5 in thread

Peter Hackett wrote:
> Kent,
>
> I'm new to JavaScript, so the choice between having the include
> evaluated in the calling
> function's context (scope?) and the global context (scope?) is
> mysterious to me. Why would
> you choose one over the other? Naively, I would think most users would
> expect the include
> to be in the global scope.

Hi Peter,
You're right, having the code evaluated in the global scope is likely to
cause the least amount of surprise. But to an unnatural being like
myself, it feels more natural that include() simply expands to eval(/*
contents of file */). ;-) Gives me more control over what's going to
happen, and I can arrange for my including+included code to play nicely
by this contract. On a related note, I like the _global extension in
ActionScript, which means you can explicitly write e.g. "_global.foo =
bar" to add something to the global scope. That's just a one-liner to
set up in Qt Script:
engine.globalObject().setProperty("_global", engine.globalObject()).
Then, regardless of in which scope your included code is evaluated, it's
always clear from the code what stuff you intend to publish to the rest
of the world.

Regards,
Kent

--
 [ signature omitted ] 

Message 6 in thread

On mandag den 22. Oktober 2007, Bill Butler wrote:
>  I can't figure out how to draw on a pixmap once it has been added to
> a
> qgraphicsscene. I'd like to do something like:
>
>
>
> QGraphicsScene prob_scene;
> QPixmap pix(100,100);
> pix.fill(Qt::red);
> {
> QPainter p(pix);
> p.drawEllipse(50,50,10,10);
> }
> QGraphicsPixmapItem pixi=prob_scene.addPixmap(pix);
> <get a painter for the pixmap inside prob_scene so I can paint with
> it>
>
>
>
> I can't figure any direct way of doing this, nor any way that doesn't
> involve a deep copy at some point. I think it would be possible to
> subclass QGraphicsItem, reimplementing paint() and adding a pixmap
> member item such that I could still draw on it, but that seems a shame
> when QGraphicsPixmapItem is already there. Or is there some other
> technique I should consider?

You are correct; all possible ways forward for you will involve a deep copy. 
This is an unfortunate side effect of the copy-on-write paradigm used in Qt.

Your only option for painting on the pixmap is something like this:

QPixmap pixmap(pixi.pixmap());
QPainter painter(&pixmap);
...
painter.end();
pixi.setPixmap(pixmap);

You will get a deep copy as soon as you start painting on the old pixmap, and 
there is no way of telling Qt to not do the deep copy.

Of course, you could do a pixi.setPixmap(QPixmap()) before opening the 
painter. This will avoid the deep copy and will work, except if the scene 
decides to redraw itself while you're doing the pixmap painting.

My guess is that it should be safe in this case to do it.

Bo.

-- 
 [ signature omitted ] 

Message 7 in thread

On Mon, 22 Oct 2007 09:28:46 +0200, Bo Thorsen
<bo@xxxxxxxxxxxxxxxxxxxxx> wrote:

>On mandag den 22. Oktober 2007, Bill Butler wrote:
>>  I can't figure out how to draw on a pixmap once it has been added to
>> a
>> qgraphicsscene. I'd like to do something like:
>>
>>
>>
>> QGraphicsScene prob_scene;
>> QPixmap pix(100,100);
>> pix.fill(Qt::red);
>> {
>> QPainter p(pix);
>> p.drawEllipse(50,50,10,10);
>> }
>> QGraphicsPixmapItem pixi=prob_scene.addPixmap(pix);
>> <get a painter for the pixmap inside prob_scene so I can paint with
>> it>
>>
>>
>>
>> I can't figure any direct way of doing this, nor any way that doesn't
>> involve a deep copy at some point. I think it would be possible to
>> subclass QGraphicsItem, reimplementing paint() and adding a pixmap
>> member item such that I could still draw on it, but that seems a shame
>> when QGraphicsPixmapItem is already there. Or is there some other
>> technique I should consider?
>
>You are correct; all possible ways forward for you will involve a deep copy. 
>This is an unfortunate side effect of the copy-on-write paradigm used in Qt.
>
>Your only option for painting on the pixmap is something like this:
>
>QPixmap pixmap(pixi.pixmap());
>QPainter painter(&pixmap);
>...
>painter.end();
>pixi.setPixmap(pixmap);
>
>You will get a deep copy as soon as you start painting on the old pixmap, and 
>there is no way of telling Qt to not do the deep copy.
>
>Of course, you could do a pixi.setPixmap(QPixmap()) before opening the 
>painter. This will avoid the deep copy and will work, except if the scene 
>decides to redraw itself while you're doing the pixmap painting.
>
>My guess is that it should be safe in this case to do it.
>
>Bo.
>
>-- 
>
>Thorsen Consulting ApS - Qt consulting services
>http://www.thorsen-consulting.dk

Bo,

Thanks a lot for your thoughts - I was afraid of that.  Your last
suggestion, which I understood as:

QPixmap pixmap(pixi.pixmap());
pixi.setPixmap(pixmap);
QPainter painter(&pixmap);

I didn't expect to work.  I would expect the setPixmap call to do a
shallow copy, initially sharing data with pixmap, but then a deep copy
will occur as soon as the painter is actually used, so that I will
paint only on pixmap, which has now been severed from pixi.  Did I
misunderstand you?


Is there a problem with subclassing QGraphicsItem to make my own
version of QGraphicsPixmapItem, one in which I can access the internal
pixmap and obtain a painter for it?  I'm guessing there must be some
reason the Qt class prevents this.


Interestingly, in another application, I got the following to work:

QRgb* display_buf;
QGraphicsScene pond_scene;
QImage *pond_image;
int w=1000, h=1000;

display_buf=new QRgb[w*h];
pond_image=new QImage((uchar*) display_buf, w, h,
QImage::Format_RGB32);
pond_pixitem=new QGraphicsPixmapItem(QPixmap::fromImage(*pond_image));
pond_scene.addItem(pond_pixitem);
pond_pixitem->setPixmap(QPixmap::fromImage(*pond_image));
ui.pond_graphicsView->setScene(&pond_scene);
ui.pond_graphicsView->show();

Now whenever I want to update the scene, I can assign QRgb values into
display_buf and do a pond_scene.invalidate().  It updates immediately.
Am I just lucky in that Qt doesn't detect my byte assigns as a write
and so doesn't make a deep copy, severing pond_image from
pond_pixitem?  Would this cease to work if I used any Qt method to
write to pond_image (causing the deep copy)?  Should I not rely on
this fortuitous behavior?

Thanks again!

Bill


--
 [ signature omitted ] 

Message 8 in thread

On tirsdag den 23. Oktober 2007, Bill Butler wrote:
> [...]
> Thanks a lot for your thoughts - I was afraid of that.  Your last
> suggestion, which I understood as:
>
> QPixmap pixmap(pixi.pixmap());
> pixi.setPixmap(pixmap);
> QPainter painter(&pixmap);
>
> I didn't expect to work.  I would expect the setPixmap call to do a
> shallow copy, initially sharing data with pixmap, but then a deep copy
> will occur as soon as the painter is actually used, so that I will
> paint only on pixmap, which has now been severed from pixi.  Did I
> misunderstand you?

You did indeed. You need pixi.setPixmap(QPixmap()). The simple idea is to set 
another pixmap on the item. If the reference counter on the pixmap data is 1, 
you won't get a deep copy.

You have to set the pixmap on the item after you are done painting, of 
course - and remember to end() the painter before you do so.

> Is there a problem with subclassing QGraphicsItem to make my own
> version of QGraphicsPixmapItem, one in which I can access the internal
> pixmap and obtain a painter for it?  I'm guessing there must be some
> reason the Qt class prevents this.

That is an option for you, but you solve your problem by duplicating pretty 
much all the code in QGraphicsPixmapItem.

> Interestingly, in another application, I got the following to work:
>
> QRgb* display_buf;
> QGraphicsScene pond_scene;
> QImage *pond_image;
> int w=1000, h=1000;
>
> display_buf=new QRgb[w*h];
> pond_image=new QImage((uchar*) display_buf, w, h,
> QImage::Format_RGB32);
> pond_pixitem=new QGraphicsPixmapItem(QPixmap::fromImage(*pond_image));
> pond_scene.addItem(pond_pixitem);
> pond_pixitem->setPixmap(QPixmap::fromImage(*pond_image));
> ui.pond_graphicsView->setScene(&pond_scene);
> ui.pond_graphicsView->show();
>
> Now whenever I want to update the scene, I can assign QRgb values into
> display_buf and do a pond_scene.invalidate().  It updates immediately.
> Am I just lucky in that Qt doesn't detect my byte assigns as a write
> and so doesn't make a deep copy, severing pond_image from
> pond_pixitem?  Would this cease to work if I used any Qt method to
> write to pond_image (causing the deep copy)?  Should I not rely on
> this fortuitous behavior?

This is AFAIK a documented behaviour of QImage. And I know of other companies 
that use this trick.

Unfortunately, there is no QGraphicsImageItem, so you can't use your trick 
here. And I'm pretty sure the conversion between QImage and QPixmap takes 
longer time than a QPixmap deep copy.

Bo.

-- 
 [ signature omitted ]