Evgenii Legotckoi
Sept. 14, 2017, 2:05 a.m.

QML - Tutorial 031. Disable the system window framing in QML and write the code for processing the move and resize windows

Content

If for some reason, you decide to abandon the standard system window frame and implement all the Title Bar windows on your own in QML, then you can easily implement this with QML, which is even easier than in C ++, in my opinion. For acquaintance you can look at the article on customization of the application window in the AIMP style . There is a significant amount of code for implementing the mechanics of moving the window, as well as its resize. The advantage of QML in this case is that QML immediately provides the layout of the application, so you can use MouseArea and anchors to immediately determine the desired processing when clicking and moving the mouse without any special calculation of the cursor position in the window area.

And the mechanics of calculating the change in size and position will be similar to the one used in the article on customizing the application in C ++. That is, it will be necessary to remember the position where the mouse button was pressed, and then, relative to this position, make a resize and move the application window until the button is released.


As you can see in the figure above, you will need to create 5 MouseArea regions, four of which will be responsible for application resizing, and the fifth central for the application migration. That is, you can move the application by holding the mouse button anywhere in the fifth central area.

If you need a special Title Bar, then it can also be responsible for moving the application window. That is, it is not necessary to make the entire central region sensitive to movement.

main.qml

The application is created by default and only the main.qml file will be changed, which I will give as an example.

  1. import QtQuick 2.7
  2. import QtQuick.Controls 2.0
  3. import QtQuick.Layouts 1.3
  4.  
  5. ApplicationWindow {
  6. id: mainWindow
  7. visible: true
  8. width: 640
  9. height: 480
  10.  
  11. flags: Qt.FramelessWindowHint // Disable window frame
  12.  
  13. // Declare properties that will store the position of the mouse cursor
  14. property int previousX
  15. property int previousY
  16.  
  17. MouseArea {
  18. id: topArea
  19. height: 5
  20. anchors {
  21. top: parent.top
  22. left: parent.left
  23. right: parent.right
  24. }
  25. // We set the shape of the cursor so that it is clear that this resizing
  26. cursorShape: Qt.SizeVerCursor
  27.  
  28. onPressed: {
  29. // We memorize the position along the Y axis
  30. previousY = mouseY
  31. }
  32.  
  33. // When changing a position, we recalculate the position of the window, and its height
  34. onMouseYChanged: {
  35. var dy = mouseY - previousY
  36. mainWindow.setY(mainWindow.y + dy)
  37. mainWindow.setHeight(mainWindow.height - dy)
  38. }
  39. }
  40.  
  41. // Similar calculations for the remaining three areas of resize
  42. MouseArea {
  43. id: bottomArea
  44. height: 5
  45. anchors {
  46. bottom: parent.bottom
  47. left: parent.left
  48. right: parent.right
  49. }
  50. cursorShape: Qt.SizeVerCursor
  51.  
  52. onPressed: {
  53. previousY = mouseY
  54. }
  55.  
  56. onMouseYChanged: {
  57. var dy = mouseY - previousY
  58. mainWindow.setHeight(mainWindow.height + dy)
  59. }
  60. }
  61.  
  62. MouseArea {
  63. id: leftArea
  64. width: 5
  65. anchors {
  66. top: topArea.bottom
  67. bottom: bottomArea.top
  68. left: parent.left
  69. }
  70. cursorShape: Qt.SizeHorCursor
  71.  
  72. onPressed: {
  73. previousX = mouseX
  74. }
  75.  
  76. onMouseXChanged: {
  77. var dx = mouseX - previousX
  78. mainWindow.setX(mainWindow.x + dx)
  79. mainWindow.setWidth(mainWindow.width - dx)
  80. }
  81. }
  82.  
  83. MouseArea {
  84. id: rightArea
  85. width: 5
  86. anchors {
  87. top: topArea.bottom
  88. bottom: bottomArea.top
  89. right: parent.right
  90. }
  91. cursorShape: Qt.SizeHorCursor
  92.  
  93. onPressed: {
  94. previousX = mouseX
  95. }
  96.  
  97. onMouseXChanged: {
  98. var dx = mouseX - previousX
  99. mainWindow.setWidth(mainWindow.width + dx)
  100. }
  101. }
  102.  
  103. // The central area for moving the application window
  104. // Here you already need to use the position both along the X axis and the Y axis
  105. MouseArea {
  106. anchors {
  107. top: topArea.bottom
  108. bottom: bottomArea.top
  109. left: leftArea.right
  110. right: rightArea.left
  111. }
  112.  
  113. onPressed: {
  114. previousX = mouseX
  115. previousY = mouseY
  116. }
  117.  
  118. onMouseXChanged: {
  119. var dx = mouseX - previousX
  120. mainWindow.setX(mainWindow.x + dx)
  121. }
  122.  
  123. onMouseYChanged: {
  124. var dy = mouseY - previousY
  125. mainWindow.setY(mainWindow.y + dy)
  126. }
  127. }
  128. }
