Show big text in qml - qt

Trying to display a text file in qml. The file size is about 3 megabytes. At the same time there are:
long opening of the form,
large waste of memory.
Tried use ScrollView, Flickable, Text and TextArea. How can these problems be avoided?
QML
ScrollView {
id: idScrollView
anchors {
fill: parent
margins: Dimensions.x(15)
}
Text {
id: idContent
anchors {
left: parent.left
right: parent.right
rightMargin: Dimensions.x(20)
}
text: viewmodel.getLogText()
font.pixelSize: Dimensions.y(10)
wrapMode: Text.Wrap
}
}
C++
QString MainViewModel::getLogText()
{
const int maxSz = 1024 * 200;
QString result;
QFile file(ALog::filePath());
if (file.open(QIODevice::ReadOnly))
{
if (file.size() > maxSz)
file.seek(file.size() - maxSz);
QByteArray arr = file.read(maxSz);
result = QString::fromLatin1(arr);
if (file.size() > maxSz)
result = QString("Skip %1 Kb\n\n").arg((file.size() - maxSz)/1024) + result;
file.close();
}
return result;
}

Found a partial solution. It loads much faster and consumes several times less memory. Among the disadvantages-there is no possibility to convert Text to TextArea to be able to select the text to copy to the clipboard.
property variant stringList: null
function updateText() {
stringList = viewmodel.getLogText().split('\n')
idContentListView.positionViewAtEnd()
}
ListView {
id: idContentListView
model: stringList
anchors {
fill: parent
margins: Dimensions.x(15)
}
delegate: Text {
anchors {
left: parent.left
right: parent.right
}
text: model.modelData
font.pixelSize: Dimensions.y(10)
textFormat: Text.PlainText
wrapMode: Text.Wrap
}
ScrollBar.vertical: ScrollBar {}
}

Related

How to create a tableview (5.12 )with column headers?

