Евгений Легоцкой14 сентября 2017 г. 2: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 хостинг.
Поддержать автора Donate
М

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

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

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

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


М

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

Я бы вместо совытий 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 г. 19:17

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

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

Комментарии

Только авторизованные пользователи могут публиковать комментарии.
Пожалуйста, авторизуйтесь или зарегистрируйтесь
Timeweb

Позвольте мне порекомендовать вам отличный хостинг, на котором расположен EVILEG.

В течение многих лет Timeweb доказывает свою стабильность.

Для проектов на Django рекомендую VDS хостинг

Посмотреть Хостинг
p
  • pstMem
  • 27 ноября 2020 г. 22:22

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

  • Результат:64баллов,
  • Очки рейтинга-1
q
  • qwerty
  • 27 ноября 2020 г. 18:29

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

  • Результат:42баллов,
  • Очки рейтинга-8
КЧ

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

  • Результат:25баллов,
  • Очки рейтинга-10
Последние комментарии
ВР

QML - Урок 016. База данных SQLite и работа с ней в QML Qt

Помогите, пожалуйста. У меня похожая задача, но я в qml слой долен передать не чистый запрос, а со сложной обработкой, поэтому у меня в С++ слое есть иерархия классов, которая имитирует бд и зап…
e
  • eviza
  • 16 ноября 2020 г. 15:32

Qt/C++ - Урок 083. Создание динамической библиотеки и подключение её в другой проект

здравствуйте! при компиляции библиотеки выскакивает окно особая программа( не удалось найти программу, укажите путь к ней), и в папке debug создается файл .dll, а .lib нет. подскажите…
ИБ

Шифрование данных в Qt алгоритмом RSA c открытым и закрытым ключами без привязки к OpenSSL

Библиотека подключилась нормально, только на выводе из первого примера выходит пустое сообщение, вместо "test message" просто "". Никаких ошибок не выдает.
VS

Qt WinAPI - Урок 002. Как сделать win инсталлятор для Qt-приложения?

Можно ли как-то однозначно (не проверяя) выяснить, запустится ли программа в windows 7? И как быть с разрядностью уже и просто в w10 (32\64)?
ДТ

Django - Урок 036. Как добавить аутентификацию через социальные сети. ВКонтакте

Возможно, автор прочитает. Делал авторизацию с помощью ВК по книге Дронова. Выдает ошибку "Backend not found". Стал гуглить, нашел вашу статью, вроде почти то же самое (оно и понятно, документа…
Сейчас обсуждают на форуме
s
  • solmik
  • 27 ноября 2020 г. 23:44

Обращение к ячейке таблицы

Спасибо, разобрался, у меня получилось немного не так, таблица создавалась в Qt Creator, рядом с таблицей поставил кнопку и вот слот обработки сигнала этой кнопки: void MainWindow::on…
m
  • mihamuz
  • 27 ноября 2020 г. 15:49

Установка OpenSSL с QT Windows

К продолжению темы в QT 5.15.1 появились уже готовые libeay32.dll и ssleay32.dll на машине сборки все работает. На целевую машину в папку с exe подкидываю libeay32.dll и ssleay32.dll результата…
ДК

QStyledItemDelegate не изменяются данные

Привет. Редактирую ячейку, нажимаю enter, либо просто кликаю на др место, но введенные данные возвращаются к тем, что были до редактирования. К примеру, меняю дату: 01.01.2000 было, редактирую 0…
m
  • mihamuz
  • 26 ноября 2020 г. 0:43

Отправка JSON

Спасибо!

Разный масштаб в формах и при запуске

Сврестайте все в один лэйаут (Выбирите окно и нажмити сверху на голубой квадратик из 9 голубфх квадратиков)
О нас
Услуги
© EVILEG 2015-2020
Рекомендует хостинг TIMEWEB