М
  • Sept. 16, 2017, 12:58 p.m.

Огромное спасибо

Евгений Миллер
  • Sept. 17, 2017, 1:53 a.m.

Драсте, спасибо за хороший урок.

Однако, остались вопросы:

При использовании флага "Qt.FramelessWindowHint", приложение исправно работает, но пропадает с панели задач и из приложений в диспетчере. Вследствие этого приложение, если его свернуть, становится неюзабельным, и с ним ничего нельзя сделать, кроме как закрыть через процессы в диспетчере.

Не подскажите, как это исправить, из-за чего это могло произойти, или, хотя бы, в какую сторону копать?

Дальше идёт код, что бы показать проблему:
import QtQuick 2.6
import QtQuick.Window 2.2

Window {
    visible: true
    width: 640
    height: 480
    flags: Qt.FramelessWindowHint
    title: qsTr("Hello World")

    TextEdit {
        id: textEdit
        text: qsTr("Enter some text...")
        verticalAlignment: Text.AlignVCenter
        anchors.top: parent.top
        anchors.horizontalCenter: parent.horizontalCenter
        anchors.topMargin: 20
        }
    }
}

Evgenii Legotckoi
  • Sept. 17, 2017, 12:29 p.m.

Это скорее всего баг Qt под конкретной операционной системой. Какую используете?
Дело в том, что я проверил Ваш код под свой рабочей системой (KDE Neon 5.8) и всё работает стабильно, без нареканий. То есть минимизация окна происходит без проблем. Под Windows проверить не могу, на данный момент у меня нет рабочих установок этой системы.

Евгений Миллер
  • Sept. 17, 2017, 2:06 p.m.

Использую Windows 7 Ultimate.

Причём, если использовать этот флаг при QT Widgets, а не QML, то всё работает правильно.
Evgenii Legotckoi
  • Sept. 17, 2017, 2:14 p.m.

Тогда это однознано баг, я бы глянул на официальном багтрекере Qt, есть ли информация об этом баге, и возможно стоит создать таск с этим багом.

Евгений Миллер
  • Sept. 17, 2017, 4:14 p.m.

Спасибо! Этим и займусь. Ещё попробую скинуть проект другу, посмотрю, как QT будет справляться там.

М
  • Oct. 18, 2017, 2:35 p.m.

После отключения системного обрамления не работают minimumHeight и minimumWidth. Что делать?


Evgenii Legotckoi
  • Oct. 18, 2017, 2:45 p.m.

Задать свои property в окне и проверять их в методах изменения размера для topArea, bottomArea, rightArea, leftArea.

В обработчиках onMouseYChanged, onMouseXChanged.
Из-за отключения обрамления не отслеживаются минимальные рамеры, поэтому нужно следить за этим самостоятельно
М
  • Nov. 6, 2017, 6:07 p.m.

Еще не могли бы подсказать как сделать чтобы отображалась иконка, так как она тоже исчезла. Весь интернет обыскал так и не нашел решения.

Evgenii Legotckoi
  • Nov. 7, 2017, 1:46 a.m.

Какая иконка? Которая была в оформлении окна? Ну всё логично, оформление отключили - иконка исчезла. Хотите иконку, придётся самосттоятельно добавить её в левый верхний угол окна через какой-нибудь QML тип Image.

Вы про какую вообще иконку говорите?
М
  • Nov. 7, 2017, 7:38 a.m.

Извиняюсь за не точность. Я имел в виду  иконку в Taskbar.


Evgenii Legotckoi
  • Nov. 8, 2017, 2:33 p.m.