I am creating a Table using the new qml tableview (Qt 5.12).
I am able to create a model in C++ and able to populate the model in tabular format along with scrollbar.How do I add column headers to this table?
Code:
import QtQuick 2.12
import QtQuick.Controls 2.5
import Qt.labs.qmlmodels 1.0
//import QtQuick.Controls.Styles 1.4
import TableModel 0.1
Rectangle {
id:table
border.width: 3
border.color: 'dark blue'
QtObject{
id:internals
property int rows:0
property int col:0
property int colwidth:0
property var columnName:[]
}
function setRows(num){ internals.rows = num}
function setCols(num){ internals.col = num}
function setColWidth(num){internals.colwidth = num}
function setColNames(stringlist){
if(stringlist.length > 1)
internals.col = stringlist.length
dataModel.setColumnName(stringlist);
}
function addRowData(stringlist){
var len = stringlist.length
if(len >0)
{
dataModel.addData(stringlist)
}
}
TableModel {
id:dataModel
}
TableView{
id:tbl
anchors.top: headerCell
anchors.fill: parent
//columnSpacing: 1
//rowSpacing: 1
clip: true
ScrollBar.horizontal: ScrollBar{}
ScrollBar.vertical: ScrollBar{}
model:dataModel
Component{
id:datacell
Rectangle {
implicitWidth: 100
implicitHeight: 20
color: 'white'
border.width: 1
border.color: 'dark grey'
Text {
id:txtbox
anchors.fill: parent
wrapMode: Text.NoWrap
clip: true
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
text: display
}
}
}
}
function init(){
console.log("Calling init")
tbl.delegate= datacell
}
}
Currently TableView does not have headers so you should create it, in this case use Row, Column and Repeater.
On the other hand you must implement the headerData method and you must do it Q_INVOKABLE.
class TableModel : public QAbstractTableModel
{
Q_OBJECT
public:
// ...
Q_INVOKABLE QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
// ...
TableView {
id: tableView
model: table_model
// ...
Row {
id: columnsHeader
y: tableView.contentY
z: 2
Repeater {
model: tableView.columns > 0 ? tableView.columns : 1
Label {
width: tableView.columnWidthProvider(modelData)
height: 35
text: table_model.headerData(modelData, Qt.Horizontal)
color: '#aaaaaa'
font.pixelSize: 15
padding: 10
verticalAlignment: Text.AlignVCenter
background: Rectangle { color: "#333333" }
}
}
}
Column {
id: rowsHeader
x: tableView.contentX
z: 2
Repeater {
model: tableView.rows > 0 ? tableView.rows : 1
Label {
width: 60
height: tableView.rowHeightProvider(modelData)
text: table_model.headerData(modelData, Qt.Vertical)
color: '#aaaaaa'
font.pixelSize: 15
padding: 10
verticalAlignment: Text.AlignVCenter
background: Rectangle { color: "#333333" }
}
}
}
The complete example you find here.
If you're using Qt 5.15, you can use a HorizontalHeaderView for column labels.
https://doc.qt.io/qt-5/qml-qtquick-controls2-horizontalheaderview.html
There's also VerticalHeaderView for row labeling.
https://doc.qt.io/qt-5/qml-qtquick-controls2-verticalheaderview.html
I'm new to the QML. I came to the answer of eyllanesc so many times through my struggle with the new TableView (qt 5.12+), so I wanna thank you and share what helped me even more.
It's this video:
Shawn Rutledge - TableView and DelegateChooser: new in Qt 5.12
part of Qt Virtual Tech Summit 2019
The discussed code
It's a bit long but he covers
the differences between old and new TableView
how to create universal model for the views
resizable headers
different representation per column type - DelegateChooser
sortable columns
column reorder

MenuBar like behaviour in QML

I want to have menubar like behaviour when clicked on a rectangle. Whenever a rectangle is clicked a model is updated and a ListView is shown. I want this ListView to disappear whenever another Rectangle is clicked and the listmodel should not be appended with each click. Here is my sample code.
Card.qml
Rectangle {
id: card
width: 50
height: 100
color: "pink"
Item {
id: rec
width: 50
anchors.bottom: parent.top
ListModel {
id: menuListModel
}
Component {
id: delegate
Rectangle {
width: 50
height: 20
color: "blue"
Text {
anchors.fill: parent
anchors.centerIn: parent
text: commandText
}
}
}
ListView {
anchors.fill: parent
model:menuListModel
delegate: delegate
interactive: false
}
}
MouseArea {
anchors.fill: parent
onClicked: {
rec.height += 40;
menuListModel.append({"commandText" : "Act"});
menuListModel.append({"commandText" : "Set"});
}
}
}
main.qml
Item {
width: 120
height: 200
Row {
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
spacing: 10
Card {
id: card1
}
Card {
id: card2
}
}
}
Also I want to call certain function upon clicking menu buttons i.e. Act and Set.
Edit
The following function is called with appropriate flags when a card(here rectangle) is clicked.
property int command_activate: 0x0001
property int command_summon: 0x0002
property int command_spsummon: 0x0004
property int command_mset: 0x0008
property int command_sset: 0x0010
property int command_repos: 0x0020
property int command_attack: 0x0040
property int command_list: 0x0080
function showMenu(flag) {
if(flag & command_activate) {
rec.height += 15;
menuListModel.append({"commandText" : "Activate"});
}
if(flag & command_summon) {
rec.height += 15;
menuListModel.append({"commandText" : "Normal Summon"});
}
if(flag & command_spsummon) {
rec.height += 15;
menuListModel.append({"commandText" : "Special Summon"});
}
if(flag & command_mset) {
rec.height += 15;
menuListModel.append({"commandText" : "Set"});
}
if(flag & command_sset) {
rec.height += 15;
menuListModel.append({"commandText" : "Set"});
}
if(flag & command_repos) {
if(position & pos_facedown) {
rec.height += 15;
menuListModel.append({"commandText" : "Flip Summon"});
}
else if(position & pos_attack) {
rec.height += 15;
menuListModel.append({"commandText" : "To Defense"});
}
else {
rec.height += 15;
menuListModel.append({"commandText" : "To Attack"});
}
}
if(flag & command_attack) {
rec.height += 15;
menuListModel.append({"commandText" : "Attack"});
}
if(flag & command_list) {
rec.height += 15;
menuListModel.append({"commandText" : "View"});
}
}
So, in short when a card is clicked a menu has to be shown according to the flag on the top of the card.
There are several problems with your code.
You cannot name your delegate "delegate". When you do this, the ListView uses its own delegate property to set itself, leading to nothing happening.
Also, why don't you just statically fill your ListView then use the visible property to toggle whether you display it or not? If you want it to disappear whenever another Card is clicked, you may have to use the focus property.
Indeed, setting focus to true will reset the focus of all other Items within the focus scope. Something like this might work:
Rectangle {
id: card
...
property alias model: list.model
Component {
id: mydelegate
Rectangle {
width: 50
height: 20
...
}
}
ListView {
id: list
visible: card.activeFocus
anchors.bottom: parent.top
width: card.width
delegate: mydelegate
interactive: false
height: childrenRect.height
}
MouseArea {
anchors.fill: parent
onClicked: {
card.focus = !card.focus
}
}
}
As for calling a function, you could add the name of the function to call directly in the ListModel. Add a MouseArea in your delegate, and send a signal on clicked. Then, you just have to call the matching slot (Agreed, the this[slot]() syntax is a bit hacky).
In Card.qml
Rectangle {
id: card
...
property alias model: list.model
signal itemClicked(string slot)
Component {
id: mydelegate
Rectangle {
...
MouseArea
{
anchors.fill: parent
onClicked: {
if(model.slot)
itemClicked(slot)
}
}
}
}
...
}
In main.qml
...
Card
{
model: ListModel {
ListElement{commandText: "Act"; slot: "act"}
ListElement{commandText: "Set"; slot: "set"}
}
function act()
{
print("act triggered")
}
function set()
{
print("set trigggered")
}
onItemClicked: { this[slot]() }
}
...
As I can't comment, to ask for clarification, I will state my assumptions from what I understood:
You have a number of ListModel that contain the information of the menu that should pop up, when clicking on the corresponding rectangle item in the bar.
The ListView that pops up shall disappear, once you clicked any other rectangle in this bar. (Also: if something outside the bar or menu is clicked? Or an item in the bar is selected?)
I'd go with only one ListView, and update/change the model, as well as the position (e.g. margin) once a button is clicked, so you don't have multiple unused objects flying around.
To not show anything, I think it is sufficient, to set an empty model.
You can also have a list of functions.
Row {
id: row
property var f: [a, b, c]
function a() { console.log('a'); }
function b() { console.log('b'); }
function c() { console.log('c'); }
width: 300
height: 50
Repeater {
id: rep
anchors.fill: parent
delegate: button
model: 3
Rectangle {
id: button
width: 98
height: 50
border.color: 'black'
Text {
text: "a"
anchors.fill: parent
}
MouseArea {
anchors.fill: parent
onClicked: row.f[index]()
}
}
}
}
e.g. will call the function a, b, or c - depending on the index of the rectangle.
I hope, so far, I could help.

C++ class exposed to QML error in fashion TypeError: Property '...' of object is not a function

I've successfully exposed a C++ class to QML. It is registered and found in Qt Creator. It's purpose is to connect to a database, as shown in following code:
#ifndef UESQLDATABASE_H
#define UESQLDATABASE_H
#include <QObject>
#include <QtSql/QSqlDatabase>
class UeSqlDatabase : public QObject
{
Q_OBJECT
Q_PROPERTY(bool m_ueConnected READ isConnected WRITE setConnected NOTIFY ueConnectedChanged)
private:
bool m_ueConneted;
inline void setConnected(const bool& ueConnected)
{ this->m_ueConneted=ueConnected; }
public:
explicit UeSqlDatabase(QObject *parent = 0);
Q_INVOKABLE inline const bool& isConnected() const
{ return this->m_ueConneted; }
~UeSqlDatabase();
signals:
void ueConnectedChanged();
public slots:
void ueConnectToDatabase (const QString& ueStrHost, const QString& ueStrDatabase,
const QString& ueStrUsername, const QString& ueStrPassword);
};
#endif // UESQLDATABASE_H
However, when I try to call method isConnected() from the following QML code
import QtQuick 2.0
Rectangle
{
id: ueMenuButton
property string ueText;
width: 192
height: 64
radius: 8
states: [
State
{
name: "ueStateSelected"
PropertyChanges
{
target: gradientStop1
color: "#000000"
}
PropertyChanges
{
target: gradientStop2
color: "#3fe400"
}
}
]
gradient: Gradient
{
GradientStop
{
id: gradientStop1
position: 0
color: "#000000"
}
GradientStop
{
position: 0.741
color: "#363636"
}
GradientStop
{
id: gradientStop2
position: 1
color: "#868686"
}
}
border.color: "#ffffff"
border.width: 2
antialiasing: true
Text
{
id: ueButtonText
color: "#ffffff"
text: qsTr(ueText)
clip: false
z: 0
scale: 1
rotation: 0
font.strikeout: false
anchors.fill: parent
font.bold: true
style: Text.Outline
textFormat: Text.RichText
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
font.pixelSize: 16
}
MouseArea
{
id: ueClickArea
antialiasing: true
anchors.fill: parent
onClicked:
{
uePosDatabase.ueConnectToDatabase("127.0.0.1",
"testDb",
"testUser",
"testPassword");
if(uePosDatabase.isConnected()==true)
{
ueMenuButton.state="ueStateSelected";
}
else
{
ueMenuButton.state="base state"
}
}
}
}
I get the following error:
qrc:/UeMenuButton.qml:92: TypeError: Property 'isConnected' of object UeSqlDatabase(0x1772060) is not a function
What am I doing wrong?
You have this error because you have declared the property isConnected in C++ but you're calling it from QML in the wrong way: uePosDatabase.isConnected is the correct way, not uePosDatabase.isConnected().
If you want to call the function isConnected() you should change its name to differate it from the property, like getIsConnected(). Given your property declaration, you neither need to call this function directly nor you need to make it callable from QML with the Q_INVOKABLE macro.
You must first create the object you want in QML code and then use the function:
your QML Code:
import QtQuick 2.0
import QSqlDatabase 1.0
Rectangle {
id: ueMenuButton
QSqlDatabase {
id: uePosDatabase
}
.
.
.
MouseArea
{
id: ueClickArea
antialiasing: true
anchors.fill: parent
onClicked: {
console.log(uePosDatabase.isConnected())
}
}
}
Be aware in case of an error some lines earlier in the same .js file can lead to this problem. The following code will not be executed.

How to flip RadioButton?

Is it possible to flip a RadioButton? By default the circle is aligned left and the text is aligned right. I'd like to position the text to the left and circle to the right. With LayoutMirroring.childrenInherit: true I position text to the left, but the circle is still on the left.
Column {
id: column1
x: -265
y: 219
width: 104
height: 45
spacing: 5
LayoutMirroring.enabled: true
LayoutMirroring.childrenInherit: true
ExclusiveGroup { id: diamTypes }
RadioButton { text: "one"; exclusiveGroup: diamTypes }
RadioButton { text: "two"; exclusiveGroup: diamTypes }
}
Add a Text item (with the desired name) within the RadioButton item and give a relative x and y position to it. Leave the text property of the RadioButton blank.
RadioButton {
id: radioButtonID
x: 319 // Button position
y: 46
Text {
x: -60 // Relative text position to the radio button
y: 3
text: "Radio Button"
font.pointSize: 8
color: "black"
}
}
Here's how to do it using widgets:
Inherit from QRadioButton, add a new QLabel for text, make the original QRadioButton::text empty, and adjust the padding. Like this:
QFont myFont;
QFontMetrics fm(myFont);
int indicatorPadding = WINDOW_WIDTH - 25;
this->setStyleSheet(QString("QRadioButton { "
"text-align: left; "
"padding-left: " + QString::number(indicatorPadding ) + "px; "
"} ")
);
int paddingLeft = indicatorPadding - RADIO_BTN_WIDTH - fm.width(radioText->text());
radioText->setStyleSheet("padding-top: 7px; padding-left: " + QString::number(paddingLeft) + ";");
Here is my full class for RadioButtons for RTL languages (it also aligns text accordingly):
my_radiobutton.h:
#ifndef MYRADIOBUTTON_H
#define MYRADIOBUTTON_H
#include <QRadioButton>
#include <QEvent>
#include <QLabel>
#include "constants.h"
class MyRadioButton : public QRadioButton
{
private:
QLabel* radioText = NULL;
public:
explicit MyRadioButton(QWidget *parent=0);
explicit MyRadioButton(const QString &text, QWidget *parent=0);
void setText(const QString &text);
QString text() const;
protected:
void alignText();
void changeEvent(QEvent * event);
};
#endif // MYRADIOBUTTON_H
my_radiobutton.cpp:
#include "my_radiobutton.h"
bool isRTL(); //returns true if RTL language, and false otherwise
MyRadioButton::MyRadioButton(QWidget *parent)
: QRadioButton(parent)
{
if(isRTL()){
radioText = new QLabel(this);
}
}
MyRadioButton::MyRadioButton(const QString &text, QWidget *parent)
: QRadioButton(text,parent)
{
if(isRTL()){
radioText = new QLabel(this);
}
}
void MyRadioButton::setText(const QString &text)
{
if(isRTL()){
QRadioButton::setText("");
if(radioText){
radioText->setText(text);
}
}
else{
QRadioButton::setText(text);
}
alignText();
}
QString MyRadioButton::text() const
{
if(isRTL() && radioText){
return radioText->text();
}
else{
return QRadioButton::text();
}
}
void MyRadioButton::alignText()
{
if(isRTL() && radioText){
QFont myFont;
QFontMetrics fm(myFont);
int indicatorPadding = WINDOW_WIDTH - 25;
this->setStyleSheet(QString("QRadioButton { "
"text-align: left; "
"padding-left: " + QString::number(indicatorPadding ) + "px; "
"} ")
);
int paddingLeft = indicatorPadding - RADIO_BTN_WIDTH - fm.width(radioText->text());
radioText->setStyleSheet("padding-top: " + QString::number(CENTER_OF_LINE) + "px; padding-left: " + QString::number(paddingLeft) + ";");
}
else{
this->setStyleSheet(QString("QRadioButton { "
"text-align: left; "
"padding-left: " TEXT_PADDING "px; "
"} ")
);
}
}
void MyRadioButton::changeEvent(QEvent * event)
{
if (event->type() == QEvent::LanguageChange){
alignText();
}
QWidget::changeEvent(event);
}
constants.h:
#define WINDOW_WIDTH 220
#define RADIO_BTN_WIDTH 10
#define CENTER_OF_LINE ((LINE_HEIGHT/2) - (FONT_SIZE/2)) // ==7
#define TEXT_PADDING STRINGIFY(10)
Result:
Go to the design view and set direction to "left to right".
RadioButtonStyle can also be used for the purpose. The alignment should be set up somehow:
RadioButton {
text: "bla-bla";
exclusiveGroup: diamTypes
style: RadioButtonStyle {
label: Label {
text: control.text
font.pointSize: 14
anchors.margins: 0
}
indicator: Rectangle {
implicitWidth: 16
implicitHeight: 16
radius: 9
border.color: control.activeFocus ? "darkblue" : "gray"
border.width: 1
Rectangle {
anchors.fill: parent
visible: control.checked
color: "#555"
radius: 9
anchors.margins: 4
}
}
}
}
you can do it easy:
RadioButton {
id: offlineMapParentBox
text: qsTr("radio button")
anchors.left: parent.left
LayoutMirroring.enabled: true
}
or if you work on left to right and right to left multilanguage app you can do it like below:
RadioButton {
id: offlineMapParentBox
text: qsTr("Offline Map")
anchors.left: parent.left
LayoutMirroring.enabled: isLeftToRight ? false : true
}
thanks

Qt/QML: Is it allowed to have a model view as a delegate?

Is it possible to have a model view (such as ListView or GridView) in a delegate of another model view?
I'm using a ListView (LV1) whose delegate also has a ListView (LV2). Everything before and after LV2 is displayed correctly (and that's in the delegate of LV1). However, nothing is shown in LV1.
My model is from this: http://qt-project.org/wiki/How_to_use_a_QSqlQueryModel_in_QML . I suppose that the SQL query is correct since it returns the correct information when ran externally to Qt. According to debug messages, that all queries is clearly executed.
I have two queries, one being updated with the other. That works good.
Figure #1 shows the debug messages, and it points out that the QML does call the data function and information is indeed returned from the database.
Figure #2 shows the QML window at the time of figure #1: we can clearly see that the layout is messed up and nothing is shown.
Verbatim #1 is the QML code.
Verbatim #2 is an excerpt of the main which shows that I connect the main model to the secondary model though a signal called updateBindings.
Verbatim #3 is my updated SqlQueryModel. The main difference with the tutorial is the signal and slots.
Verbatim #4 is for reference, since this class is used in verbatim #3.
Any input is valued, I've been struggling with this for a few days now. Thanks.
Verbatim #1 (QML):
import QtQuick 1.1
import "Styles"
Rectangle {
id : window;
width : 750
height : 500
signal searchSignal(string msg)
signal resetSignal()
property variant subtypes: {"S":"Status (digital)", "V":"Value (analog)"}
property variant phases: {'66': 'Final Mass Properties', '67': 'Final Thruster Aignment/Solar Array Inst.', '60': 'Final Functional Performance', '114': 'Maneuver (Wheel Mode) Overlay (LTO)', '88': 'Troubleshooting Test Phase 9', '89': 'Troubleshooting Test Phase 10', '111': 'Battery 1 Reconditioning Overlay (LTO)', '110': 'Battery Recharge Overlay (LTO)', '113': 'Maneuver (SK Mode) Overlay (LTO)', '112': 'Battery 2 Reconditioning Overlay (LTO)', '68': 'Flight Battery Functional', '83': 'Troubleshooting Test Phase 4', '80': 'Troubleshooting Test Phase 1', '81': 'Troubleshooting Test Phase 2', '86': 'Troubleshooting Test Phase 7', '87': 'Troubleshooting Test Phase 8', '84': 'Troubleshooting Test Phase 5', '85': 'Troubleshooting Test Phase 6', '24': 'Post T/V Performance ( Ambient)', '26': 'Alignments - Pre Dynamics', '27': 'Antenna Installation', '20': 'Cold (Equinox) Functional Plateau', '21': 'Transition Monitor (cold to hot)', '22': 'Hot ( Winter Solstice)', '82': 'Troubleshooting Test Phase 3', '28': 'Solar Array Installation', '40': 'CATR', '41': 'ESD (PFM Only)', '1': 'North Panel Electrical integration', '3': 'SSM/Bus Module Electrical Integration', '2': 'South Panel Electrical integration', '5': 'South and bus Electrical Integration', '4': 'North and Bus Electrical integration', '6': 'S/C Electrical Integration', '75': 'Hazardous Processing Facility Operations', '39': 'Final Alignment Verif. CATR Preps', '77': 'Launch Complex Performance', '76': 'HPF Payload testing', '108': 'Lunar Eclipse Event Overlay (LTO)', '109': 'Battery Discharge Overlay (LTO)', '70': 'Launch Base Functional Performance', '102': 'On Orbit', '103': 'Station Keeping', '100': 'Prime Shift Orbit Raising', '101': 'Off Shift Orbit Raising', '106': 'Quiescent Non-Eclipse State of Health (LTO)', '107': 'Earth Eclipse Season Overlay (LTO)', '104': 'Eclipse', '105': 'Post Eclipse', '10': 'Panel RF Testing', '13': 'Integrated System Reference Performance', '12': 'End to End Satting', '15': 'Transition Monitoring (Ambient to Hot Solstice)', '14': 'Pre Thermal Vacuum performance (Ambient)', '16': 'Summer Solstice Functional Plateau', '18': 'Transition Monitoring (Hot to Cold)', '31': 'Sine Vibration', '30': 'Acoustic Vibration', '36': 'Post Dynamics Performance', '35': 'Deployments', '34': 'Launch Vehicle Adapter Fit Check', '65': 'Propulsion Global Helium'}
ListView {
anchors.fill: parent
focus: true
highlightRangeMode: ListView.StrictlyEnforceRange
orientation: ListView.Horizontal
snapMode: ListView.SnapOneItem
model: tcModel
delegate: Component {
Item {
id: item
width: window.width; height: window.height
Flickable {
id: mainScrollView
contentHeight: parent.height
contentWidth: parent.width
anchors.fill: parent
clip: true
focus: true
interactive: true
Column{
id: dataCol
spacing: 10
/* Buttons */
Row{
width: window.width
Button{
text: "Go"
action.onClicked: searchSignal("TLM_NO LIKE '" + num.text + "%'")
bgColor: "lightgreen"
width: window.width/10; height: window.width/30
}
Button{
text: "Reset"
action.onClicked: resetSignal()
bgColor: "lightgrey"
width: window.width/10; height: window.width/30
}
}
/* */
Grid{
columns: 5
spacing: 10
Text {
text: "Mnemonic"
font.bold: true
}
Text {
text: "Name"
font.bold: true
}
Text {
text: "Type"
font.bold: true
}
Text {
text: "Subtype"
font.bold: true
}
Text {
text: "Category"
font.bold: true
}
/* NEW LINE */
TextEdit {
id: num
text: TLM_NO
}
Text {
text: TLM_NAME
}
Text {
text: TLM_TYPE
}
Text {
text: (SUBTYPE ? subtypes[SUBTYPE] : "Unknown")
}
Text {
text: TLM_CATEGO
}
} /* End grid */
Separator{}
Text{
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
text: "<u>Limits</u>"
id: limitLabel
}
/* Limits */
ListView {
//anchors.top: limitLabel.bottom
header:
Row{
height: 30
clip: true
anchors.margins: 4
Text {
text: "Phase name"
font.bold: true
}
Text {
text: "Red low"
font.bold: true
}
Text {
text: "Yellow low"
font.bold: true
}
Text {
text: "Yellow high"
font.bold: true
}
Text {
text: "Red high"
font.bold: true
}
}
delegate: Item {
id: delegate
width: delegate.ListView.view.width;
height: 30
clip: true
anchors.margins: 4
Row {
anchors.margins: 4
anchors.fill: parent
spacing: 4;
Text {
text: PHASE_NO?phases[PHASE_NO]:"N/A"
}
Text {
text: CRIT_LO?CRIT_LO:"N/A"
}
Text {
text: NOM_LO?NOM_LO:"N/A"
}
Text {
text: NOM_HI?NOM_HI:"N/A"
}
Text {
text: CRIT_HI?CRIT_HI:"N/A"
}
}
}
model: limitModel
height: window.height / 3
} /* End limits grid view*/
Separator{}
Text{
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
text: "end limits"
}
}/* End column*/
}
}
}
}
}
Verbatim #2 (main.cpp , excerpt):
/* Let's create all the models associated with this given user form.*/
/* We need the viewer to connect all the signals and to set context properties.*/
QmlApplicationViewer viewer;
SqlQueryModel *mainModel;
QList<SqlQueryModel*>::iterator umIt;
QHash<QString, SqlQueryModel*> modelCnx = QHash<QString, SqlQueryModel*>();
for(umIt = selectedUF.userModels.begin(); umIt != selectedUF.userModels.end(); umIt++){
SqlQueryModel *model = *umIt;
/* Let's go through each binding of the UserModel and connect create the bindings in the model. */
model->exec();
viewer.rootContext()->setContextProperty(model->modelName, model);
/* If this is the selected search model, let's save it to connect the signals later. */
if (model->modelName == selectedUF.searchModel){
mainModel = model;
}else{
QObject::connect(mainModel, SIGNAL(bindedValueChanged(QString, QString, QVariant)),
model, SLOT(updateBindings(QString,QString,QVariant)));
}
}
viewer.setMainQmlFile(QString("qml/" + selectedUF.qml));
QObject::connect((QObject*)viewer.rootObject(), SIGNAL(searchSignal(QString)),
mainModel, SLOT(search(QString)));
QObject::connect((QObject*)viewer.rootObject(), SIGNAL(resetSignal()),
mainModel, SLOT(reset()));
viewer.showExpanded();
return app->exec();
Verbatim #3 (sqlquerymodel.cpp):
#include "sqlquerymodel.h"
SqlQueryModel::SqlQueryModel(QObject *parent) :
QSqlQueryModel(parent)
{
this->modelName = "";
this->query = "";
bindings = QHash<QString, QList<ModelBinding> >();
}
void SqlQueryModel::setQuery(const QString &query, const QSqlDatabase &db)
{
if(query.length() == 0){
this->query = query;
}
QSqlQueryModel::setQuery(query, db);
generateRoleNames();
QSqlError le = this->lastError();
if (le.isValid()){
QString errMsg("Query was:\n%1\n\nError:\n%2");
QMessageBox::critical(0, "Query error", errMsg.arg(query).arg(le.text()));
return;
}
}
void SqlQueryModel::setQuery(const QSqlQuery &query)
{
QSqlQueryModel::setQuery(query);
generateRoleNames();
QSqlError le = this->lastError();
if (le.isValid()){
QString errMsg("Query was:\n%1\n\nError:\n%2");
qDebug() << errMsg.arg(query.lastQuery()).arg(le.text());
/* We're not using a MessageBox because it causes a segfault for some reason. */
//QMessageBox::critical(0, "Query error", errMsg.arg(query.lastQuery()).arg(le.text()));
return;
}
}
/**
* #brief SqlQueryModel::exec This function prepares and executes the query.
*/
void SqlQueryModel::exec()
{
qDebug() << "Executing query on model" << this->modelName;
/* Let's create a QSqlQuery. It will store the query and we'll bind values to it.*/
QSqlQuery sQuery;
/* If we initialize the query with the string, then we CANNOT use bind (won't work and won't show any error).*/
sQuery.prepare(this->query);
/** Now, let's go through all the models associated to this instance.
* For each of them, we'll bind its value. Note that we're avoiding making a copy by using the adresses
* cf. : http://stackoverflow.com/questions/17106243/qt-iterator-not-accessing-the-correct-object
**/
QHash<QString, QList<ModelBinding> >::iterator bindingsIt;
for (bindingsIt = bindings.begin(); bindingsIt != bindings.end(); bindingsIt++){
QList<ModelBinding>::iterator eachBindingIt;
QList<ModelBinding>& curBinding = *bindingsIt;
for(eachBindingIt = curBinding.begin(); eachBindingIt != curBinding.end(); eachBindingIt++){
ModelBinding& binding = *eachBindingIt;
binding.bindToQuery(&sQuery);
}
}
/* Let's not forget to execute this query, or nothing will be displayed in the QML. */
sQuery.exec();
qDebug() << "----------------";
qDebug() << sQuery.lastQuery();
QMapIterator<QString, QVariant> i(sQuery.boundValues());
while (i.hasNext()) {
i.next();
qDebug() << i.key().toAscii().data() << "="
<< i.value().toString().toAscii().data();
}
qDebug() << "----------------";
this->setQuery(sQuery);
}
void SqlQueryModel::generateRoleNames()
{
QHash<int, QByteArray> roleNames;
for( int i = 0; i < record().count(); i++) {
roleNames[Qt::UserRole + i + 1] = record().fieldName(i).toAscii();
}
qDebug() << "Generating role names for" << modelName;
setRoleNames(roleNames);
}
void SqlQueryModel::search(QString str){
QString nQuery (query);
nQuery.append(" WHERE ").append(str);
qDebug() << "Set query to: " << nQuery;
this->setQuery(nQuery);
QSqlError le = this->lastError();
if (le.isValid()){
QString errMsg("An error occurred while loading the file.\n\nQuery was:\n%1\n\nError:\n%2");
QMessageBox::critical(0, "Database error", errMsg.arg(nQuery).arg(le.text()));
}
}
QVariant SqlQueryModel::data(const QModelIndex &index, int role) const
{
QVariant value = QSqlQueryModel::data(index, role);
if(role < Qt::UserRole){
value = QSqlQueryModel::data(index, role);
}else{
int columnIdx = role - Qt::UserRole - 1;
QModelIndex modelIndex = this->index(index.row(), columnIdx);
value = QSqlQueryModel::data(modelIndex, Qt::DisplayRole);
qDebug() << modelName << ":" << record().fieldName(columnIdx) << "=" << value;
emit bindedValueChanged(modelName, record().fieldName(columnIdx), value);
}
return value;
}
void SqlQueryModel::reset()
{
qDebug() << "Resetting original SQL query to: " << query;
this->setQuery(query);
}
void SqlQueryModel::updateBindings(QString modelName, QString col, QVariant val)
{
/** Now, let's go through all the models associated to this instance.
* We're going to see if the new signal we got is used for this model (for model name and column name).
* If so, we'll assigned it and then we'll execute this query by calling exec().
**/
bool anyValueChanged = false;
QHash<QString, QList<ModelBinding> >::iterator bindingsIt;
for (bindingsIt = bindings.begin(); bindingsIt != bindings.end(); bindingsIt++){
QList<ModelBinding>::iterator eachBindingIt;
QList<ModelBinding>& curBinding = *bindingsIt;
for(eachBindingIt = curBinding.begin(); eachBindingIt != curBinding.end(); eachBindingIt++){
ModelBinding& binding = *eachBindingIt;
if(bindingsIt.key() == modelName && binding.column == col){
binding.value = val;
anyValueChanged = true;
}
}
}
if (anyValueChanged){
this->exec();
}
}
Verbatim #4 (modelbinding.cpp):
#include "modelbinding.h"
ModelBinding::ModelBinding(QString placeholder, QString column, QVariant value)
{
this->placeholder = placeholder;
this->column = column;
this->value = value;
}
void ModelBinding::bindToQuery(QSqlQuery *sQuery)
{
sQuery->bindValue(placeholder, value);
}
The item in your base ListView delegate is sized to the window's height and width. I believe this would effectively hide any other delegates from the root level ListView because the first delegate would take up the entire screen.

Resources