Removing unrelated object causes binding loop warnings - qt

It's been several times already, so this time I decided to ask and see if anyone has an idea what is going on.
I have this item backbone:
Item {
id: main
property alias rw: main.childrenRect.width
property alias rh: main.childrenRect.height
property alias rx: main.childrenRect.x
property alias ry: main.childrenRect.y
// other stuff here
Item {
Rectangle { // debugrect
width: main.rw
height: main.rh
x: main.rx
y: main.ry
color: "#00000000"
}
}
}
The debugrect served as a visual debug aid, note how it is wrapped in another item at position 0,0 with size 0,0 - that is it is not in any way included in the childrenRect of the main item. Also it doesn't have an id, thus it is not being referenced by any other item whatsoever.
What's most confusing, it is not the presence of this object that causes binding loop messages, but its removal from the code. For some reason, even though it is completely isolated from influencing any other object, removing it causes binding loops in other objects which have absolutely nothing to do with it. What possible mechanic could involve that isolated object's presence in the manifestation of binding loops?
Note: I have not included the full code because it is irrelevant. It is longer than anyone would be willing to debug for me, I have debugged it and found nothing wrong with it, the question is not about "what's wrong with my code" but about "what's wrong with qt" - about how something isolated could be influencing unrelated bindings or triggering false warnings.
Edit: To dispel theories that the debugrect might in some way influence the main.childrenRect, I'd like to offer this:
Logical proof: The debugrect is sized by the main.childrenRect, thus if it influenced it in any way, then that would cause a binding loop. There is no such loop, as there is no such influence. The wrapping Item does influence the main.childrenRect, however, it is a zero size item at position 0,0, and it doesn't move or anything like it, so with or without it, absent any other objects, you'd get a 0,0,0,0 for the main.childrenRect.
Practical proof:
Item {
Rectangle {
width: 10
height: 10
}
Item {
Rectangle {
width: 100
height: 100
}
}
Component.onCompleted: console.log(childrenRect)
}
The output, as expected, says: QRectF(0, 0, 10, 10) - the inner rectangle does not influence the outer rectangle's childrenRect in any way when it is wrapped in a "null" item.
Also, not that it matters, but the binding loops I am getting are about objects, declared in other sources which have no notion of the debugrect whatsoever.
My preliminary theory is that the presence or absence of the rectangle in question influences in some way the order and structure of the binding evaluation trees (and not the actual results). The code is very complex but it also works just fine, there is no erroneous behavior when the debugrect is removed, debugging has not indicated any actual binding loops taking place, just the warnings of them. Big whoop, some might say, however this application redirects the console output to the user to indicate actual problems and I can't have it flooded by bogus warnings. As the binding logic is quite complex, it seems to hit a weak spot in the binding evaluation implementation, and from earlier detailed tests I have verified that qml bindings are hardly evaluated in the most logical or efficient way. The solution is to simply leave the rectangle there, but out of curiosity, I would like to get to the bottom of this.
Edit 2: Extra info:
debugrect having static position and dimension results in the same behavior as removing it altogether. It seems that absent those bindings the qtquick runtime generates the warnings
the main.childrenRect is actually barely used. It is not being used to anchor anything, and besides the debugrect the only other reference to that, which is the actual intended use, is to size the content size of a Flickable the main item is in, and to compensate for objects in negative space, since flickable doesn't seem to work with objects in it, it starts at 0,0. The idea here is for the main item to expand the flickable content area to accommodate all of the main item's children. Removing the intended use reference doesn't make a difference
the main.childrenRect is not used in any way in any of the objects which generate binding loop warnings, neither directly, nor indirectly. What's more, the objects in question don't influence the main.childrenRect either

Related

QML InputHandler stop propagation of event