Вы же под Windows пишете? Есть такой класс QWinTaskbarButton, у него есть property overlayIcon . Возможно, стоит поработать с этим клсассом и установить туда иконку. Вот в этой статье есть пример работы с этим QWinTaskbarButton .


М
  • Nov. 8, 2017, 4:56 p.m.

Огромное спасибо, работает.

Edward Wayne
  • July 27, 2018, 7:20 p.m.

Я бы вместо совытий mouseXChanged  и mouseYChanged использовал positionChanged, так как у двух первых есть один недостаток, они производные от mouseX и Y которые содержат текущие координаты X и Y соответсвенно - это значит что они обновляются (записываются) регулярно при нажатой кнопки мыши (если свойство hoverEnabled будет true то они будут обновляться и без нажатия кнопок мыши). Это значит, что  эти свойства вызывают ложную логику, если вы еще не двигали курсор мыши, а просто нажали кнопку мыши. В то время как positionChanged срабатывает только когда положение курсора в действительности было изменено при нажатой кнопки мыши (если hoverEnabled = false, опять же).


Например, в случае с обработчиками событий onMouseXChanged  и onMouseYChanged окно бы ложно сворачивалось при простом нажатии кнопки мыши (так как эти свойства записываются постоянно при нажатой кнопке). PositionChanged дает пользователю время на обдумывание, нужно ли ему свернуть окно и начать перемещение или нет. Он может отменить это действие просто отпустив кнопку мыши не двигая курусор, как это реализованно с окнами windows. Да и код выглядит, согласитесь,  поменьше.
MouseArea {
	id: appMovigArea

	anchors.fill: windowTitleBar
	hoverEnabled: false
	onPressed: {
		previousMouseXPosition = mouseX;
		previousMouseYPosition = mouseY;
	}

	onPositionChanged: {
		if(window.visibility == Window.Maximized) {
			showNormal();
		} else {
			window.setX(window.x + (mouseX - previousMouseXPosition));
			window.setY(window.y + (mouseY - previousMouseYPosition));
		}
	}
}


m
  • Oct. 13, 2019, 7:17 p.m.

Здравствуйте. Сделал подобным образом ресайз и в Qt Widgets, и в QML. Везде получаю, что при изменении размера через левую или верхннюю границы проихсодит мерцание подобно как на этом видео . Всё перелопатил, нигде ответа не нашел. Не знаете как это победить?

Evgenii Legotckoi
  • Oct. 14, 2019, 5:48 p.m.

Добрый день. Нет, если сами по себе координаты при ресайзе все подсчитываются правильно, то это уже проблема графической подсистемы в ОС и работы с X11, если вы конечно под Linux собирали проект. В том видео в исходниках крутят платформозависимые флаги. На тему тех флагов есть Bug на багтрекере Qt . Возможно также локальная проблема с конкретным дистрибутивом, если на мейнстримовых дистрибутивах всё работает хорошо. В общем скорее всего это та помойка, в которой не хотелось бы копаться.

Comments

Only authorized users can post comments.
Please, Log in or Sign up
  • Last comments
  • Evgenii Legotckoi
    March 9, 2025, 9:02 p.m.
    К сожалению, я этого подсказать не могу, поскольку у меня нет необходимости в обходе блокировок и т.д. Поэтому я и не задавался решением этой проблемы. Ну выглядит так, что вам действитель…
  • VP
    March 9, 2025, 4:14 p.m.
    Здравствуйте! Я устанавливал Qt6 из исходников а также Qt Creator по отдельности. Все компоненты, связанные с разработкой для Android, установлены. Кроме одного... Когда пытаюсь скомпилиров…
  • ИМ
    Nov. 22, 2024, 9:51 p.m.
    Добрый вечер Евгений! Я сделал себе авторизацию аналогичную вашей, все работает, кроме возврата к предидущей странице. Редеректит всегда на главную, хотя в логах сервера вижу запросы на правильн…
  • Evgenii Legotckoi
    Oct. 31, 2024, 11:37 p.m.
    Добрый день. Да, можно. Либо через такие же плагины, либо с постобработкой через python библиотеку Beautiful Soup
  • A
    Oct. 19, 2024, 5:19 p.m.
    Подскажите как это запустить? Я не шарю в программировании и кодинге. Скачал и установаил Qt, но куча ошибок выдается и не запустить. А очень надо fb3 переконвертировать в html