Can these limitations be fixed/worked around?

瀏覽次數:46 次
跳到第一則未讀訊息

Michael Kirsch

未讀,
2012年4月20日 晚上9:13:252012/4/20
收件者:lqt-bindings
I am trying to add colored entries to a QComboBox, and for that I need
to put a QBrush in a QVariant. QVariant doesn't have such a
constructor, and for some reason QVariant.fromValue() doesn't exist. I
would also like to store a Lua table in a QVariant, but I don't know
if that's even possible.

Also, I wonder why you have to manually manage memory in lqt while in
Qt bindings for other languages you simply forget about memory
management. Why can't lqt do the same?

Michal Kottman

未讀,
2012年4月21日 清晨5:26:402012/4/21
收件者:lqt-bi...@googlegroups.com
On 21 April 2012 03:13, Michael Kirsch <mkirs...@gmail.com> wrote:
> I am trying to add colored entries to a QComboBox, and for that I need
> to put a QBrush in a QVariant. QVariant doesn't have such a
> constructor, and for some reason QVariant.fromValue() doesn't exist. I
> would also like to store a Lua table in a QVariant, but I don't know
> if that's even possible.

There are QVariant:setValue() and QVariant:value() to work over the
limitations of the automated generator. These bindings are hand-made
and handle all object types supported by QVariant. Unfortunately you
cannot store a Lua table in a QVariant, but you can assign custom Lua
data directly to the QVariant itself, this is redirected to a Lua
table which an empty table environment table [1] for the userdata
(this is true for any lqt-created object):

local q = QVariant()
q.abcd = 'hello'
q.efgh = 'world'

> Also, I wonder why you have to manually manage memory in lqt while in
> Qt bindings for other languages you simply forget about memory
> management. Why can't lqt do the same?

You do not have to manage memory if you do not want to. Just use this
form of construction:

local mainWindow = QMainWindow()

This is the default and expected way to work with items. It uses Lua
GC mechanism to delete the object when it is no longer referenced.

If you need to create an object so that Lua does not tamper with it
(may happen), you can use something like QObject.new(parent) and let
Qt handle the memory management (parent-child relationship), which is
useful in MVC classes like list views and models together with items.

At last, there is also an item:delete() method which explicitly calls
the destructor (may be needed sometimes).


[1] http://www.lua.org/manual/5.1/manual.html#2.9

Michal Kottman

未讀,
2012年4月21日 清晨5:52:502012/4/21
收件者:lqt-bi...@googlegroups.com
On 21 April 2012 03:13, Michael Kirsch <mkirs...@gmail.com> wrote:
> Also, I wonder why you have to manually manage memory in lqt while in
> Qt bindings for other languages you simply forget about memory
> management. Why can't lqt do the same?

Ok, I've just realized that the documentation is not really clear with
this, which may be the source of confusion. The default "managed" mode
by writing QObject() should work fine most of the time.

Still you have to realize that Qt does its own bookkeeping if you use
the parent-child mechanism (or it is forced onto you), and you have to
know where to look when items suddenly start disappearing from your
lists etc. This happened to me a few time - I didn't keep Lua
references to auto-managed list items, and Lua GC was deleting them
during GC (they remove themselves from the parent widget on
destruction).

Michael Kirsch

未讀,
2012年4月21日 上午8:31:152012/4/21
收件者:lqt-bindings


On Apr 21, 5:26 am, Michal Kottman <k0mpju...@gmail.com> wrote:
> you can assign custom Lua
> data directly to the QVariant itself, this is redirected to a Lua
> table which an empty table environment table [1] for the userdata
> (this is true for any lqt-created object):
>
> local q = QVariant()
> q.abcd = 'hello'
> q.efgh = 'world'

But as I understand QVariants are passed by copy insode Qt, and since
QVariants themselves don't have a concept of adding Lua data to them,
won't it be gone when I (for example) put the QVariant in a
QComboBox's userdata and then retrieve it later?

> You do not have to manage memory if you do not want to. Just use this
> form of construction:
>
> local mainWindow = QMainWindow()
>
> This is the default and expected way to work with items. It uses Lua
> GC mechanism to delete the object when it is no longer referenced.

What if I do this:

function build_ui(win)
local layout = QVBoxLayout()
win:setLayout(layout)
win.button = QPushButton('test')
layout:addWidget(win.button)
end

Will the layout be destroyed when the function ends, even when it's
still in use by the window object?

Michael Kirsch

未讀,
2012年4月21日 上午8:37:572012/4/21
收件者:lqt-bindings
On Apr 21, 5:52 am, Michal Kottman <k0mpju...@gmail.com> wrote:
> Still you have to realize that Qt does its own bookkeeping if you use
> the parent-child mechanism (or it is forced onto you), and you have to
> know where to look when items suddenly start disappearing from your
> lists etc. This happened to me a few time - I didn't keep Lua
> references to auto-managed list items, and Lua GC was deleting them
> during GC (they remove themselves from the parent widget on
> destruction).