I have two Rectangles, each with a TapHandler. Rectangle A is the parent of Rectangle B
How can I configure A and B, so that when B is clicked, the EventPoint does not propagate to the onSingleTapped handler of A?
The EventPoint docs suggest to set its accepted property to true:
Setting accepted to true prevents the event from being propagated to Items below the PointerHandler's Item.
However, at the same time the docs state that accepted is a read-only property, which does not make much sense (I guess the documentation is out-of-date or simply wrong).
TestCode:
Rectangle {
id: a
width: 200
height: 200
color: "yellow"
TapHandler {
onSingleTapped: console.log("A tapped")
}
Rectangle {
id: b
color: "blue"
width: 100
height: 100
TapHandler {
onSingleTapped: function(eventPoint) {
// Stop propagation.
eventPoint.accepted = true
console.log("B tapped")
}
}
}
}
UPDATE: Setting the gesturePolicy of B to TapHandler.ReleaseWithinBounds prevents A from receiving the event. Not sure if this really the best solution
For Handlers, the entire event is delivered to each handler; therefore Handlers accept individual points, not the whole event. In general, accepting all points implies accepting the entire event, but it may be that one handler accepts some points while another accepts other points. delivery is not “done” until all the points are accepted.
It looks like setting grabPermissions without a gesturePolicy does not do what's expected .. grab the event and preventing propagation to other items.
Changing Rectnagle b (a's child) TapHandler to have gesturePolicy: TapHandler.ReleaseWithinBounds TapHandler.WithinBounds seems the right way to aaccept, in other words this way it accepts the point, that means the event will not propagate to the TapHandler of the parent Rectangle!
Rectangle {
id: b
z:2
color: "blue"
width: 100
height: 100
TapHandler {
gesturePolicy: TapHandler.ReleaseWithinBounds | TapHandler.WithinBounds
grabPermissions: PointerHandler.CanTakeOverFromAnything | PointerHandler.CanTakeOverFromHandlersOfSameType | PointerHandler.CanTakeOverFromItems
| PointHandler.ApprovesTakeOverByAnything | PointHandler.ApprovesCancellation
onSingleTapped: function(eventPoint) {
// Stop propagation.
eventPoint.accepted = true // this is just a confirmation!
console.log("B tapped")
}
}
}
further from .. narkive interset group
Qt makes a difference between active and passive touch point / pointer grabs. grabPermissions only affect active grabbers. TapHandler is passive with the default gesturePolicy, and active otherwise. That's why you need to change the gesturePolicy in a TapHandler to see any grabPermissions in effect, even the default ones.
Other input handlers don't have this same quirk, but have others. While each handler is simpler than a MouseArea or MultiPointTouchArea as Qt intended, interactions between layered handlers became much more complicated than between layered Area instances. So, for complex input, I'm using an Area instead. Which one depends on whether I'm handling hover or multitouch.
Active and passive grabs: https://doc.qt.io/qt-6/qtquickhandlers-index.html#pointer-grab
TapHandler behavior: https://doc.qt.io/qt-6/qml-qtquick-taphandler.html#details
Yes as the grabPermissions docs say:
This property specifies the permissions when this handler's logic decides to take over the exclusive grab, or when it is asked to approve grab takeover or cancellation by another handler.
"Exclusive" means it's the conventional kind of grab that only one Item or Handler can hold at any given moment. Whereas passive grabs are made for stealth, to deal with the fact that all gestures are ambiguous at the time when the user presses initially: several handlers can take passive grabs to register interest in that point, to monitor mouse or touchpoint movements independently, and then perhaps one of them can decide later to transition to taking the exclusive grab when some condition is met, such that the handler believes the user is initiating a gesture that is relevant to that handler. At the time of the transition, grabPermissions control the negotiation that will occur: which one gets to take over the exclusive grab.
But TapHandler's default gesturePolicy is DragThreshold, and in that case it can detect a tap using only a passive grab, which makes it work independently of other items or handlers. This is meant to be useful for augmenting behavior of other components without interfering with their built-in behavior; but it's not so useful if you want to ensure that only one thing can detect a tap or click. If you want TapHandler to participate in exclusive-grab negotiations (stealing the grab from other items or handlers, and allowing or disallowing the grab to be stolen by another), then you need to set gesturePolicy first, to make it use an exclusive grab. Then, if the TapHandler takes the exclusive grab on press, and the grab is stolen by something else, it will emit canceled rather than tapped.
Sorry it turned out a bit unintuitive: the intention was that gesturePolicy is a designer-friendly property, you just specify the behavior you want and don't worry about grabs. But in practice, it seems to me that we often end up changing gesturePolicy specifically to make TapHandler take an exclusive grab, to not be so stealthy, to participate in the negotations with other components.
If you need to troubleshoot grab-transition scenarios at runtime (which in practice is the first thing to think about whenever mouse or touch behavior is not what you expected), there are several grabbing-related logging categories: qt.pointer.grab qt.quick.handler.grab and qt.quick.pointer.grab. (There are some logging differences between Qt 5 and 6 though.) What I do on Linux is I have a big ~/.config/QtProject/qtlogging.ini file with all logging categories that I've ever cared about, in alphabetical order, mostly commented out (first line begins with # symbol), but those related to event delivery and grabs are often uncommented, since I spend a lot of time trying to fix Qt bugs related to that.
As for setting accepted to true or false on an individual event: that's an old MouseArea pattern, not carried forward into the way that Pointer Handlers are used. There are several problems with it:
QPointerEvent and QEventPoint are stack-allocated Q_GADGET types, not Q_OBJECT. That means they are passed from C++ to QML by value. So setting the accepted property would have no effect, even if we made it a writeable property: you'd be modifying a copy, and the event delivery logic would not see it. When MouseArea lets you do that, it has to populate a special QMouseEvent (QObject) on the fly so that it can be emitted by pointer, just so that you can set that property, and Qt code can see that you set it. (At least since 5.8 those wrapper objects get reused rather than being allocated on-the-fly.)
QML is supposed to be a declarative language. It's not declarative (and not FP) to require you to write a snippet of JS that imperatively sets a property for the sake of its side effect on delivery logic.
QML is supposed to be a designer-friendly language. Newbies should not need to understand what it means to accept an event. (ok perhaps that's really not achieved... nice goal though?)
Accepting a whole event is sensible for mouse but not for touch: if some fingers touch one item and other fingers touch another, you could execute multiple gestures simultaneously (for example you could drag multiple sliders with multiple fingers, if the slider component uses DragHandlers). If accepting the event implies that one item is grabbing all fingers at the same time, that's incompatible with providing the complete event to each item. Because of this, we have to split up QTouchEvents during delivery to Items, which is complex and bad for efficiency. We'd like to be able to stop doing that some day. For now, only Handlers get to see the complete events (all touchpoints, even those that are outside the Item's bounds). This allows things like the margin property to work.
Accepting an event does two things: you are asking to stop propagation, and you are also implicitly asking for an exclusive grab, of the entire event, as you saw it (after the touch-splitting). Basically you are saying "the buck stops here", which is a bit arrogant. (How can one component know for sure, at the time of press, that it's the sole component in the entire application that could possibly care about that gesture?) This is legacy logic that we have to maintain because of all the legacy Items that do event handling that way (MouseArea, Qt Quick Controls, stuff in other Qt modules, lots of third-party components). In the future it's probably better to separate grabbing from control of propagation. This is part of why passive grabs exist too: handlers must be able to act cooperatively, to deal with ambiguity, so they generally should avoid stopping propagation, to allow other handlers to also see the same events. Only when a handler is sure that a gesture has really started should it attempt to take the exclusive grab.

Broken recurssive instantiation checks

I notice that QML has some unspecified problem with the following - when a delegate tries to instantiate an object that contains it (the delegate that is).
Consider this trivial tree builder example:
// Object.qml
Row {
property int c : 0
Rectangle {
width: 50
height: 50
color: "red"
border.color: "black"
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.LeftButton | Qt.RightButton
onClicked: {
if (mouse.button === Qt.LeftButton) c++
else if (c) c--
}
}
}
List { model: c }
}
// List.qml
Column {
property alias model : r.model
Repeater {
id: r
delegate: Object {} // wont work
}
}
In this example Object contains List, which has a delegate that instantiates another Object. However it doesn't work, there are no error messages, not even in debug mode, the application window simply doesn't appear, and from the looks of memory usage, it is hopelessly stuck somewhere in oblivion. It doesn't crash either. It just doesn't work.
My first suspicion is that this is yet another one of QML's shortsighted implementation details, with the noble goal to protect against accidental infinite recursions. However, in this case there is no danger of such, because even though the two objects mutually instantiate each other, the process is controlled by the "model".
It also happens to work "flawlessly" if an "indirection" is added, for example:
Column {
property alias model : r.model
Repeater {
id: r
delegate: Loader { source: "Object.qml" }
}
}
If the delegate is replaced from an Object to a Loader that instnatiates an Object, it works OK, I assume because it doesn't trigger the dumb cross-reference check which interrupts execution.
You might be asking why I simply don't use the second scenario, and the answer is simple - it is trivial example it is OK, but in my actual production code this breaks something very complex I am making with cascading the amount of children items in negative xy space, and inserting a Loader in between simply kills it, and I haven't found a way to get it to work.
I also notice delegates have that absolutely identical problem when instantiating the tree example from Components within that same source, cross-reference or self-reference - when this type of instantiation recursion is used, the application gets stuck the same way - no errors, no warnings, no crash, no window, no nothing.
Any idea what's going on and how to fix it? My money is on infinite recursion protection, which assumes it is infinite even if it is in fact controlled and very much finite recursion.
EDIT: Also note that wrapping it this way doesn't work either delegate: Component { Object {} }. It works only if Object is not referenced as an instance, so it looks like some very superficial check if objects mutually contain each other regardless of the actual circumstances. So as expected, it also works if the wrapping is moved to Object:
Loader {
Component.onCompleted: setSource("List.qml", {"model": Qt.binding(function() {return c})})
}
UPDATE:
I also noticed this:
// Obj.qml
Item {
id: main
property Obj thisObj : main
}
Which doesn't work either. This time there is some actual output in the console, saying that Obj is instantiated recursively which it really isn't since it is used in the context of a type property, not a value/instance.
It seems that the recursive instantiation checks in QML and fundamentally broken, they just produce absolutely nothing if a literal QML element type self/cross reference is found in the body of an object tree, and produce false output when it the reference is used to specify a property type, even if QML object properties are actually implemented as references rather than instances.
I also issued a bug report and proposing how to distinguish between actual infinite recursions and cases of self or cross reference which are actually not infinite recursions, which is quite simple - simply don't trigger if the reference is a property type or inside a delegate.

Artifacts showing when modifying a custom QGraphicsItem

I'm currently developping a small vector drawing program in wich you can create lines and modify them after creation (those lines are based on a custom QGraphicsItem). For instance, the picture below shows what happens when the leftmost (marked yellow) point of the line is dragged to the right of the screen, effectively lengthening the line :
Everything works fine when the point is moved slowly, however, when moved rapidly, some visual artifacts appear :
The piece of code I'm using to call for a repaint is located in the mouseMoveEvent redefined method, which holds the following lines of code :
QRectF br = boundingRect();
x2 = static_cast<int>(event->scenePos().x()-x());
y2 = static_cast<int>(event->scenePos().y()-y());
update(br);
There's apparently no problem with my boundingRect definition, since adding painter->drawRect(boundingRect()) in the paint method shows this :
And there are also no problem when the line is simply moved (flag QGraphicsItem::ItemIsMovable is set), even rapidly.
Does anyone know what is happening here ? My guess is that update is not being called immediately hence mouseMoveEvent can be called multiple times before a repaint occurs, maybe canceling previous calls ? I'm not sure.
Of course the easy fix is to set the viewport mode of the QGraphicsView object holding the line to QGraphicsView::FullViewportUpdate), but that is ugly (and slow).
Without seeing the full function for how you're updating the line, I would guess that you've omitted to call prepareGeometryChange() before updating the bounding rect of the items.
As the docs state: -
Prepares the item for a geometry change. Call this function before changing the bounding rect of an item to keep QGraphicsScene's index up to date.

QML TableView access model properties from delegate

I have a TableView for which I've defined my own itemDelegate. Now, from within this delegate I can access the value for the column using styleData.value, but I'd also need to access the other properties in this same item but I can't find how to.
I need this, because the text styling needs to change depending on some other property of the item model.
Any ideas? thanks!
There is some documentation missing. Within the item delegate you can access the following (taken from the source code of TreeView.qml):
styleData (see documentation)
model (currently not documented)
modelData (currently not documented, not sure about this but I guess it's similar to ListView)
(By the way, what's also missing in the documentation but which is useful is styleData.role. Also, the documentation of the other delegates lacks some available properties too; the best is to peek into the source code of the QML file and have a look for the Loader element which instantiates your delegate. As a plus you learn how that creepy stuff works. ;))
With model and the row/column information you can then navigate to the item data. This code depends on the type of model.
If you're using QML's ListModel, then you can use model.get: model.get(styleData.row)[styleData.role] should then work (untested since I use it rarely, please give feedback).
If you're using a C++ QAbstractItemModel or friends, the best is to add a slot to the model class which takes just the row and role name, since that's the information the TableView works with (nor with role numbers nor with columns...).
However in both cases you shouldn't use the expression in a property binding! The notification system will not work since you don't use the property system for accessing the data. According to your question, I guess you wanted to use it in a expression with binding. I don't know how to properly listen to changes in the model manually.
An alternative approach is to access the other items of the row and provide a property there. Some hints:
From within one item, you can access other items of the same row by walking the object tree up twice (first to the Loader which instantiates your component, then to the actual row) and then down twice (first to the particular child object which is a Loader, then its instantiated item). You need to know the column number you want to access (not the role name), I assume you want to access the first column (index 0):
parent.parent.children[0].item
You can provide the model data using a property in each item. Assuming a simple Text element this might be:
Text {
property variant value: styleData.value // <-- Here you make it available
// your other stuff
}
Putting them together could look like the following. In this example I assume the first row contains an integer, and if it is zero, the second column should be red.
// (within TableView)
itemDelegate: Text {
property variant value: styleData.value
text: styleData.value
color: (styleData.column == 1 && parent.parent.children[0].item.value === 0)
"red" : "black"
}
I think it's pretty easy if you read the source code of TableViewItemDelegateLoader.qml (it is a private code in qtquickcontrol)
To access any role you use use : model[your_role_name] .
For exp: model["comment"]
Faced with same problem today, this is result of my investigations (Qt 5.2.x)
If you have hard limit to TableView, there is only one correct solution - use model.get(styleData.row)["roleForStyling"] as #leemes wrote. But it will very slow if you have big amount of data in model and using, for example, proxy model for sorting/filtering.
Direct solution from #leemes answer is great, but in general case not be working, because in TableView any Item wrapped in Loader and therefore independent from parent and other items:
When some item is created (where you want to change text style)
another element (from which to receive identity) cannot yet be
created
You may not have "parent" on item creation (i.e. binding will
be broken)
In my case, the best solution for deep customise was creation of the simple wrapper for ListView. In this case you have access for complete row data in delegate without the overhead. Highlights for making component ("My own ListView as table"):
Create standalone header (Rectangle or Item) - do not use header form ListView.This make it fixed for any amount of data.
Wrap ListView to ScrollView (if you need scrollbars)
Use Clip: true property in list for make correct
Set style for highlight and set highlightFollowsCurrentItem:true in ListView
As bonus in future this may be used for make "TreeTable" :)

Flash/Flex: "Warning: filter will not render" problem

In my flex application, I have a custom TitleWindow that pops up in modal fashion. When I resize the browser window, I get this warning:
Warning: Filter will not render. The DisplayObject’s filtered dimensions (1286, 107374879) are too large to be drawn.
Clearly, I have nothing set with a height of 107374879.
After that, any time I mouse over anything in the Flash Player (v. 10), the CPU churns at 100%. When I close the TitleWindow, the problem subsides. Sadly, the warning doesn't seem to indicate which DisplayObject object is too large to draw. I've tried attaching explicit height/widths to the TitleWindow and the components within, but still no luck.
[Edit]
The plot thickens:
I found that the problem only occures when I set the PopUpManager's createPopUp modal parameter to "true." I don't see the behavior when modal is set to "false." It's failing while applying the graying filter to other components that comes from being modal. Any ideas how I can track down the one object that has not been initialized but is being filter during the modal phase?
Thanks for reading.
This might not be the case in your application, but I have come across similar cases where a height or width of an object has been set to some unreasonable big number as the result of misuse of an unsigned integer in calculations for positioning, height or width.
Schematic code for such a scenario could be like this:
var offset:uint = 30;
var position:uint = txt.textHeight - offset;
divider.y = position;
The code wrongfully assumes that txt.textHeight will always be bigger than 30. If it is not, txt.textHeight - offset will be a negative number, that when stored in an uint will instead become a very large number.
Let's say for example that the text in txt, presumed to be a long multiline text, instead is a single line that is 20 pixels heigh. The result will then be -10, and when stored in the uint var position, the value of position will be 4294967286.
The above is crappy code, an example, but in a real world situation the uint problem can occur in some more complex way, that might be harder to spot right away. I guess it is seldom a good idea to use an unsigned integer for stuff like x and y positions, that can have negative values.
You could write some code to recursively step down the hierarchy of DisplayObjectContainer and DisplayObject objects and check for the large height.
Should be pretty simple to write. A function something like this should do the trick:
function RecurseDisplayObjects(DisplayObject obj):void
{
//check for height and do a trace() or whatever here
if(obj is DisplayObjectContainer)
{
var container:DisplayObjectContainer = obj as DisplayObjectContainer;
for(var i:int=0; i<container.numChildren; i++)
{
RecurseDisplayObjects(container.getChildAt(i);
}
}
}
You would need to start this off by passing it the top level DisplayObject in your application. (possibly obtained with DisplayObject.root)
The other option you have is to get the Flex framework source and modify it to give you a more meaningful error.
The problem is probably not in your TitleWindow, but in objects below it. The filter failing to render is probably the blur filter flash applies over everything below the modal dialog. If one of the objects on the stage is too big to apply blur on it in real time, you get the error you mentioned.
I solved that problem by applying a mask to the object below the titlewindow, set to the size of the stage. That will probably solve your problem but you should definitely look into why something gets to that size, doesn't sound healthy. :-)
Sadly I have no idea, but we're trying to track down a similar issue in ours. Maybe this will help?
http://www.mail-archive.com/flashcoders#chattyfig.figleaf.com/msg48091.html
I had a similar issue, tracked it down to an alpha filter applied to an object scaled to -0.23453422334. Once I rounded the scale to 2 significant digits everything worked fine. A difficult error to track down however.

Resources