How can I return resize logic of borders in Frameless Window?
The frame windows has this logic:
Code in QML:
import QtQuick
import QtQuick.Controls 2.5
import Qt5Compat.GraphicalEffects
import NR 1.0
Window {
id: mainWindow
width: 640
height: 720
visible: true
title: qsTr("Hello World")
flags: Qt.Window | Qt.FramelessWindowHint
color: "transparent"
// (1)
MouseArea {
id: bottomArea
height: 5
anchors {
bottom: parent.bottom
left: parent.left
right: parent.right
}
cursorShape: Qt.SizeVerCursor
onPressed: {
previousY = mouseY
}
onMouseYChanged: {
var dy = mouseY - previousY
mainWindow.setHeight(mainWindow.height + dy)
}
}
// Some code of another Items here
}
I tried this code for left side:
MouseArea {
id: leftSideMouseArea
anchors.fill: parent
property point lastMousePos: Qt.point(0, 0)
onPressed: { lastMousePos = Qt.point(mouseX, mouseY); }
onMouseXChanged: mainWindow.width += (mouseX + lastMousePos.x)
}
I put this code in (1) place, but it doesn't work - on click (without move) windows resize to the rigth and app crashes with error:
QQuickPaintedItem::textureProvider: can only be queried on the
rendering thread of an exposed window
This looks like on picture:
Can you help me?
Thanks!
Since Qt5.15, we have startSystemResize, which performs a native resizing and is recommended against using methods like comparing the click position to the current position.
The function is very simple; once you pass an edge, the window begins to resize.
An example of a frameless window is shown below:
CustomWindow.QML
Change the offset from the window's edges where the mouse can be pressed by using this property.
property int edgeOffest: 5
Also for moving the window as well You can use a DragHandler, which, when activated, calls startSystemMove.
Window {
width: 200; height: 100
color: '#fab'
flags: Qt.Window | Qt.FramelessWindowHint
DragHandler {
onActiveChanged: if(active) startSystemMove();
}
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
acceptedButtons: Qt.LeftButton
property int edges: 0;
property int edgeOffest: 5;
function setEdges(x, y) {
edges = 0;
if(x < edgeOffest) edges |= Qt.LeftEdge;
if(x > (width - edgeOffest)) edges |= Qt.RightEdge;
if(y < edgeOffest) edges |= Qt.TopEdge;
if(y > (height - edgeOffest)) edges |= Qt.BottomEdge;
}
cursorShape: {
return !containsMouse ? Qt.ArrowCursor:
edges == 3 || edges == 12 ? Qt.SizeFDiagCursor :
edges == 5 || edges == 10 ? Qt.SizeBDiagCursor :
edges & 9 ? Qt.SizeVerCursor :
edges & 6 ? Qt.SizeHorCursor : Qt.ArrowCursor;
}
onPositionChanged: setEdges(mouseX, mouseY);
onPressed: {
setEdges(mouseX, mouseY);
if(edges && containsMouse) {
startSystemResize(edges);
}
}
}
}
Preview
Final Notes
Still, I do not recommend developing a custom window with custom functionality, which forces you to handle a lot of functions while still not feeling like a native one.
However, there are a few github projects that offered some helper libraries for this, so take a look at those.
https://github.com/antonypro/QGoodWindow
https://github.com/wangwenx190/framelesshelper
I can't think of better way to do this
Here is a working example:
import QtQuick 2.15
import QtQuick.Window 2.15
Window {
id: window
width: 640
height: 480
visible: true
title: qsTr("Hello World")
flags: Qt.Window | Qt.FramelessWindowHint
Rectangle
{
id: dragItemRight
anchors.top: parent.top
anchors.bottom: parent.bottom
width: 5
color: "red"
x: window.width - width
onXChanged:
{
if (dragItemRightMouse.drag.active)
{
window.width = dragItemRight.x + width
}
}
MouseArea
{
id: dragItemRightMouse
anchors.fill: parent
drag.target: parent
drag.axis: Drag.XAxis
cursorShape: Qt.SizeHorCursor
drag.minimumX: 300
drag.onActiveChanged:
{
if (!drag.active)
{
dragItemRight.x = Qt.binding(function() { return window.width - width })
}
}
}
}
Rectangle
{
id: dragItemBottom
anchors.left: parent.left
anchors.right: parent.right
height: 5
color: "red"
y: window.height - height
onYChanged:
{
if (dragItemBottomMouse.drag.active)
{
window.height = dragItemBottom.y + height
}
}
MouseArea
{
id: dragItemBottomMouse
anchors.fill: parent
drag.target: parent
drag.axis: Drag.YAxis
cursorShape: Qt.SizeVerCursor
drag.minimumY: 300
drag.onActiveChanged:
{
if (!drag.active)
{
dragItemBottom.y = Qt.binding(function() { return window.height - height })
}
}
}
}
Rectangle
{
id: dragItemBottomRight
width: 5
height: 5
color: "green"
x: window.width - width
y: window.height - height
onYChanged:
{
if (dragItemBottomRightMouse.drag.active)
{
window.height = dragItemBottomRight.y + height
}
}
onXChanged:
{
if (dragItemBottomRightMouse.drag.active)
{
window.width = dragItemBottomRight.x + width
}
}
MouseArea
{
id: dragItemBottomRightMouse
anchors.fill: parent
drag.target: parent
drag.axis: Drag.XAndYAxis
drag.minimumX: 300
drag.minimumY: 300
cursorShape: Qt.SizeFDiagCursor
drag.onActiveChanged:
{
if (!drag.active)
{
dragItemBottomRight.x = Qt.binding(function() { return window.width - width })
dragItemBottomRight.y = Qt.binding(function() { return window.height - height })
}
}
}
}
}
I found the solution:
import QtQuick
import QtQuick.Controls 2.15
import Qt5Compat.GraphicalEffects
import NR 1.0
Window {
id: mainWindow
width: 640
height: 720
visible: true
title: qsTr("Hello World")
flags: Qt.Window | Qt.FramelessWindowHint
color: "transparent"
property point startMousePos
property point startWindowPos
property size startWindowSize
function absoluteMousePos(mouseArea) {
var windowAbs = mouseArea.mapToItem(null, mouseArea.mouseX, mouseArea.mouseY)
return Qt.point(windowAbs.x + mainWindow.x,
windowAbs.y + mainWindow.y)
}
MouseArea {
id: moveArea
anchors.fill: title
property point mPos;
onPressed: {
mPos = Qt.point(mouseX, mouseY)
}
onPositionChanged: {
mainWindow.setX(mainWindow.x + mouseX - mPos.x)
mainWindow.setY(mainWindow.y + mouseY - mPos.y)
}
}
MouseArea {
id: leftArea
anchors.top: parent.top
anchors.topMargin: 48
anchors.bottom: parent.bottom
anchors.bottomMargin: 5
cursorShape: Qt.SizeHorCursor
width: 5
onPressed: {
startMousePos = absoluteMousePos(leftArea)
startWindowPos = Qt.point(mainWindow.x, mainWindow.y)
startWindowSize = Qt.size(mainWindow.width, mainWindow.height)
}
onMouseXChanged: {
var abs = absoluteMousePos(leftArea)
var newWidth = Math.max(mainWindow.minimumWidth, startWindowSize.width - (abs.x - startMousePos.x))
var newX = startWindowPos.x - (newWidth - startWindowSize.width)
mainWindow.x = newX
mainWindow.width = newWidth
}
Rectangle {
anchors.fill: parent
color: "red"
}
}
MouseArea {
id: rightArea
width: 5
x: parent.width - rightArea.width
anchors.right: parent.rigth
anchors.top: parent.top
anchors.rightMargin: 5
anchors.topMargin: 48
anchors.bottom: parent.bottom
anchors.bottomMargin: 5
cursorShape: Qt.SizeHorCursor
onPressed: {
startMousePos = absoluteMousePos(rightArea)
startWindowPos = Qt.point(mainWindow.x, mainWindow.y)
startWindowSize = Qt.size(mainWindow.width, mainWindow.height)
}
onMouseXChanged: {
var abs = absoluteMousePos(rightArea)
var newWidth = Math.max(mainWindow.minimumWidth, startWindowSize.width + (abs.x - startMousePos.x))
mainWindow.width = newWidth
}
Rectangle {
anchors.fill: parent
color: "red"
}
}
MouseArea {
id: buttonArea
y: parent.height - buttonArea.height
height: 5
anchors.leftMargin: 5
anchors.left: parent.left
anchors.rightMargin: 5
anchors.right: parent.right
anchors.bottom: parent.bottom
cursorShape: Qt.SizeVerCursor
onPressed: {
startMousePos = absoluteMousePos(buttonArea)
startWindowPos = Qt.point(mainWindow.x, mainWindow.y)
startWindowSize = Qt.size(mainWindow.width, mainWindow.height)
}
onMouseYChanged: {
var abs = absoluteMousePos(buttonArea)
var newHeight = Math.max(mainWindow.minimumHeight, startWindowSize.height + (abs.y - startMousePos.y))
mainWindow.height = newHeight
}
Rectangle {
anchors.fill: parent
color: "red"
}
}
}
Related
My custom ComboBox works is correctly, but I do not understand how to up popup with list of element above of combobox.
ComboBox {
id: pointsPositionComboBox
anchors.fill: parent
displayText: qsTr("Position point")
model: map.pointPositionModel
textRole: "name"
delegate: Row {
Rectangle {
id: delegateRect
width: pointsPositionRect.width - removePointRect.width
height: pointsPositionRect.height
color: {
if (!delegateRectMouseArea.containsMouse)
return "#25263C"
else if (delegateRectMouseArea.containsMouse
&& !delegateRectMouseArea.pressed) {
return "#5F8C6D"
} else if (delegateRectMouseArea.containsMouse
&& delegateRectMouseArea.pressed) {
return "#73AB84"
}
}
Text {
text: model.name
color: "white"
width: parent.width - removePointItem.width
font.pixelSize: pointsPositionComboBox.font.pixelSize
anchors.topMargin: pointsPositionRect.height / 3
anchors.left: parent.left
anchors.leftMargin: 5
}
MouseArea {
id: delegateRectMouseArea
anchors.fill: delegateRect
hoverEnabled: true
acceptedButtons: Qt.LeftButton
onClicked: {
if (hasUserMenuProperty.hasUserMenu()
&& !globalUserManager.hasAccessToPrivilege("controlMap"))
return
const latitude = model.position.latitude
const longitude = model.position.longitude
map.center.latitude = latitude
map.center.longitude = longitude
map.zoomLevel = model.zoomLevel
globalMapProperty.bearing = model.bearing
pointsPositionComboBox.popup.close()
}
}
}
Rectangle {
id: removePointRect
width: 30
height: 30
z: pointsPositionComboBox.z + 1
color: {
if (!removePointItemMouseArea.containsMouse)
return "#25263C"
else if (removePointItemMouseArea.containsMouse
&& !removePointItemMouseArea.pressed) {
return "#73A081"
} else if (removePointItemMouseArea.containsMouse
&& removePointItemMouseArea.pressed) {
return "#52785E"
}
}
Image {
id: removePointItem
anchors.centerIn: parent
source: removePointItemMouseArea.containsMouse ? "qrc:/icons/compass/remove_point_en.svg"
: "qrc:/icons/compass/remove_point_dis.svg"
}
MouseArea {
id: removePointItemMouseArea
anchors.fill: removePointRect
hoverEnabled: true
acceptedButtons: Qt.LeftButton
onClicked: {
if (hasUserMenuProperty.hasUserMenu()
&& !globalUserManager.hasAccessToPrivilege("controlMap"))
return
map.pointPositionModel.remove(map.pointPositionModel.index(index, 0))
}
}
}
}
}
Component.onCompleted: {
pointsPositionComboBox.popup.contentItem.implicitHeight = Qt.binding(function () {
// Делаем отображение только 10 элементов (остальные скроллом только)
return Math.min(pointsPositionRect.height * 10,
pointsPositionComboBox.popup.contentItem.contentHeight)
})
}
How to display a list of combo box elements from the top, and not from the bottom as in the photo?
P.s. I set implicitHeight for displaying element of combobox in Component.onCompleted
P.s.2 Now it looks like picture:
I've used the implicitHeight of the Popup to set its y property. Have a look at the source below. The default behavior of the Popup is when ever it fits below it will open up below, if there is not enough space below it will open above the ComboBox. The following code is a tweaked version of the Customizing ComboBox example from the Qt documentation.
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.12
Window {
width: 400
height: 300
Row {
anchors.centerIn: parent
ComboBox {
id: control
model: ["First", "Second", "Third"]
delegate: ItemDelegate {
width: control.width
contentItem: Text {
text: modelData
color: "#21be2b"
font: control.font
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
}
highlighted: control.highlightedIndex === index
}
indicator: Canvas {
id: canvas
x: control.width - width - control.rightPadding
y: control.topPadding + (control.availableHeight - height) / 2
width: 12
height: 8
contextType: "2d"
Connections {
target: control
function onPressedChanged() { canvas.requestPaint(); }
}
onPaint: {
context.reset();
context.moveTo(0, 0);
context.lineTo(width, 0);
context.lineTo(width / 2, height);
context.closePath();
context.fillStyle = control.pressed ? "#17a81a" : "#21be2b";
context.fill();
}
}
contentItem: Text {
leftPadding: 10
rightPadding: control.indicator.width + control.spacing
text: control.displayText
font: control.font
color: control.pressed ? "#17a81a" : "#21be2b"
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
}
background: Rectangle {
implicitWidth: 120
implicitHeight: 40
border.color: control.pressed ? "#17a81a" : "#21be2b"
border.width: control.visualFocus ? 2 : 1
radius: 2
}
popup: Popup {
y: myCheckBox.checked ? -implicitHeight + 1 : control.height - 1
width: control.width
implicitHeight: contentItem.implicitHeight
padding: 1
contentItem: ListView {
clip: true
implicitHeight: contentHeight
model: control.popup.visible ? control.delegateModel : null
currentIndex: control.highlightedIndex
ScrollIndicator.vertical: ScrollIndicator { }
}
background: Rectangle {
border.color: "#21be2b"
radius: 2
}
}
}
CheckBox {
id: myCheckBox
text: qsTr("above")
}
}
}
I have a ListView which has dynamically added Items, and what I kinda want to know is how I could access items for example by index. Specifically, I want to have the color of the item rectangle change when I change the color using the Color Dialog. I guessed it should be maybe possible to first set a variable to the current item before calling the color dialog and then in the onAccepted method change the color of that item using the variable, but I don't know how to achieve anything of this in QML, because I am rather new to QML. Maybe you can offer even a cleaner way to change the color of the item's rectangle when the color dialog was closed (onAccepted). Thx for any help! :)
import QtQuick 2.0
import QtQuick.Window 2.2
import QtQuick.Controls 1.4
import QtQuick.Dialogs 1.2
import QtQuick.Controls.Styles 1.4
Rectangle {
id: listViewContainer
width: parent.width/10*9;
height: 50
Behavior on height {
NumberAnimation {
duration: 100;
}
}
gradient: Gradient {
GradientStop {position: 0.0; color: "white" }
GradientStop {position: 1.0; color: "silver" }
}
radius: 5
ColorDialog {
id: colorDialog
title: "Please choose a color"
onAccepted: {
console.log("You chose: " + colorDialog.color)
Qt.quit()
}
onRejected: {
console.log("Canceled")
Qt.quit()
}
}
Component {
id: playerDelegate
Item {
anchors.left: parent.left
anchors.right: parent.right
height: 50
Column {
Text { text: '<b>Name:</b> ' + name }
Row {
MouseArea {
width: 20
height: 20
onClicked: {
colorDialog.visible = true;
playerColor = colorDialog.color;
//open color dialog
}
Rectangle {
radius: 3
anchors.fill: parent
color: playerColor
}
}
}
}
}
}
ListView {
id: playerListView
anchors.fill: parent
model:
ListModel {
id: playerListViewModel;
ListElement {
name: "Bill Smith"
playerColor: "red"
}
}
delegate: playerDelegate
}
Button {
id: addPlayerButton
anchors.top: playerListView.bottom
anchors.left: playerListView.left
anchors.right: playerListView.right
style: ButtonStyle {
label: Text {
text: "Add new player"
horizontalAlignment: Text.Center
}
}
onClicked: {
root.addnewPlayer(playerListView); //dont worry about this method
playerListViewModel.append({name: "Billy", playerColor: "blue"});
listViewContainer.height += 50;
}
}
}
Here is a sure fire way to make a working colorDialog --
in playerDelegate
Component {
id: playerDelegate
Item {
anchors.left: parent.left
anchors.right: parent.right
height: 50
Column {
Text {
text: '<b>Name:</b> ' + name
}
/* Object to store value from color selector */
Item {
id: colorSelector
property color color: "#000000"
onColorChanged: { playerColor = color; }
}
/* box to display color */
Rectangle {
//Layout.fillWidth: true
height: 120
width: 120
anchors.left: button.right
//Layout.alignment: Qt.AlignHCenter
color: colorSelector.color
border.width: 1
border.color: "#000000"
}
/* button to open dialog -- can be mousearea or other clickable object */
Button {
id: button
text: "Browse..."
onClicked: {
colorDialog.color = colorSelector.color
colorDialog.open()
}
}
/* actual color dialog for this delegate */
ColorDialog {
id: colorDialog
modality: Qt.ApplicationModal
title: "Please choose a color"
onAccepted: colorSelector.color = currentColor
}
}
}
}
In traditional Qt (QWidget) I have one QMainWindow and some dynamically created QWidgets with the content and I change them that one was seen in main windows. What are the ways of achieving when I have couple qml files and I wants to be able to switch between them when eg clicking a button.
There are at least 3 options for solving this problem:
You can use the ready for this purpose component StackView. The point is that you will create 2 components at once and you'll be able to change them by clicking on the button.
Example:
import QtQuick 2.12
import QtQuick.Controls 2.5
ApplicationWindow {
id: window
visible: true
width: 640
height: 480
title: qsTr("Stack")
header: ToolBar {
contentHeight: toolButton.implicitHeight
ToolButton {
id: toolButton
text: stackView.depth > 1 ? "\u25C0" : "\u2630"
font.pixelSize: Qt.application.font.pixelSize * 1.6
onClicked: {
if (stackView.depth > 1) {
stackView.pop()
} else {
drawer.open()
}
}
}
Label {
text: stackView.currentItem.title
anchors.centerIn: parent
}
}
Drawer {
id: drawer
width: window.width * 0.66
height: window.height
Column {
anchors.fill: parent
ItemDelegate {
text: qsTr("Page 1")
width: parent.width
onClicked: {
stackView.push("Page1Form.qml")
drawer.close()
}
}
ItemDelegate {
text: qsTr("Page 2")
width: parent.width
onClicked: {
stackView.push("Page2Form.qml")
drawer.close()
}
}
}
}
StackView {
id: stackView
initialItem: "HomeForm.qml"
anchors.fill: parent
}
}
Use the Loader here you will dynamically load files during execution, the disadvantage of this method is that if you switch often, it will be time consuming.
Example:
import QtQuick 2.0
Item {
width: 200; height: 200
Loader { id: pageLoader }
MouseArea {
anchors.fill: parent
onClicked: pageLoader.source = "Page1.qml"
}
}
You can create a class in C++ that will be given to an already initialized QML object to an empty qml form. thus, mono place individual components into libraries and use them as plugins (use qqmlcomponent).
I would use a simple Loader, with a button that when is clicked, changes the source file of the Loader. Like so :
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
Window {
id: root
Rectangle {
id: page_main
color: "#202020"
anchors.fill : parent
Button {
id: button_page_1
width: 105
text: qsTr("Page 1")
anchors {
left: parent.left
top: parent.top
leftMargin: 6
topMargin: 0
}
checkable: true
onClicked: {
if (loader_main.source == "/page_1.qml") {
loader_main.source = ""
} else {
loader_main.source = "/page_1.qml"
button_page_2.checked = false
button_page_3.checked = false
}
}
}
Button {
id: button_page_2
width: 105
text: qsTr("Page 2")
anchors {
left: button_auto.right
top: parent.top
leftMargin: 6
topMargin: 0
}
checkable: true
onClicked: {
if (loader_main.source == "/page_2.qml") {
loader_main.source = ""
} else {
loader_main.source = "/page_2.qml"
button_page_1.checked = false
button_page_3.checked = false
}
}
}
Button {
id: button_page_3
width: 105
text: qsTr("Page 3")
anchors {
left: button_manual.right
top: parent.top
leftMargin: 6
topMargin: 0
}
checkable: true
onClicked: {
if (loader_main.source == "/page_page_3.qml") {
loader_main.source = ""
} else {
loader_main.source = "/page_page_3.qml"
button_page_1.checked = false
button_page_2.checked = false
}
}
}
}
Loader {
id: loader_main
y: 60
visible: true
anchors {
top: parent.top
bottom: parent.bottom
right: parent.right
left: parent.left
topMargin: 48
bottomMargin: 0
leftMargin: 0
rightMargin: 0
}
}
}
I am trying to get how to show C++ variables on a QML window, and also how to hande C++ properties or call C++ functions from there.
While i'm trying to follow various tutorials (most of them linked in other questions at this site) i can't get it work....
The "big plan" is that i would like to have a class UI_Updater which will expose to the QML all the data i want to show, and have the "real" app working underline (it will gather data using a socket or a serial).
Right now, all i want is to show 3 integers, changing them with a timer and/or by a couple of menu items.
This is all my code and the errors i get:
ui_updater.h
#ifndef UI_UPDATER_H
#define UI_UPDATER_H
#include <QObject>
#include <QtGui>
class UI_Updater : public QObject
{
Q_OBJECT
Q_PROPERTY(int countEjT
READ getcountEjT
WRITE setcountEjT
NOTIFY countEjTChanged
)
Q_PROPERTY(int countEjF
READ getcountEjF
WRITE setcountEjF
NOTIFY countEjFChanged
)
Q_PROPERTY(int countEjFNC
READ getcountEjFNC
WRITE setcountEjFNC
NOTIFY countEjFNCChanged
)
public:
explicit UI_Updater(QObject *parent = 0);
int getcountEjT();
int getcountEjF();
int getcountEjFNC();
void setcountEjT(int NewVal);
void setcountEjF(int NewVal);
void setcountEjFNC(int NewVal);
Q_INVOKABLE void increment(int i);
signals:
void countEjTChanged();
void countEjFChanged();
void countEjFNCChanged();
public slots:
void testSlot();
private:
int _countEjT;
int _countEjF;
int _countEjFNC;
QTimer *myTimer;
void OnTimeout();
};
#endif // UI_UPDATER_H
ui_updater.cpp
#include "ui_updater.h"
UI_Updater::UI_Updater(QObject *parent) :
QObject(parent)
{
myTimer = new QTimer(this);
myTimer->start(1000);
connect(myTimer, SIGNAL(timeout()), this, SLOT(testSlot()));
_countEjT = _countEjF = _countEjFNC = 0;
emit countEjTChanged();
emit countEjFChanged();
emit countEjFNCChanged();
}
void UI_Updater::setcountEjT(int NewVal)
{
_countEjT = NewVal;
emit countEjTChanged();
}
void UI_Updater::setcountEjF(int NewVal)
{
_countEjF = NewVal;
emit countEjFChanged();
}
void UI_Updater::setcountEjFNC(int NewVal)
{
_countEjFNC = NewVal;
emit countEjFNCChanged();
}
int UI_Updater::getcountEjT()
{
return _countEjT;
}
int UI_Updater::getcountEjF()
{
return _countEjF;
}
int UI_Updater::getcountEjFNC()
{
return _countEjFNC;
}
void UI_Updater::OnTimeout()
{
increment(0);
}
void UI_Updater::increment(int i)
{
if (i==0) {
++_countEjT;
}else{
_countEjT+=i;
}
emit countEjTChanged();
if (_countEjT%2 == 0) {
++_countEjF;
emit countEjFChanged();
}
if (_countEjF%4 == 0) {
++_countEjFNC;
emit countEjFNCChanged();
}
}
void UI_Updater::testSlot()
{
increment(2);
}
main.cpp
#include <QtGui/QGuiApplication>
#include <QQmlApplicationEngine>
#include <QtQml>
#include <QtQuick/QQuickView> // Necessario per QQuickWindow
#include "ui_updater.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
qmlRegisterType<UI_Updater>("Phase.UI_Updater", 1, 0, "UI_Updater");
QQmlApplicationEngine engine(QUrl("qrc:/qml/MainForm.qml"));
QObject *topLevel = engine.rootObjects().value(0);
QQuickWindow *window = qobject_cast<QQuickWindow *>(topLevel);
if ( !window ) {
qWarning("Error: Your root item has to be a Window.");
return -1;
}
window->show(); // Evita di dover settare visible: true nel file qml
return app.exec();
}
The QML windows is meant to have multiple pages. I'll copy the code only of the relevant one:
MainForm.qml
import QtQuick 2.1
import QtQuick.Controls 1.0
import QtQuick.Window 2.1
import Phase.UI_Updater 1.0
ApplicationWindow {
id: screen
width: 480
height: 272
//toolBar: {
//}
property int partition: height / 3
title: qsTr("Main Screen")
menuBar: MenuBar {
Menu {
title: qsTr("File")
MenuItem {
text: qsTr("Exit")
onTriggered: Qt.quit();
}
}
Menu {
title: qsTr("Pages")
MenuItem {
text: qsTr("Working")
onTriggered: currentPage = "pagWorking";
}
MenuItem {
text: qsTr("Graphics")
onTriggered: currentPage = "pagGraphics";
}
MenuItem {
text: qsTr("Setup")
onTriggered: currentPage = "pagSetup";
}
// These items should increment the variables....
MenuItem {
text: qsTr("Add One")
onTriggered: UI_Updater.countEjT = UI_Updater.countEjT + 1;
}
MenuItem {
text: qsTr("Increment")
onTriggered: UI_Updater.increment(1);
}
}
}
// GESTIONE PAGINE ---------------------------------------------------------
property variant pagesList: [
"pagWorking",
"pagSetup",
"pagGraphics"
];
property string currentPage: "pagWorking";
Repeater {
model: pagesList;
delegate: Loader {
id: pageLoader
x: 0
y: 0
anchors.rightMargin: 0
anchors.bottomMargin: 0
anchors.leftMargin: 0
anchors.topMargin: 0
active: false;
asynchronous: true;
anchors.fill: parent;
visible: (currentPage === modelData);
source: "%1.qml".arg(modelData);
onVisibleChanged: { loadIfNotLoaded(); }
Component.onCompleted: { loadIfNotLoaded(); }
function loadIfNotLoaded() {
// Carica la pagina la prima volta che ce n'è bisogno
if (visible && !active) {
active = true;
}
}
} // Fine Loader
}
UI_Updater {
countEjT: 0;
countEjF: 0;
countEjFNC: 0;
}
// Fine GESTIONE PAGINE ------------------------------------------------
}
pagWorking.qml
import QtQuick 2.0
import QtQuick.Controls 1.0
import Phase.UI_Updater 1.0
Rectangle {
id: pagBackground
width: 400
height: 250
gradient: Gradient {
GradientStop {
position: 0
color: "#010036"
}
GradientStop {
position: 1
color: "#08006b"
}
}
Label {
id: lPagTitle
x: 0
width: parent.width
height: 20
color: "#0e909c"
text: "Working"
font.bold: true
font.pointSize: 12
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
anchors.top: parent.top
anchors.topMargin: 0
}
GroupBox {
id: group_box1
x: 133
width: 120
height: 100
anchors.top: parent.top
anchors.topMargin: 20
anchors.horizontalCenter: parent.horizontalCenter
Text {
id: tTachimetro
x: 162
y: 50
width: 100
height: 30
color: "#d0d0ff"
text: qsTr("000.0°")
anchors.verticalCenterOffset: 5
anchors.verticalCenter: parent.verticalCenter
font.bold: true
style: Text.Normal
verticalAlignment: Text.AlignVCenter
font.pixelSize: 25
anchors.horizontalCenter: parent.horizontalCenter
horizontalAlignment: Text.AlignHCenter
}
Text {
id: labTach
x: 149
y: 30
width: 100
height: 20
color: "#a0a0ff"
text: qsTr("Position")
anchors.verticalCenterOffset: -20
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenterOffset: 0
verticalAlignment: Text.AlignVCenter
font.pixelSize: 15
anchors.horizontalCenter: parent.horizontalCenter
horizontalAlignment: Text.AlignHCenter
}
}
GroupBox {
id: group_box2
x: 204
width: 200
height: 74
anchors.horizontalCenterOffset: 100
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
anchors.topMargin: 120
Text {
id: tET
x: 147
y: 0
width: 110
height: 20
color: "#ff8000"
text: UI_Updater.countEjT.toString();
anchors.right: parent.right
anchors.rightMargin: 0
anchors.top: parent.top
anchors.topMargin: 0
font.bold: true
style: Text.Normal
verticalAlignment: Text.AlignVCenter
font.pixelSize: 16
horizontalAlignment: Text.AlignRight
}
Text {
id: labET
x: 11
width: 70
height: 20
color: "#a0a0ff"
text: qsTr("Ej T")
anchors.top: parent.top
anchors.topMargin: 0
anchors.right: parent.right
anchors.rightMargin: 110
verticalAlignment: Text.AlignVCenter
font.pixelSize: 16
horizontalAlignment: Text.AlignRight
}
Text {
id: tEF
x: 130
y: 21
width: 110
height: 20
color: "#ff8000"
text: UI_Updater.countEjF.toString();
anchors.top: parent.top
font.bold: true
font.pixelSize: 16
verticalAlignment: Text.AlignVCenter
style: Text.Normal
anchors.rightMargin: 0
anchors.right: parent.right
anchors.topMargin: 21
horizontalAlignment: Text.AlignRight
}
Text {
id: labEF
x: 60
y: 21
width: 70
height: 20
color: "#a0a0ff"
text: qsTr("Ej F")
anchors.top: parent.top
font.pixelSize: 16
verticalAlignment: Text.AlignVCenter
anchors.rightMargin: 110
anchors.right: parent.right
anchors.topMargin: 21
horizontalAlignment: Text.AlignRight
}
Text {
id: tENCF
x: 130
y: 40
width: 110
height: 20
color: "#ff8000"
text: UI_Updater.countEjFNC.toString();
anchors.top: parent.top
font.bold: true
anchors.rightMargin: 0
style: Text.Outline
verticalAlignment: Text.AlignVCenter
font.pixelSize: 16
anchors.right: parent.right
anchors.topMargin: 42
horizontalAlignment: Text.AlignRight
}
Text {
id: labENCF
x: 60
y: 40
width: 70
height: 20
color: "#a0a0ff"
text: qsTr("Ej FNC")
anchors.top: parent.top
anchors.rightMargin: 110
verticalAlignment: Text.AlignVCenter
font.pixelSize: 16
anchors.right: parent.right
anchors.topMargin: 42
horizontalAlignment: Text.AlignRight
}
}
property bool shown: false;
state: "NASCOSTO";
onVisibleChanged: {
if (visible === false)
pagBackground.state = "NASCOSTO"
else if (visible === true)
pagBackground.state = "VISIBILE"
}
states: [
State {
name: "VISIBILE"
PropertyChanges { target: pagBackground; opacity: 1 }
}
,
State {
name: "NASCOSTO"
PropertyChanges { target: pagBackground; opacity: 0 }
}
]
//! [states]
//! [transitions]
transitions: [
Transition {
to: "NASCOSTO"
NumberAnimation { properties: "opacity"; duration: 1500; easing.type: Easing.OutExpo }
}
,
Transition {
to: "VISIBILE"
NumberAnimation { properties: "opacity"; duration: 1500; easing.type: Easing.OutExpo }
}
]
//! [transitions]
}
Now, it builds, but when i run i get:
TypeError: Cannot call method 'toString' to undefned
and clicking on the menu item "Increment i get
TypeError: object [object Object] has no method 'increment'
Now seems to me that my object UI_Updater was'nt really istanciated.... even if the editor sees it.
What should i do?
Thanks
The error message seem quite clear, you are not invoking increment on an UI_Updater instance but on UI_Updater type.
Try to put an id on your UI_Updater, then you can call methods declare by your UI_Updater C++ class. Here a little update in your MainForm.qml :
import QtQuick 2.1
import QtQuick.Controls 1.0
import QtQuick.Window 2.1
import Phase.UI_Updater 1.0
ApplicationWindow {
id: screen
width: 480
height: 272
//toolBar: {
//}
property int partition: height / 3
title: qsTr("Main Screen")
menuBar: MenuBar {
Menu {
title: qsTr("File")
MenuItem {
text: qsTr("Exit")
onTriggered: Qt.quit();
}
}
Menu {
title: qsTr("Pages")
MenuItem {
text: qsTr("Working")
onTriggered: currentPage = "pagWorking";
}
MenuItem {
text: qsTr("Graphics")
onTriggered: currentPage = "pagGraphics";
}
MenuItem {
text: qsTr("Setup")
onTriggered: currentPage = "pagSetup";
}
// These items should increment the variables....
MenuItem {
text: qsTr("Add One")
onTriggered: updater.countEjT = updater.countEjT + 1;
}
MenuItem {
text: qsTr("Increment")
onTriggered: updater.increment(1);
}
}
}
// GESTIONE PAGINE ---------------------------------------------------------
property variant pagesList: [
"pagWorking",
"pagSetup",
"pagGraphics"
];
property string currentPage: "pagWorking";
Repeater {
model: pagesList;
delegate: Loader {
id: pageLoader
x: 0
y: 0
anchors.rightMargin: 0
anchors.bottomMargin: 0
anchors.leftMargin: 0
anchors.topMargin: 0
active: false;
asynchronous: true;
anchors.fill: parent;
visible: (currentPage === modelData);
source: "%1.qml".arg(modelData);
onVisibleChanged: { loadIfNotLoaded(); }
Component.onCompleted: { loadIfNotLoaded(); }
function loadIfNotLoaded() {
// Carica la pagina la prima volta che ce n'è bisogno
if (visible && !active) {
active = true;
}
}
} // Fine Loader
}
UI_Updater {
id: updater
countEjT: 0;
countEjF: 0;
countEjFNC: 0;
}
// Fine GESTIONE PAGINE ------------------------------------------------
}
I have a simple list view. I need the user to be able to manipulate the font size (visual impairment issues). QML quite happily calculates new width and height for the listView items but since the strings are different lengths that leads to listView that looks like badly stacked boxes. What I need is for it to look like a rectangle the width of the longest string and wrap if it reaches the edge of the window. I figure I make the listView item backgrounds transparent and calculate the width of the rectangle the listView is in to fit the updated font size. I have tried a few ways to do this and have not managed to make it work.
Any clues? code below (data coming from c++)
import QtQuick 2.0
Rectangle
{
id: theMenu
property double fontSize: menuManager.menuFontPointSize
property double menuWidth: menuManager.menuItemHeight
Component
{
id: menuEntryDelegate
Rectangle
{
id: menuItemContainer
width: menuEntry.width
height: menuEntry.height * 1.25
anchors.top: prompts.bottom
property double fontSize: theMenu.fontSize
state: ListView.isCurrentItem ? "selected" : "notselected"
Text
{
id: menuEntry
font.pointSize: fontSize
//width: parent.width
wrapMode: Text.WordWrap
text: displayText
clip: true
}
MouseArea
{
hoverEnabled: false
anchors.fill: parent
onClicked: menuHolder.currentIndex = index
onDoubleClicked: menuManager.displayMenu(menuHolder.currentIndex)
}
states:
[
State
{
name: "selected"
PropertyChanges
{
target: menuItemContainer
color: "#FAFCD9"
}
PropertyChanges
{
target: icon
source: iconUrl
}
PropertyChanges
{
target: prompts
text: getPrompt()
}
PropertyChanges
{
target: menuEntry
color: "black"
}
},
State
{
name: "notselected"
PropertyChanges
{
target: menuItemContainer
color: "black"
}
PropertyChanges
{
target: menuEntry
color: "white"
}
},
State
{
name: "hidden"
PropertyChanges
{
target: menuItemContainer
color: "green"
}
}
]
}
}
Rectangle
{
id: menuContainer
width: menuManager.menuWidth
height: (50 * 9) //TBD
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: prompts.bottom
color: "purple"
ListView
{
id: menuHolder
model: menuModel
anchors.fill: parent
opacity: 1
/* header: Rectangle
{
TextBox {}
}*/
header: Rectangle
{
width: menuHolder.width
height: 50
color: "#2A51A3"
Text
{
id: header
anchors.centerIn: parent
text: "FIX" + currentMenu.displayText
font.pointSize: currentMenu.fontPointSize
color: "green"
width: parent.width
wrapMode: Text.WordWrap
}
}
delegate: menuEntryDelegate
focus: true
add: Transition
{
NumberAnimation { properties: "x,y" }
}
Keys.onPressed:
{
if(event.key === Qt.Key_F1)
{
theMenu.z = -1
}
else if(event.key === Qt.Key_F3)
{
theMenu.z = 1
}
else if(event.key === Qt.Key_F2)
{
menuManager.menuFontPointSize *= menuManager.scale
theMenu.fontSize = menuManager.menuFontPointSize
}
else if(event.key === Qt.Key_F10)
{
scaleFactor -= 0.1
menuContainer.scale = scaleFactor
promptsContainer.scale = scaleFactor
//promptsContainer.z = 1
}
else if(event.key === Qt.Key_Right)//zoom in
{
menuContainer.x +=10
}
else if(event.key === Qt.Key_Left)//zoom out
{
menuContainer.x -=10
}
else if(event.key === Qt.Key_Home)//go back to Main menu
{
menuManager.displayMainMenu();
theMenu.fontSize = menuManager.menuFontPointSize
}
//Ways to select a menu item
else if((event.key >= Qt.Key_1 && event.key <= Qt.Key_9)
|| event.key === Qt.Key_Return || event.key === Qt.Key_Enter)
{
if(event.key >= Qt.Key_1 && event.key <= Qt.Key_9)
{
menuHolder.currentIndex = event.key - Qt.Key_1;
}
menuManager.displayMenu(menuHolder.currentIndex);
theMenu.fontSize = menuManager.menuFontPointSize
}
menuEntryDelegate.updateIcon()
}
}
}
}#
Just use :
width: parent.width;
In menuItemContainer Rectangle element in your delegate component, to fill the ListView width.
And then add :
anchors { left: parent.left; right: parent.right }
To menuEntry Text element to give it a max size, so it can know when it must wrap (it will extend to the right infinitely else).