Russian Qt Forum
Май 23, 2024, 18:21 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1]   Вниз
  Печать  
Автор Тема: (РЕШЕНО) Странный глюк в Android - баг в Qt или Android  (Прочитано 9733 раз)
Гурман
Гуру общения
******
Offline Offline

Сообщений: 1442

Qt 2.2, 3.3, 4.5, 4,7, 4.8, 5.3, 5.6, 5.9, 5.12


Просмотр профиля
« : Апрель 04, 2017, 22:55 »

На окне приложения есть несколько моих виджетов, наследующих QFrame, назовём их TextLabel. От оригиналов отличаются возможностью кучи вещей - настройкой шрифтов, цвета фона и цвета текста в дизайнере, скроллингом и т.д. Все параметры задаются в пропертях, поскольку этот класс сделан плагином дизайнера. Всё работает замечательно во всех ОС, включая Windows, Linux, Android 4 и 5, но кроме одного непонятного пока глюка в Android 6. Фишка ещё в том, что приложение работает строго в ландшафтном режиме. Когда приложение запускаешь - всё замечательно, оно работает, если не давать смартфону уснуть, то есть выключить экран. При выходе смартфона из "сна", то есть при включении экрана, он первоначально оказывается в портретной ориентации, независимо от того, как повёрнут смартфон. Но тут же переворачивается в ландшафт, и в большом количестве случаев происходит нечто необъяснимое - один из виджетов TextLabel, который лежит выше всех остальных TextLabel, "размазывается" влево и вниз до краёв экрана, и закрывает всё, что под него попало (но разумеется кроме виджетов, которые лежат над ним). При этом как этот, так и другие TextLabel (даже те, которые оказались не перекрыты) перестают отображать находящийся в них текст, они становятся "пустыми". Очевидно размазывается только изображение, но не геометрия виджета - поскольку все активные виджеты под ним по-прежнему реагируют на события, то есть, если там оказались кнопки, то они нажимаются, отрабатывают как положено. Только не видны.

На устройствах с Android 4 и 5 ничего подобного не происходит - на них экран при включении из сна сразу находится в ландшафте, и приложение нормально отображается всегда. Так происходит, даже если держать устройство "в портрет". Есть одна фишка, которая может иметь значение - приложение собрано с Android SDK level 15, то есть, под Android 4.0.3 ICS. Но более нигде не возникло каких-либо иных проблем с отображением, функционированием и т.д. А приложение довольно сложное, и использует много чего, включая использование многих функций ОС через Java-интерфейс (которые на уровне Qt не реализованы). Есть там и анимированные SVG, и меняющие прозрачность растровые картинки - ничего из этого не вызывает проблем.

Может кто встречался с чем-то подобным? Конечно, у меня тоже может быть багоплюха, хотя код TextLabel достаточно давно написан, не раз вылизывался, и как-то сомнений совершенно не вызывал.
« Последнее редактирование: Апрель 11, 2017, 20:47 от Гурман » Записан

2^7-1 == 127, задумайтесь...
qate
Супер
******
Offline Offline

Сообщений: 1175


Просмотр профиля
« Ответ #1 : Апрель 05, 2017, 09:44 »

Я например с андроид 6 вообще не сталкивался )
Кроме как минимальный эзкейс и спросить на https://forum.qt.io/  я выхода не вижу в решении проблемы.
Записан
Гурман
Гуру общения
******
Offline Offline

Сообщений: 1442

Qt 2.2, 3.3, 4.5, 4,7, 4.8, 5.3, 5.6, 5.9, 5.12


Просмотр профиля
« Ответ #2 : Апрель 05, 2017, 13:06 »

Я например с андроид 6 вообще не сталкивался )
Кроме как минимальный эзкейс и спросить на https://forum.qt.io/  я выхода не вижу в решении проблемы.
Спасибо, кэп... Просьба всем, кто захочет отозваться - пишите только если хотя бы в курсе в чём проблема.
Записан