It is forced onto you when you are making a GUI, and it's often nice
not to have to keep references to things like layouts (that I won't
need to access later).

Maybe it's possible to add a __gc metamethod like this to QObject-
based classes:

function (self)
if not self:parent() then self:delete() end
end

that will only delete the object if it has no parent.

Michal Kottman

未讀,
2012年4月21日 上午8:42:112012/4/21
收件者:lqt-bi...@googlegroups.com
On 21 April 2012 14:31, Michael Kirsch <mkirs...@gmail.com> wrote:
> On Apr 21, 5:26 am, Michal Kottman <k0mpju...@gmail.com> wrote:
>> you can assign custom Lua
>> data directly to the QVariant itself, this is redirected to a Lua
>> table which an empty table environment table [1] for the userdata
>> (this is true for any lqt-created object):
>>
>> local q = QVariant()
>> q.abcd = 'hello'
>> q.efgh = 'world'
>
> But as I understand QVariants are passed by copy insode Qt, and since
> QVariants themselves don't have a concept of adding Lua data to them,
> won't it be gone when I (for example) put the QVariant in a
> QComboBox's userdata and then retrieve it later?

Hmm, well, yes, this would be a problem. lqt caches pointers to
objects, so if you create a Qt object in Lua, pass it to Qt and then
retrieve it later from some other method, if the pointer does not
change, you get the original Lua-created object. But since QVariants
are passed by value, the object you get back will be different, hence
you lose the Lua table associated with the object.

I guess I'll have to create a custom QVariant type which will lock the
Lua table in registry and keep a reference to it, allowing you to
retrieve the Lua table elsewhere, unfortunately currently I have no
time to hack on lqt.

>> You do not have to manage memory if you do not want to. Just use this
>> form of construction:
>>
>> local mainWindow = QMainWindow()
>>
>> This is the default and expected way to work with items. It uses Lua
>> GC mechanism to delete the object when it is no longer referenced.
>
> What if I do this:
>
>    function build_ui(win)
>        local layout = QVBoxLayout()
>        win:setLayout(layout)
>        win.button = QPushButton('test')
>        layout:addWidget(win.button)
>    end
>
> Will the layout be destroyed when the function ends, even when it's
> still in use by the window object?

It will not be destroyed when the function ends, but when the GC kicks
in. Which may be immediately, or it may be until you move your mouse,
etc. Either way you will lose your layout. Lua GC has no way of
knowing that Qt still needs this object. To Lua, that object is
garbage once the function ends. This is why you should rely on the Qt
parent-child relationship, and use the .new() allocation together with
parent object. You get the best of both worlds - everything works and
everything is nicely deallocated when necessary.

function build_ui(win)
local layout = QVBoxLayout.new(win)
win:setLayout(layout)
win.button = QPushButton('test') -- notice that this is Lua
reference, hence Lua GC will know about this
layout:addWidget(win.button)
end

Michal Kottman

未讀,
2012年4月21日 上午8:44:512012/4/21
收件者:lqt-bi...@googlegroups.com
On 21 April 2012 14:37, Michael Kirsch <mkirs...@gmail.com> wrote:
> Maybe it's possible to add a __gc metamethod like this to QObject-
> based classes:
>
>    function (self)
>        if not self:parent() then self:delete() end
>    end
>
> that will only delete the object if it has no parent.

You can definitely try it out if this works for you:

QVBoxLayout.__gc = function(self)


if not self:parent() then self:delete() end
end

And then allocate you object using QVBoxLayout and see if it behaves
as expected. If it works fine, then I will think about updating lqt
memory management.

Michael Kirsch

未讀,
2012年4月21日 上午9:04:012012/4/21
收件者:lqt-bindings


On Apr 21, 8:44 am, Michal Kottman <k0mpju...@gmail.com> wrote:
> You can definitely try it out if this works for you:
>
> QVBoxLayout.__gc = function(self)
>   if not self:parent() then self:delete() end
> end
>
> And then allocate you object using QVBoxLayout and see if it behaves
> as expected. If it works fine, then I will think about updating lqt
> memory management.

I tried writing this test program:

require 'qtcore'
require 'qtgui'

local app = QApplication(select('#', ...), {'lua', ...})

function QVBoxLayout:__gc()
if not self:parent() then self:delete() end
endᐧ

local function build_ui(win)
local layout = QVBoxLayout()
win:setLayout(layout)
win.button = QPushButton('test')
layout:addWidget(win.button)
end

local win = QWidget()
build_ui(win)

collectgarbage()
collectgarbage()

win:show()

app.exec()

And the layout still does get destroyed. The strange thing is that it
still gets destroyed even when I make the __gc metamethod empty.

Also, I didn't know that the metatable if stored in the table with the
name of the class. Because if it is then why can't you use
"BaseClass.method(self)" to call a base class's version of a method
instead of that weird, ugly "error(SUPER)" hack?

Michal Kottman

