Evgenii Legotckoi
Қыр. 14, 2017, 2:05 Т.Ж.

QML сабағы 031

Мазмұны

Если по какой-то причине, вы решили отказаться от стандартного системного обрамления окна и реализовать все Title Bar`ы окна самостоятельно в QML, то вы вполне можете реализовать это средствами QML, что будет даже проще чем в C++, на мой взгляд. Для ознакомления можете посмотреть статью по кастомизации окна приложения в стиле AIMP . Там имеется значительная доля кода по реализации механики перемещания окна, а также его ресайза. Преимущество QML в данном случае состоит в том, что QML сразу обеспечивает верстку приложения, а значит можно с помощью MouseArea и якорей сразу определить нужную обработку при клике и перемещении мыши без каких-либо специальных расчётов положения курсора в области окна.

А механика расчёта изменения размеров и положения будет сходна с той, которая была применена в статье по кастомизации приложения в C++. То есть необходимо будет запомнить положение, где была нажата кнопка мыши, а потом уже относительно этого положения делать ресайз и перемещение окна приложения, пока кнопка не будет отпущена.


Как видно на рисунке выше, потребуется создать 5 областей MouseArea, четыре из которых будут отвечать за ресайз приложения, а пятая центральная за перемещение приложения. То есть можно будет передвигать приложение зажав кнопку мыши в любом месте пятой центральной области.

Если Вам понадобится специальный Title Bar, то он также может отвечать за перемещение окна приложения. То есть необязательно делать всю центральную область чувствительной к перемещению.

main.qml

Приложение создаётся по умолчанию и изменять будет только файл main.qml, который я и приведу в качестве примера.

  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 // Отключаем обрамление окна
  12.  
  13. // Объявляем свойства, которые будут хранить позицию зажатия курсора мыши
  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. // Устанавливаем форму курсора, чтобы было понятно, что это изменение размера
  26. cursorShape: Qt.SizeVerCursor
  27.  
  28. onPressed: {
  29. // Запоминаем позицию по оси Y
  30. previousY = mouseY
  31. }
  32.  
  33. // При изменении позиции делаем пересчёт позиции окна, и его высоты
  34. onMouseYChanged: {
  35. var dy = mouseY - previousY
  36. mainWindow.setY(mainWindow.y + dy)
  37. mainWindow.setHeight(mainWindow.height - dy)
  38. }
  39. }
  40.  
  41. // Аналогичные расчёты для остальных трёх областей ресайза
  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. // Центральная область для перемещения окна приложения
  104. // Здесь уже потребуется использовать положение как по оси X, так и по оси Y
  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. }

Осы тақырып бойынша ұсынылатын мақалалар

Мақала бойынша сұралады0сұрақтар(лар)

5

Ол саған ұнайды ма? Әлеуметтік желілерде бөлісіңіз!

М
  • Қыр. 16, 2017, 12:58 Т.Қ.

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

Евгений Миллер
  • Қыр. 17, 2017, 1:53 Т.Ж.

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

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

При использовании флага "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
  • Қыр. 17, 2017, 12:29 Т.Қ.

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

Евгений Миллер
  • Қыр. 17, 2017, 2:06 Т.Қ.

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

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

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

Евгений Миллер
  • Қыр. 17, 2017, 4:14 Т.Қ.

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

М
  • Қаз. 18, 2017, 2:35 Т.Қ.

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


Evgenii Legotckoi
  • Қаз. 18, 2017, 2:45 Т.Қ.

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

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

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

Evgenii Legotckoi
  • Қар. 7, 2017, 1:46 Т.Ж.

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

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

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


Evgenii Legotckoi
  • Қар. 8, 2017, 2:33 Т.Қ.

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


М
  • Қар. 8, 2017, 4:56 Т.Қ.

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

Edward Wayne
  • Шілде 27, 2018, 7:20 Т.Қ.

Я бы вместо совытий 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
  • Қаз. 13, 2019, 7:17 Т.Қ.

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

Evgenii Legotckoi
  • Қаз. 14, 2019, 5:48 Т.Қ.

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

Пікірлер

Тек рұқсаты бар пайдаланушылар ғана пікір қалдыра алады.
Кіріңіз немесе Тіркеліңіз