Evgenii Legotckoi
Evgenii Legotckoi13 сентября 2017 г. 16:05

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

Содержание

Если по какой-то причине, вы решили отказаться от стандартного системного обрамления окна и реализовать все 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)
        }
    }
}
Рекомендуем хостинг TIMEWEB
Рекомендуем хостинг TIMEWEB
Стабильный хостинг, на котором располагается социальная сеть EVILEG. Для проектов на Django рекомендуем VDS хостинг.

Вам это нравится? Поделитесь в социальных сетях!

М
  • 16 сентября 2017 г. 2:58

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

Евгений Миллер
  • 16 сентября 2017 г. 15: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 г. 2:29

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

Евгений Миллер
  • 17 сентября 2017 г. 4:06

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

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

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

Евгений Миллер
  • 17 сентября 2017 г. 6:14

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

М
  • 18 октября 2017 г. 4:35

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


Evgenii Legotckoi
  • 18 октября 2017 г. 4:45

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

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

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

Evgenii Legotckoi
  • 6 ноября 2017 г. 14:46

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

Вы про какую вообще иконку говорите?
М
  • 6 ноября 2017 г. 20:38

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


Evgenii Legotckoi
  • 8 ноября 2017 г. 3:33

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


М
  • 8 ноября 2017 г. 5:56

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

Edward Wayne
  • 27 июля 2018 г. 9: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 г. 9:17

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

Evgenii Legotckoi
  • 14 октября 2019 г. 7:48

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

Комментарии

Только авторизованные пользователи могут публиковать комментарии.
Пожалуйста, авторизуйтесь или зарегистрируйтесь
e
  • ehot
  • 1 апреля 2024 г. 0:29

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

  • Результат:78баллов,
  • Очки рейтинга2
B

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

  • Результат:16баллов,
  • Очки рейтинга-10
B

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

  • Результат:46баллов,
  • Очки рейтинга-6
Последние комментарии
k
kmssr9 февраля 2024 г. 5:43
Qt Linux - Урок 001. Автозапуск Qt приложения под Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
АК
Анатолий Кононенко5 февраля 2024 г. 12:50
Qt WinAPI - Урок 007. Работаем с ICMP Ping в Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
EVA
EVA25 декабря 2023 г. 21:30
Boost - статическая линковка в CMake проекте под Windows Ошибка LNK1104 часто возникает, когда компоновщик не может найти или открыть файл библиотеки. В вашем случае, это файл libboost_locale-vc142-mt-gd-x64-1_74.lib из библиотеки Boost для C+…
J
JonnyJo25 декабря 2023 г. 19:38
Boost - статическая линковка в CMake проекте под Windows Сделал всё по-как у вас, но выдаёт ошибку [build] LINK : fatal error LNK1104: не удается открыть файл "libboost_locale-vc142-mt-gd-x64-1_74.lib" Хоть убей, не могу понять в чём дел…
G
Gvozdik19 декабря 2023 г. 8:01
Qt/C++ - Урок 056. Подключение библиотеки Boost в Qt для компиляторов MinGW и MSVC Для решения твой проблемы добавь в файл .pro строчку "LIBS += -lws2_32" она решит проблему , лично мне помогло.
Сейчас обсуждают на форуме
a
a_vlasov14 апреля 2024 г. 16:41
Мобильное приложение на C++Qt и бэкенд к нему на Django Rest Framework Евгений, добрый день! Такой вопрос. Верно ли следующее утверждение: Любое Android-приложение, написанное на Java/Kotlin чисто теоретически (пусть и с большими трудностями) можно написать и на C+…
Павел Дорофеев
Павел Дорофеев14 апреля 2024 г. 12:35
QTableWidget с 2 заголовками Вот тут есть кастомный QTableView с многорядностью проект поддерживается, обращайтесь
f
fastrex4 апреля 2024 г. 14:47
Вернуть старое поведение QComboBox, не менять индекс при resetModel Добрый день! У нас много проектов в которых используется QComboBox, в версии 5.5.1, когда модель испускает сигнал resetModel, currentIndex не менялся. В версии 5.15 при resetModel происходит try…
P
Pisych27 февраля 2023 г. 15:04
Как получить в массив значения из связанной модели? Спасибо, разобрался:))
AC
Alexandru Codreanu19 января 2024 г. 22:57
QML Обнулить значения SpinBox Доброго времени суток, не могу разобраться с обнулением значение SpinBox находящего в делегате. import QtQuickimport QtQuick.ControlsWindow { width: 640 height: 480 visible: tr…

Следите за нами в социальных сетях