Политика конфиденциальностиКонтактыО сайтеОтзывыGitHubDonate
© EVILEG 2015-2018
Рекомендует хостинг
TIMEWEB

QML - Урок 031. Отключаем системное обрамление окна в QML и пишем код для обработки перемещения и ресайза окна

QML, Qt, Frameless Window, Custom Style

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

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

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

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

main.qml

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

import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.3

ApplicationWindow {
    id: mainWindow
    visible: true
    width: 640
    height: 480

    flags: Qt.FramelessWindowHint // Отключаем обрамление окна

    // Объявляем свойства, которые будут хранить позицию зажатия курсора мыши
    property int previousX
    property int previousY

    MouseArea {
        id: topArea
        height: 5
        anchors {
            top: parent.top
            left: parent.left
            right: parent.right
        }
        // Устанавливаем форму курсора, чтобы было понятно, что это изменение размера
        cursorShape: Qt.SizeVerCursor

        onPressed: {
            // Запоминаем позицию по оси Y
            previousY = mouseY
        }

        // При изменении позиции делаем пересчёт позиции окна, и его высоты
        onMouseYChanged: {
            var dy = mouseY - previousY
            mainWindow.setY(mainWindow.y + dy)
            mainWindow.setHeight(mainWindow.height - dy)
        }
    }

    // Аналогичные расчёты для остальных трёх областей ресайза
    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)
        }
    }

    MouseArea {
        id: leftArea
        width: 5
        anchors {
            top: topArea.bottom
            bottom: bottomArea.top
            left: parent.left
        }
        cursorShape: Qt.SizeHorCursor

        onPressed: {
            previousX = mouseX
        }

        onMouseXChanged: {
            var dx = mouseX - previousX
            mainWindow.setX(mainWindow.x + dx)
            mainWindow.setWidth(mainWindow.width - dx)
        }
    }

    MouseArea {
        id: rightArea
        width: 5
        anchors {
            top: topArea.bottom
            bottom: bottomArea.top
            right: parent.right
        }
        cursorShape:  Qt.SizeHorCursor

        onPressed: {
            previousX = mouseX
        }

        onMouseXChanged: {
            var dx = mouseX - previousX
            mainWindow.setWidth(mainWindow.width + dx)
        }
    }

    // Центральная область для перемещения окна приложения
    // Здесь уже потребуется использовать положение как по оси X, так и по оси Y
    MouseArea {
        anchors {
            top: topArea.bottom
            bottom: bottomArea.top
            left: leftArea.right
            right: rightArea.left
        }

        onPressed: {
            previousX = mouseX
            previousY = mouseY
        }

        onMouseXChanged: {
            var dx = mouseX - previousX
            mainWindow.setX(mainWindow.x + dx)
        }

        onMouseYChanged: {
            var dy = mouseY - previousY
            mainWindow.setY(mainWindow.y + dy)
        }
    }
}
М

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

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

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

При использовании флага "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
        }
    }
}

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

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

Причём, если использовать этот флаг при QT Widgets, а не QML, то всё работает правильно.

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

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

М

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


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

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

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

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

Вы про какую вообще иконку говорите?
М

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


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


М

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

K
  • #
  • 27 июля 2018 г. 13: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));
		}
	}
}


Комментарии

Только авторизованные пользователи могут публиковать комментарии.
Пожалуйста, авторизуйтесь или зарегистрируйтесь
НБ
15 февраля 2019 г. 13:09
Николай Булахтин

C++ - Тест 003. Условия и циклы

  • Результат:50баллов,
  • Очки рейтинга-4
НБ
15 февраля 2019 г. 13:03
Николай Булахтин

C++ - Тест 002. Константы

  • Результат:25баллов,
  • Очки рейтинга-10
НБ
15 февраля 2019 г. 13:01
Николай Булахтин

C++ - Тест 001. Первая программа и типы данных

  • Результат:73баллов,
  • Очки рейтинга1
Последние комментарии
V
14 февраля 2019 г. 18:41
Vlad15007

Спасибо огромное! Заработало!
А
12 февраля 2019 г. 9:26
Александр90

Сам разборался, спасибо.
А
12 февраля 2019 г. 8:19
Александр90

День добрый! Можешь выложить форму mainwindow.ui от урока? Заранее спасибо
11 февраля 2019 г. 10:51
Евгений Легоцкой

Нет, у меня проблема с жёстким диском случилась, занимался восстановлением ПК, ещё пару вечеров придётся этим заниматься, увы.
Сейчас обсуждают на форуме
15 февраля 2019 г. 21:22
IscanderChe

Доброй ночи.Скромно напоминаю о своём вопросе...
15 февраля 2019 г. 15:36
Евгений Легоцкой

Ну я тут нашёл одно решение, но сам его не проверял. Вам нужно помещать фамилии скорее всего в ячейки заголовка, и потом просто перерисовывать их QHeaderView * header = m_ui->tableWidget...
15 февраля 2019 г. 7:53
Евгений Легоцкой

Добрый день! Не работал с remoteobjects, поэтому глянул документацию, чтобы рассмотреть, что это за зверь. После просмотра документации сложилось стойкой впечатление, что это вполне возм...
m
14 февраля 2019 г. 18:28
mr_roman

Нашел решение на Java. Удалось интегрировать в проект сервиса на Qt, теперь из Qt запускаю Java-код акселерометра.
14 февраля 2019 г. 11:00
Евгений Легоцкой

ok. I see. You changed related name Try this {% if goal.joined.all|user_in:request.user %}
Присоединяйтесь к нам в социальных сетях

Для зарегистрированных пользователей на сайте присутствует минимальное количество рекламы