未讀,
2012年4月21日 上午9:17:052012/4/21
收件者:lqt-bi...@googlegroups.com
On 21 April 2012 15:04, Michael Kirsch <mkirs...@gmail.com> wrote:
> Also, I didn't know that the metatable if stored in the table with the
> name of the class. Because if it is then why can't you use
> "BaseClass.method(self)" to call a base class's version of a method
> instead of that weird, ugly "error(SUPER)" hack?

To be honest the whole dependency chain still makes my head turn
around. I am not the original author of the code, I just needed to
quickly fix some problems for my diploma project, that's why the
hacks. I do not remember why the error(SUPER) was needed, I guess it
was because of protected methods were not bound outside of the class,
and there was no way to call them from Lua.

But AFAIK, I have already bound protected method, so you should be
able to call them using the nicer BaseClass.method(args) syntax. I
guess I'll need to do a major cleanup in summer...

Michael Kirsch

未讀,
2012年4月26日 上午10:54:172012/4/26
收件者:lqt-bindings
On Apr 21, 9:17 am, Michal Kottman <k0mpju...@gmail.com> wrote:
> But AFAIK, I have already bound protected method, so you should be
> able to call them using the nicer BaseClass.method(args) syntax. I
> guess I'll need to do a major cleanup in summer...

When I run this program:

require 'qtcore'
require 'qtgui'
local app = QApplication.new(select('#', ...) + 1, {'lua', ...})
local win = QWidget()
win.event = function (self, event)
print 'event'
QWidget.event(self, event)
end
win:show()
app.exec()

it says that I'm calling a nil value on the "QWidget.event()" line.

Also, any idea why this:

function QVBoxLayout:__gc()
if not self:parent() then self:delete() end
endᐧ

doesn't work?

I was looking at the source code to see if I can fix these issues, but
it's really complex and doesn't have much comments (and I'm still not
really sure I understand how the "shell" classes work). Maybe I'll
look at it more later.

Michal Kottman

未讀,
2012年4月26日 下午2:44:372012/4/26
收件者:lqt-bi...@googlegroups.com
On 26 April 2012 16:54, Michael Kirsch <mkirs...@gmail.com> wrote:
> it says that I'm calling a nil value on the "QWidget.event()" line.

Ah yes, the QWidget global only stores the public functions. The "real
deal" is stored in the Lua registry:

lua> = debug.getregistry()['QWidget*'].event
function: 0x7fbea9908c30

> Also, any idea why this:
>
>    function QVBoxLayout:__gc()
>        if not self:parent() then self:delete() end
>    endᐧ
>
> doesn't work?

Try doing this on the object directly, i.e.:

local q = QObject()
function q:__gc()
print('Deleting', self)
if not self:parent() then self:delete() end
end
q = nil
collectgarbage('collect') --> Deleting userdata: 0x7fbea9a70bb8

> I was looking at the source code to see if I can fix these issues, but
> it's really complex and doesn't have much comments (and I'm still not
> really sure I understand how the "shell" classes work). Maybe I'll
> look at it more later.

It really is complex. Did I mention a major cleanup?

Shell classes are used to "capture" C++ overriding. For every class
that has virtual methods, a "shell" class is constructed, which
overrides those methods. Each virtual method then determines whether a
Lua function of the same name is stored in the object, in which case
it converts the arguments and calls the Lua override, otherwise calls
the original function. Then, object construction ("new") is hooked to
return the shell class (i.e. lqt_shell_QWidget instead of QWidget).

This is also the reason why you cannot override functions and define
new slots of an object, which is returned from the Qt API and is not
constructed by Lua - the necessary hooks are missing.

Michael Kirsch

未讀,
2012年4月26日 下午3:04:072012/4/26
收件者:lqt-bindings
On Apr 26, 2:44 pm, Michal Kottman <k0mpju...@gmail.com> wrote:
> Ah yes, the QWidget global only stores the public functions. The "real
> deal" is stored in the Lua registry:
>
> lua> = debug.getregistry()['QWidget*'].event
> function: 0x7fbea9908c30

That works. Now what about this error:

Qt has caught an exception thrown from an event handler. Throwing
exceptions from an event handler is not supported in Qt. You must
reimplement QApplication::notify() and catch all exceptions there.

It keeps the Lua error message from showing, so how am I supposed to
debug it?

> Try doing this on the object directly, i.e.:
>
> local q = QObject()
> function q:__gc()
>   print('Deleting', self)
>   if not self:parent() then self:delete() end
> end
> q = nil
> collectgarbage('collect') --> Deleting        userdata: 0x7fbea9a70bb8

But metamethods like __gc only work when they're in the object's
metatable right?

Ralf Van Bogaert

未讀,
2012年4月27日 凌晨2:58:012012/4/27
收件者:lqt-bi...@googlegroups.com
I have a long weekend coming up. I will create a case for every known problem (ie missing methods) on the git page. If there's anything else I can do, please let me know.
回覆所有人
回覆作者
轉寄
0 則新訊息