2^7-1 == 127, задумайтесь...
Гурман
Гуру общения
******
Offline Offline

Сообщений: 1442

Qt 2.2, 3.3, 4.5, 4,7, 4.8, 5.3, 5.6, 5.9, 5.12


Просмотр профиля
« Ответ #3 : Апрель 05, 2017, 18:52 »

Чуть лучше локализовал момент появления проблемы - она возникает, если устройство было залочено. То есть, на экране лок-скрин, и требуется идентификации для снятия блокировки. При этом лок-скрин всегда нарисован в портретной ориентации (это же смартфон). Вот после того, как исчезает lock-screen, моё приложение рисуется неправильно. Но в остальных случаях, если на экране было другое приложение, независимо от ориентации экрана, или экран был погашен, но ещё не заблокирован - всё работает нормально.

Возможно, проблема связана с не совсем корректной работой лончера или что там управляет блокировкой - но в других приложениях она не проявляется. Впрочем... они и не на С++ написаны. А на других устройствах с Android у меня просто нет экрана блокировки, поэтому на них проблема не появляется.
Записан

2^7-1 == 127, задумайтесь...
Гурман
Гуру общения
******
Offline Offline

Сообщений: 1442

Qt 2.2, 3.3, 4.5, 4,7, 4.8, 5.3, 5.6, 5.9, 5.12


Просмотр профиля
« Ответ #4 : Апрель 05, 2017, 23:35 »

В общем, пока не нашёл другого решения, кроме как на время работы приложения запретить залочивание девайса. Это, в общем-то, логично, поскольку приложение у меня творит разные визуализации, а они подразумевают, что экран во время их показа всё время включен.
Записан

2^7-1 == 127, задумайтесь...
Гурман
Гуру общения
******
Offline Offline

Сообщений: 1442

Qt 2.2, 3.3, 4.5, 4,7, 4.8, 5.3, 5.6, 5.9, 5.12


Просмотр профиля
« Ответ #5 : Апрель 06, 2017, 20:34 »

Просто беда какая-то... Если вручную включить блокировку экрана, то после возврата получается опять искажённый экран приложения. Пытался и графическую сцену перерисовывать, и просто окно обновлять - ничего не помогает. Борьба осложняется ещё тем, что почему-то не работает qDebug() - возможно из-за того, что ОС level 23, а приложение собрано с SDK level 15. А может какие-то особые разрешения нужны, это же пендитный 6-й Android.
Записан

2^7-1 == 127, задумайтесь...
Гурман
Гуру общения
******
Offline Offline

Сообщений: 1442

Qt 2.2, 3.3, 4.5, 4,7, 4.8, 5.3, 5.6, 5.9, 5.12


Просмотр профиля
« Ответ #6 : Апрель 06, 2017, 22:57 »

О! Смог поймать этот же глюк в Android 4 SDK level 15. Проблема не характерна для Android 6. Это как-то обнадёживает - в Android 4 у меня хотя бы отладка работает нормально.
Записан

2^7-1 == 127, задумайтесь...
Гурман
Гуру общения
******
Offline Offline

Сообщений: 1442

Qt 2.2, 3.3, 4.5, 4,7, 4.8, 5.3, 5.6, 5.9, 5.12


Просмотр профиля
« Ответ #7 : Апрель 10, 2017, 23:10 »

Чуть-чуть удалось продвинуться - выяснил, что из трёх виджетов, один перерисовывается (вызывается его paintEvent()) в момент до того как снята блокировка экрана, или даже во время её снятия, а два других после этого. Вот тот, который рисуется до снятия блокировки, и ломает всю консерваторию. Отложить рисование до окончания снятия блокировки пока не получилось. Перерисовка всего окна после снятия блокировки вызовом repaint() тоже ничего не даёт.

ЗЫ

Ситуация ещё хуже. Портится структура виджета, который перерисовывается до снятия блокировки. Если виджет динамически убрать, он пропадает. Но если его вернуть - он рисуется с тем же искажением.

