I have a QML textInput element like this:
TextBox.qml
FocusScope {
id: focusScope
property int fontSize: focusScope.height -30
property int textBoxWidth: parent.width * 0.8
property int textBoxHeight: 45
property string placeHolder: 'Type something...'
property bool isUserInTheMiddleOfEntringText: false
width: textBoxWidth
height: textBoxHeight
Rectangle {
width: parent.width
height: parent.height
border.color:'blue'
border.width: 3
radius: 0
MouseArea {
anchors.fill: parent
onClicked: {
focusScope.focus = true
textInput.openSoftwareInputPanel()
}
}
}
Text {
id: typeSomething
anchors.fill: parent; anchors.rightMargin: 8
verticalAlignment: Text.AlignVCenter
text: placeHolder
color: 'red'
font.italic: true
font.pointSize: fontSize
MouseArea {
anchors.fill: parent
onClicked: {
focusScope.focus = true
textInput.openSoftwareInputPanel()
}
}
}
MouseArea {
anchors.fill: parent
onClicked: {
focusScope.focus = true
textInput.openSoftwareInputPanel()
}
}
TextInput {
id: textInput
anchors {
right: parent.right
rightMargin: 8
left: clear.right
leftMargin: 8
verticalCenter: parent.verticalCenter
}
focus: true
selectByMouse: true
font.pointSize: fontSize
}
Text {
id: clear
text: '\u2717'
color: 'yellow'
font.pointSize: 25
opacity: 0
visible: readOnlyTextBox ? false : true
anchors {
left: parent.left
leftMargin: 8
verticalCenter: parent.verticalCenter
}
MouseArea {
anchors.fill: parent
onClicked: {
textInput.text = ''
focusScope.focus = true;
textInput.openSoftwareInputPanel()
}
}
}
states: State {
name: 'hasText'; when: textInput.text != ''
PropertyChanges {
target: typeSomething
opacity: 0
}
PropertyChanges {
target: clear
opacity: 0.5
}
}
transitions: [
Transition {
from: ''; to: 'hasText'
NumberAnimation {
exclude: typeSomething
properties: 'opacity'
}
},
Transition {
from: 'hasText'; to: ''
NumberAnimation {
properties: 'opacity'
}
}
]
}
I want to add autocomplete and suggestions like google search to this text box. Autocomple get data from database and database return a list of dictionaries by a pyside SLOT.(or c++ slot)
How I can do this work?
Take a look at this code: https://github.com/jturcotte/liquid/blob/master/qml/content/SuggestionBox.qml
I bet it will do the job.
Edit:
Code that linked above is somewhat complicated and requires C++ backend, so I simplified it and made pure Qml example application, that you can play with, edit a little and apply to your needs. Sources can be found here. Most important things there are:
This implementation of SuggestionBox that uses some sort of model as it's source for completing/suggesting something
Its signal itemSelected(item) will be emitted every time user clicks on item
Main component of application that binds its LineEdit component to SuggestionBox
Note that code is quite rough and written for a sake of example.
I was looking for something very similar: a QML autocomplete component built around QML TextField, rather than the lower-level, more flexible but also more work intensive TextInput as in the question.
Since I could not find that, I implemented it. If anyone wants to use it: it's licensed under MIT and available as part of an application I am developing. You find the component in src/qml/AutoComplete.qml, and the application may serve as usage example. Features:
highlighting of autocompleted characters in bold, as in Google Search
Key bindings (navigating with arrow keys, Return / Enter, Esc to close completion box, Esc Esc to unfocus)
uses a simple QStringList as model for now, with the application showing how to update the model with live SQL database queries when the next key is pressed
heavily documented code, so it should be easy enough to adapt
Let me know if this is useful, I might then package it as a Qt QPM package or even try to make it mature enough to be added to the QML UI library KDE Kirigami.
Related
Repeater {
model: myModel.buttonParameters
delegate: Button
{
width: 47
height: 47
contentItem: Text {
id: content
text: modelData.name
font.family: MyStyle.fontFamily
fontSizeMode: Text.Fit
font.pixelSize: 30
font.styleName: "Bold"
topPadding: height / 6
color: modelData.visibility ? MyStyle.colorFromSeriesName(this.text) : MyStyle.dividerColor
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
background: Rectangle
{
anchors.fill: parent
radius: 4
color: MyStyle.backgroundColor
border.color:MyStyle.dividerColor
border.width: 2
}
onClicked: {
cntModel.visibilityOfChartChanged(modelData.name, "plot");
}
}
}
On the C++ side.
myModel.buttonParameters is a QList<MyModel*> , where MyModel is a class inherited from QObject.
Q_PROPERTY(QVariant buttonParameters READ buttonParametersList NOTIFY buttonParametersChanged)
QVariant buttonParametersList()
{
return QVariant::fromValue(m_buttonParametersList );
}
The problem is that with a certain change in the model
(the signal buttonParametersChanged is sent), namely,
if the number of objects was equal to one and after the update there
is also one object, but with different characteristics, no redrawing
takes place, the old button remains. Moreover, it somehow depends on
the runtime. Also, if I remove the line with color, the model will update.
The issue is that buttonParametersChanged is a signal that triggers on the assignment of a new container (QList<>) to buttonParameters. It doesn't trigger on changes to the contents of an existing QList<> assigned to that property.
However, you can always manually trigger buttonParametersChanged when you know you've modified the contents of the QList<> which should give you the effect you want.
Note, a QML ListModel or C++ equivalent would likely be more appropriate for this use case. Many QML components are designed to specifically integrate with them and handle the cases of container contents changing.
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
Good evening,
We are programming a DaVinci DM8168 board with Qt5.7.1 and QtQuick2.7
we would like to pass a list of type:
QList from C ++ to QML
to be able to draw it asynchronously and above all efficiently. The list is modified by a thread on C ++.
We cannot state the update frequence of this list at all: it can be just one time per second or dozens! It depends by the target enviroment.
our solution (partially working) is the following:
On the C++ side we are using QPROPERTIES:
an extract of our code:
MyClass.h
class MyClass: public Observer
{
Q_OBJECT
Q_PROPERTY(QList<QObject*> getFailuresHistory READ getFailuresHistory NOTIFY failuresHistoryListChanged)
QList<QObject*>getFailuresHistory(){return failuresHistory_;}
signals:
void failuresHistoryListChanged();
}
then my .cpp we emit the signal every time the list changes ( not true at all: if more elements of the list changes at the same time we emit the signal only once in order to optimize the application)
emit failuresHistoryListChanged();
Then on QML "side" we do the following:
Item
{
// ... other code
//DELAGATE
Component
{
id: failuresDelegateHistory
Item
{
id: listHistoryItem
width: listHistory.width
height: 10
anchors.leftMargin: 5
Row
{
Text{
id: failureIdHistory
width: 30
text: qsTr(modelData.codeFailure) + mainSettingsHandler.emptyString
color: mainWindow.currentStyleColor.colorRed
font.pixelSize: 16
horizontalAlignment: Text.AlignHCenter
}
Text {
id: descriptionFailIdHistory
width: (listHistoryItem.width-failureIdHistory.width)*0.5
color:mainWindow.currentStyleColor.colorWhite
maximumLineCount: 1
text: qsTr(modelData.descriptionFail) + mainSettingsHandler.emptyString
wrapMode: Text.WrapAnywhere
horizontalAlignment: Text.AlignJustify
font.pixelSize: 12//16
}
Text
{
id: columnTimeStampFailureHistory
width: (listHistoryItem.width-descriptionFailIdHistory.width-failureIdHistory.width- 2)
maximumLineCount: 1
text: modelData.dateFail + mainSettingsHandler.emptyString
wrapMode: Text.WrapAnywhere
color:mainWindow.currentStyleColor.colorWhite
font.pixelSize: 12
horizontalAlignment: Text.AlignJustify
}
}
}
}
//LIST
ListView
{
id: list1
y: labelHistory.y + labelHistory.height+5
width: (FileConfiguration.widthMenuArea - space - 5)*0.5-1-space
height: (270 - space) *itemsPerPage
model: myClass.getFailuresHistory//failuresHistoryTable.getFailuresHisory
delegate: failuresDelegateHistory
spacing: 10
enabled: false
}
// ... other code
}
OK;
as I said before this solution is partially working because is very, very slow!
When the:
emit failuresHistoryListChanged();
on c++ side is called, the list is drawn but the software is "slow". We have keepalive message that shall be set every 300 ms and that is not sent anymore for a couple of seconds.
We need a more efficent way to pass the list to the QML.
if we comment the emit the software works fine within its deadlines.
So our purpose is: we would like to pass in a very efficent way this list to QML. Do you have solutions that involve a modification of the code that we have shown you or new architectures? do you have any examples? we are not experts in qml.
If you need more information ask us.
We thank you in advance, we have been trying to solve the problem for a long time.
thanks
I am using a QML TextArea as a text input. It seems like this TextArea automatically removes leading and trailing whitespaces. If I start the text with a couple of spaces, the cursor doesn't move. If I then type a normal character, the cursor jumps the amount of spaces typed before.
I managed to include trailing whitespaces by changing the TextOptions of the underlying QQuickTextDocument on the C++-side, but couldn't find a way to show leading whitespaces.
Is there a way to do that?
EDIT: It's really just a (very slightly styled) TextArea
T.TextArea {
id: control
padding: 6
opacity: enabled ? 1 : 0.8
font.pixelSize: TextStyle.font.size.regular
color: Color.gray_09
selectionColor: Color.cyan_066
selectedTextColor: color
wrapMode: TextEdit.Wrap
InputFieldPlaceholderText {
anchors.top: parent.top
anchors.left: parent.left
anchors.topMargin: parent.topPadding
anchors.leftMargin: parent.leftPadding
textBox: control
}
TextEditFormatter {
id: textAreaFormatter
Component.onCompleted: onCompleted()
}
}
Which I call like this:
Flickable {
anchors.fill: parent
flickableDirection: Flickable.VerticalFlick
TextArea {
id: textArea
anchors.fill: parent
}
}
For Completion: This is what the TextEditFormatter does, but not using it doesn't change the behaviour...:
void TextEditFormatter::onCompleted()
{
auto document = parent()
->property("textDocument")
.value<QQuickTextDocument *>()
->textDocument();
auto textOptions = document->defaultTextOption();
textOptions.setFlags(QTextOption::IncludeTrailingSpaces);
document->setDefaultTextOption(textOptions);
}
I'm looking for a simple way to make widgets for a touch-screen that will allow users to set the time and IP address on the computer running the code and provide a simple (uppercase Latin-alphabetic) name.
This question is not about how to actually set the system time or IP address; I'm just looking for information about how to make the graphical widgets themselves.
What I want is for each editable property (time, address, and name) to be divided into "scrollable" fields, where the fields for "time" are hours, minutes, possibly seconds, and AM/PM/24-hr, and the fields for address/name are the individual characters. Each field would have an arrow above and below it, and touching on an arrow would scroll through the valid values for that field.
I think this is a pretty common UX pattern, especially in meatspace (e.g. on alarm clocks), but just in case it's not clear what I'm trying to describe, here's an example with a user editing the "name" property:
^^^
BN
vvv
User presses "down" below the "N":
^^^
BO
vvv
User presses "down" below the empty space:
^^^^
BOA
vvvv
...and again on the same down-arrow:
^^^^
BOB
vvvv
I'm writing this using C++14 with Qt 5. (If worst comes to worst, I'd be open to writing a separate app using a different language and/or framework, but I'm not asking for framework suggestions here; if you have one, let me know and I'll open a corresponding question on Software Recommendations SE.)
I don't see anything in the Qt 5 widget library like this; most of the input widgets are text fields. QSpinBox looks somewhat promising, but the arrows are probably too small for my touchscreen, and using a separate spinbox for each letter would probably be confusing and ugly.
I don't really know enough about Qt or GUI-programming in general to feel confident trying to write my own widgets from scratch, but this interface looks simple enough that I would expect a couple lines of QML would get me well on my way.
ListView as well as PathView can produce the desired result with slightly different behaviors and slightly different performances. Differently from ListView, PathView is circular, i.e. elements can be iterated continuously by using just one of the selection controls. It is also easier to fully customize the behavior of the path in PathView via the PathAttribute type. Anyhow path customization seems not to be a required feature, according to the question.
If you implement the solution via a ListView you should ensure that just one element is shown and that any model is processed.
Component {
id: spinnnnnnnner
Column {
width: 100
height: 110
property alias model: list.model
property string textRole: ''
spacing: 10
Item {
width: 100
height: 25
Text { anchors.centerIn: parent; text: "-"; font.pixelSize: 25; font.bold: true }
MouseArea {anchors.fill: parent; onClicked: list.decrementCurrentIndex() }
}
ListView {
id: list
clip: true
width: 100
height: 55
enabled: false // <--- remove to activate mouse/touch grab
highlightRangeMode: ListView.StrictlyEnforceRange // <--- ensures that ListView shows current item
delegate: Text {
width: ListView.view.width
horizontalAlignment: Text.AlignHCenter
font.pixelSize: 50
font.bold: true
text: textRole === "" ? modelData :
((list.model.constructor === Array ? modelData[textRole] : model[textRole]) || "")
}
}
Item {
width: 100
height: 25
Text { anchors.centerIn: parent; text: "+"; font.pixelSize: 25; font.bold: true }
MouseArea {anchors.fill: parent; onClicked: list.incrementCurrentIndex() }
}
}
}
The checks over the model ensure that any type of model can be passed to the component. Here is an example using three very different models:
import QtQuick 2.5
import QtQuick.Window 2.2
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.1
ApplicationWindow {
visible: true
width: 400
height: 300
ListModel {
id: mod
ListElement {texty: "it1"}
ListElement {texty: "it2"}
ListElement {texty: "it3"}
}
Row {
Repeater {
id: rep
model: 3
delegate: spinnnnnnnner
Component.onCompleted: {
rep.itemAt(0).model = mod // listmodel
rep.itemAt(0).textRole = "texty"
rep.itemAt(1).model = 10 // number model
//
rep.itemAt(2).model = ["foo", "bar", "baz"] // array model
}
}
}
}
PathView implementation is not so different from the ListView one. In this case it is sufficient to define a vertical path and specify that just one one element is visible at a time via pathItemCount. Finally, setting preferredHighlightBegin/preferredHighlightEnd ensures that the visible element is centered in the view. The revisited component is the following:
Component {
id: spinnnnnnnner
Column {
width: 100
height: 110
property alias model: list.model
property string textRole: ''
spacing: 10
Item {
width: 100
height: 25
Text { anchors.centerIn: parent; text: "-"; font.pixelSize: 25; font.bold: true }
MouseArea {anchors.fill: parent; onClicked: list.decrementCurrentIndex() }
}
PathView {
id: list
clip: true
width: 100
height: 55
enabled: false // <--- remove to activate mouse/touch grab
pathItemCount: 1
preferredHighlightBegin: 0.5
preferredHighlightEnd: 0.5
path: Path {
startX: list.width / 2; startY: 0
PathLine { x: list.width / 2; y: list.height }
}
delegate: Text {
width: PathView.view.width
horizontalAlignment: Text.AlignHCenter
font.pixelSize: 50
font.bold: true
text: textRole === "" ? modelData :
((list.model.constructor === Array ? modelData[textRole] : model[textRole]) || "")
}
}
Item {
width: 100
height: 25
Text { anchors.centerIn: parent; text: "+"; font.pixelSize: 25; font.bold: true }
MouseArea {anchors.fill: parent; onClicked: list.incrementCurrentIndex() }
}
}
}