Beefy Boxes and Bandwidth Generously Provided by pair Networks
XP is just a number
 
PerlMonks  

Re^3: OT: Stupid User Interfaces

by afoken (Chancellor)
on May 31, 2017 at 23:32 UTC ( [id://1191766]=note: print w/replies, xml ) Need Help??


in reply to Re^2: OT: Stupid User Interfaces
in thread OT: Stupid User Interfaces

Let me show some more details. For that project, I use Qt 5.3 (C++) and QML on an embedded Linux system. The current problem was:

There is a TableView that is fed with data by a ListModel. (To be continued ...)

And here, things already get ugly:

  1. There seems to be no way to get documentation for a selected (i.e. my) Qt/QML version. There is a 4.x set of documentation, currently (and probably finally) at 4.8; and there is a 5.x set, currently at 5.8 or 5.9. No 5.3. Let's hope the 5.x set documents every change since 5.3. (Yes, maybe the complete documentation should come with the Linux distribution. No, it does not. The distribution sucks even more.)
  2. Then, I want a table. It should be fed by a model. Why is that model called ListModel and not TableModel? Well, it is an implementation detail. The table looks like a table, but it is really a modified ListView, where each list element is a table row.

A new requirement: The table shall contain sections.

Qt can do that. Nice. Less work for me, or so I thought. Here is the entire documentation of the section feature for TableViews:

section group

section.property : string

section.criteria : enumeration

section.delegate : Component

section.labelPositioning : enumeration


These properties determine the section labels.

See also ListView.section.

Isn't that great? A piece of art! To be fair, ListView.section is a link. Guess what waits behind that link:

section group

section.property : string

section.criteria : enumeration

section.delegate : Component

section.labelPositioning : enumeration


These properties determine the expression to be evaluated and appearance of the section labels.

Now, where have I seen that before?

OK, now I'm unfair. The text actually continues:

section.property holds the name of the property that is the basis of each section.

After that, it's talking about ListView not automatically sorting data, stacking order (which has the magical property of being 2), and performance issues. So, I need to set section.property in my TableView (that is actually a mutated ListView, or to say it nice, that inherits from ListView) to some magical name of some property. Umm, yes, the part left of the colon is obvious.

There is a code example. And even a picture of how a sectioned ListView should look like. Let's look at the code:

// The delegate for each section header Component { id: sectionHeading Rectangle { width: container.width height: childrenRect.height color: "lightsteelblue" Text { text: section font.bold: true font.pixelSize: 20 } } } ListView { id: view anchors.top: parent.top anchors.bottom: buttonBar.top width: parent.width model: animalsModel delegate: Text { text: name; font.pixelSize: 18 } section.property: "size" section.criteria: ViewSection.FullString section.delegate: sectionHeading }

I'm not making this up. This is the EXACT copy of the example code.

So, section.property is set to "size" in this example. Why? What does that mean? I know that the delegate stuff is responsible for painting onto the screen, and apart from delegates, the only other interesting thing left is the model. It's something with the ID animalsModel. So there must be something named size in that model. Let's look at that model.

Oops, it's not in the example code. Never heard of SSCCE? Obviously not. Well, the ListView document fills a few pages, so maybe that model was previously used to explain other stuff? You wish. There is exactly one animalsModel, right in that non-working example. And that does not mean there is only one hit in that document. There is exactly one hit in the entire documentation! (Or at least Google is not able to find anything but copies of that single document and completely unrelated pages talking about experiments with animals.)

So, what's in a model like ListModel? RTFM, where "F" is not "fine". A ListModel is documented to have two properties. One is named count, the other is named dynamicRoles. No property named size. But let's read on:

The ListModel is a simple container of ListElement definitions, each containing data roles. The contents can be defined dynamically, or explicitly in QML.

No size property. No property at all.

There is also an example:

The following example shows a ListModel containing three elements, with the roles "name" and "cost".

import QtQuick 2.0 ListModel { id: fruitModel ListElement { name: "Apple" cost: 2.45 } ListElement { name: "Orange" cost: 3.25 } ListElement { name: "Banana" cost: 1.95 } }

Yeah, sure. Why use animals when you can use fruits to explain that? From there on, the examples get more and more complicated, but no animals and no size property. And nothing that attempts to place fruits into groups. But the example is roughly similar to how my model looked at that time.

Again, to be fair, there is a good hint in that document that I did not see. In this sentence from the third paragraph in the "detailed description":

Elements can be manipulated via the model using the setProperty() method, which allows the roles of the specified element to be set and changed.

What section.property in ListView / TableView expects is the name of a "role" in a ListElement (here's the doc), which is a user-choosen name of a property in all ListElements in a ListModel. Quoting ListElement:

List elements are defined like other QML elements except that they contain a collection of role definitions instead of properties. Using the same syntax as property definitions, roles both define how the data is accessed and include the data itself.

The names used for roles must begin with a lower-case letter and should be common to all elements in a given model. Values must be simple constants; either strings (quoted and optionally within a call to QT_TR_NOOP), boolean values (true, false), numbers, or enumeration values (such as AlignText.AlignHCenter).

So, redefining names of things in every document, and reusing names used for other things is a quite clever way to force clients to hire a consultant. No, we didn't hire one. I know consultants from three former jobs. I don't want consultants in my office. Not that kind of consultants.


I choose the hard way, searching Google for working examples, and finally found one (no animals, no fruits). It's pretty simple. Choose a name. "group" may be a good one. Add that name to each and every ListElement in the model. Choose any value you like for group, and assign the same value for all ListElements that should be in the same group / section.

Now, section.criteria. Qt/QML must somehow compare each ListElement's group name, and insert a section into the list / table whenever it changes. Take a deep breath before reading on:

section.criteria holds the criteria for forming each section based on section.property. This value can be one of:

  • ViewSection.FullString (default) - sections are created based on the section.property value.
  • ViewSection.FirstCharacter - sections are created based on the first character of the section.property value (for example, 'A', 'B', 'C' sections, etc. for an address book)

A case insensitive comparison is used when determining section boundaries.

And that's all. Either compare the entire value using some vagely specified compare function, or compare the first character using the same vagely specified compare function. There is no other way.

I don't want to think about how that compare function compares the "normal" I/i and the turkish variants with a dot on I and no dot on i. And I also don't want to know how the code behind ListView extracts a "character" from a Unicode string. Perhaps I should think at least about the former thing, because the group names in my application are translated to various languages. For now, I won't. I've spend way too much time with that documentation, and if it breaks for some exotic language, I'll fix it later. No one will get hurt if that table does not look as pretty as it should.


This problem may look trivial, because I've still simplified some things. Translations, generating model data from Office documents at compile time (using Perl, what else?!), look and feel of that table, optimizing for a small screen with tiny pixels, and so on. And of course, this is a propritary system, and the client will certainly be not amused if parts of the source code or significant implementation details pop up at every corner of the internet.

Now guess how much fun I had trying to find out how to draw graphs from data in a model that automatically update when the data in the model changes. In the end, I got rid of the model for drawing and instead passed a Javascript AoH around. It may not be as nice as a model, but it gets the job done in less time.

Alexander

--
Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://1191766]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others contemplating the Monastery: (9)
As of 2024-03-28 10:07 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found