Вот же блин...
« Последнее редактирование: Апрель 11, 2017, 00:11 от Гурман » Записан

2^7-1 == 127, задумайтесь...
Гурман
Гуру общения
******
Offline Offline

Сообщений: 1442

Qt 2.2, 3.3, 4.5, 4,7, 4.8, 5.3, 5.6, 5.9, 5.12


Просмотр профиля
« Ответ #8 : Апрель 11, 2017, 15:03 »

Опа, опа - есть баг в Qt! В paintEvent() виджета я использую вызов frameRect() чтобы получить прямоугольник для заливки фона. По неизвестной причине (очевидно дыра в Qt для Android или в самом Android) если включена блокировка устройства и экран повёрнут в портрет, то эта функция возвращает не прямоугольник самого виджета, а... прямоугольник экрана. Причём она возвращает также прямоугольник экрана и после того, как блокировка снимается, и экран переводится в ландшафт. Если блокировка не включена, или если экран блокируется в ландшафте - то всё работает нормально. Может быть, это дырка и не Qt, а самого Android, но она срабатывает в версиях Android 4.0.3 и 6.0.

Ну да, не просто frameRect() портится - а geometry(). Причём это происходит только с QFrame.

« Последнее редактирование: Апрель 11, 2017, 15:46 от Гурман » Записан

2^7-1 == 127, задумайтесь...
kuzulis
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2812


Просмотр профиля
« Ответ #9 : Апрель 11, 2017, 17:06 »

Баг запостил? Улыбающийся
Записан

ArchLinux x86_64 / Win10 64 bit
Гурман
Гуру общения
******
Offline Offline

Сообщений: 1442

Qt 2.2, 3.3, 4.5, 4,7, 4.8, 5.3, 5.6, 5.9, 5.12


Просмотр профиля
« Ответ #10 : Апрель 11, 2017, 17:19 »

Баг запостил? Улыбающийся

Да, конечно. QTBUG-60101.

Вот как его обойти - пока ещё не решил. Могу, конечно, повесить в Java коде обработчик события блокировки клавиатуры (для screen lock/unlock отдельного события в Android нет) и вызывать из него Native функцию, дальше из неё сигнал, и по нему в виджете сохранять и восстанавливать геометрию QFrame. Этот вариант понятен. Но может есть вариант какой-то более элегантный, чем топор... Думаю.

Сигнал QCoreApplication::applicationStateChanged(Qt::ApplicationActive) не хиляет - он посылается до фактической разблокировки, когда виджет ещё имеет нормальный размер.
« Последнее редактирование: Апрель 11, 2017, 17:30 от Гурман » Записан

2^7-1 == 127, задумайтесь...
Гурман
Гуру общения
******
Offline Offline

Сообщений: 1442

Qt 2.2, 3.3, 4.5, 4,7, 4.8, 5.3, 5.6, 5.9, 5.12


Просмотр профиля
« Ответ #11 : Апрель 11, 2017, 19:13 »

Не, ну ваще... сделал топором - сохраняю геометрию при залочивании экрана, и вызываю восстановление при разлочивании. В дебаг выводе всё хорошо, восстановилось. Но потом по чьему-то желанию происходит ещё одна перерисовка, и бабах! Виджетов три штуки, в paintEvent перед рисованием у каждого выводится
Код:
       qDebug()<<"repaint"<<"frame"<<frameRect()<<"geometry"<<geometry();
и получаю вот такое:
Цитировать
D/AlpsGL17(24511): (null):0 ((null)): repaint frame QRect(0,0 307x21) geometry QRect(471,320 307x21)
D/AlpsGL17(24511): (null):0 ((null)): repaint frame QRect(0,0 83x30) geometry QRect(471,270 83x30)
D/AlpsGL17(24511): (null):0 ((null)): repaint frame QRect(0,0 307x20) geometry QRect(471,340 307x20)
D/AlpsGL17(21709): (null):0 ((null)): repaint frame QRect(0,0 307x21) geometry QRect(471,320 307x21)
D/AlpsGL17(21709): (null):0 ((null)): repaint frame QRect(0,0 83x30) geometry QRect(471,270 83x30)
D/AlpsGL17(21709): (null):0 ((null)): repaint frame QRect(0,0 307x20) geometry QRect(471,340 307x20)
D/AlpsGL17(21709): (null):0 ((null)): repaint frame QRect(0,0 1280x752) geometry QRect(471,320 1280x752)
D/AlpsGL17(21709): (null):0 ((null)): repaint frame QRect(0,0 1280x752) geometry QRect(471,270 1280x752)
D/AlpsGL17(21709): (null):0 ((null)): repaint frame QRect(0,0 1280x752) geometry QRect(471,340 1280x752)

То есть, сначала рисует виджеты правильного размера, а потом сразу испорченного. И это уже после того, как экран разблокирован. Но! Только если экран в положении портрет. В положении ландшафт при тех же действиях только один раз:

Цитировать
D/AlpsGL17(24511): (null):0 ((null)): repaint frame QRect(0,0 307x21) geometry QRect(471,320 307x21)
D/AlpsGL17(24511): (null):0 ((null)): repaint frame QRect(0,0 83x30) geometry QRect(471,270 83x30)
D/AlpsGL17(24511): (null):0 ((null)): repaint frame QRect(0,0 307x20) geometry QRect(471,340 307x20)

И разумеется всё нормально.

Отсюда единственный выход виден - сохранять размеры этих трёх виджетов при старте приложения, и использовать их для рисования в paintEvent(). Потому что где портится геометрия, выяснить уже становится совсем сложно. Правда тогда придётся отслеживать изменение размеров в дизайнере, поскольку там они вполне могут изменяться.
« Последнее редактирование: Июнь 05, 2017, 13:58 от Гурман » Записан

2^7-1 == 127, задумайтесь...
Гурман
Гуру общения
******
Offline Offline

Сообщений: 1442

Qt 2.2, 3.3, 4.5, 4,7, 4.8, 5.3, 5.6, 5.9, 5.12


Просмотр профиля
« Ответ #12 : Апрель 11, 2017, 20:44 »

Окончательное решение, которое работает, без использования Java кода, не шибко элегантное, но простое.
В приватных свойствах класса виджета, наследующего QFrame:
Код:
QRect geo;
Где-то в конструкторе или инициализации виджета, лишь бы пораньше:
Код:
geo = geometry();
В начале paintEvent() этого виджета пишем:
Код:
   if( geo != geometry() )
        setGeometry( geo );

Дальнейшее нужно, если геометрия виджета может меняться в процессе работы приложения. Надо не забывать при каждом таком изменении сохранять новую геометрию в geo = geometry();. Кроме этого, в классе виджета заводим слот:
Код:
void screenWasLocked(){ geo = geometry(); }
В объявлении класса главного окна приложения, которое наследует QMainWindow создаём сигнал:
Код:
   void screenIsLocked();
Ниже него слот:
Код:
void appStateChanged(Qt::ApplicationState state)
{
    if( state == Qt::ApplicationSuspended )
        emit screenIsLocked();
}
Где-нибудь в конструкторе этого класса:
Код:
   connect(QApplication::instance(), SIGNAL(applicationStateChanged(Qt::ApplicationState)), SLOT(appStateChanged(Qt::ApplicationState)));
И, наконец, в дизайнере соединяем сигнал screenIsLocked() главного окна со всеми слотами screenWasLocked() всех виджетов, которые расположены на форме. Всё.

Это работает. Каждый раз при перерисовке QFrame будет выполняться лишнее сравнение с вызовом geometry(), но это стоит недорого.
« Последнее редактирование: Апрель 11, 2017, 22:03 от Гурман » Записан

2^7-1 == 127, задумайтесь...
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.051 секунд. Запросов: 22.