LINUX.ORG.RU

Как в QMainWindow поймать момент после старта QApplication.exec()

 , qapplication, , , восстановление геометрии


0

2

Обнаружил в Qt такую проблему. Есть обычное главное окно программы:

class MainWindow : public QMainWindow
{
Q_OBJECT

public:
  MainWindow();
  ~MainWindow();

  void restoreWindowGeometry(void);
}

void MainWindow::restoreWindowGeometry(void)
{
  restoreGeometry( Тут_байты_с_геометрией );
}

Если вызывать метод восстановления геометрии окна restoreGeometry() до старта основного цикла приложения, например так:
int main(int argc, char ** argv)
{
 MainWindow win;
 win.restoreWindowGeometry();

 return app.exec();
}

То в некоторых оконных средах, например в OpenBox+LXDE, положение окна восстанавливается со смещением на размер заголовка окна и толщины обрамления. (В KDE4 и Gnome2, кстати, этой проблемы нет).

Если же вызвать restoreGeometry() после того, как основной цикл запущен, то восстановление геометрии происходит правильно.

Таким образом, вызывать восстановление геометрии надо после того, как запущен основной цикл приложения. Хотелось бы, чтобы главное окно само вызвало восстановление геометрии в нужный момент. Проблема в том, как этот момент отловить.

Пока я вижу только громоздкую конструкцию:

1. Сделать промежуточный класс, унаследованный от QApplication, и в методе exec() выставлять флаг что основной цикл запущен. Сделать в этом классе метод isRunning(), возвращающий этот флаг.
2. В классе MainWindow сделать таймер с минимальной задержкой, который будет крутиться до того момента, пока isRunning() приложения не вернет true. И тогда сделать вызов restoreGeometry().

Но как-то это очень громоздко. Может быть можно воспользоваться каким-нибудь сигналом у QApplication или у QMainWindow, которые среботают после запуска основного цикла? Но только я таких сигналов не нашел.

★★★★★

Ты чот не дописал в своем примере. Я не вижу где ты апп создал до вызова его экзека.

deep-purple ★★★★★
()
Ответ на: комментарий от anonymous

А, отоно что - вкорячить в окне ваншот таймер?

anonymous
()

Если же вызвать restoreGeometry() после того, как основной цикл запущен, то восстановление геометрии происходит правильно

Интересно, как ты это узнал, ведь метод экзек пока не закончится, дальше выполняться ничего не будет. А у тебя там уде ретурн из майна.

Давай полный пример, а лучше даже прожект весь — проверю у себя.

deep-purple ★★★★★
()
Последнее исправление: deep-purple (всего исправлений: 1)

Сигналы есть, и в каком-то треде я даже привёл пример такого. Но это костыль, а не решение. Решением будет написать в рассылку qt-user, описать проблему и, возможно, получить предложение оформить багрепорт

XMs ★★★★★
()
Ответ на: комментарий от deep-purple

Одно другого не исключает. Пусть и пример даст, и в рассылку напишет

XMs ★★★★★
()
Ответ на: комментарий от deep-purple

Ну вот минимальный пример (это минимальная инфраструктура MyTetra, всего пять классов):

http://rgho.st/7ckgNqcTR

Для работы потребуется файл ~/.config/mytetra/conf.ini с содержимым:

[General]
mainwingeometry="AdnQywACAAAAAAHYAAABSwAABaMAAALOAAAB2QAAAWEAAAWiAAACyAAAAAAAAAAAB4A="
programm=mytetra
version=35
printdebugmessages=true
runinminimizedwindow=false

В этом примере восстановление работает правильно, потому что сделаны пункты 1 и 2 из топика.

Чтобы увидеть глюк в LXDE, надо: раскомментировать строчку win.restoreWindowGeometry() в main.cpp и закомментировать строчку restoreWindowGeometry() в MainWindow::timerUpdate().

Xintrea ★★★★★
() автор топика
Ответ на: комментарий от Xintrea

минимальный пример

Доустановил свг либы и собралось.

потребуется файл

Предложило выбрать где создавать. Создался сам.

восстановление работает правильно

Нет. Поменяй окну размеры, перетащи его по столу и нажми кнопку «store geometry». Там начинает происходить чорти что. То только размер возвращает, то только положение. И причем тут вообще возвращение к предыдущему если ты только сторишь. Да и еще потыкал, при прятках в трей и возвращении на стол оно вылазиет со смещением правее и ниже на пол титлебара.

Чего-то ты там накодил. Ушел смотреть.

deep-purple ★★★★★
()
Ответ на: комментарий от deep-purple

Там одна кнопка Restore geometry (а не store). Можешь ее убрать, я ее делал чтоб вручную проверить восстановление после exec().

Xintrea ★★★★★
() автор топика
Ответ на: комментарий от Xintrea

Уже увидел. Просто текст в кнопку не влез.

Просто на иконку в трее понажимай — с каждым нажатием окно «едет» все выше. И при первом открытии появляется в верхнем левом углу. Кстати тестирую на: деб 8, матэ, культя 5.3.

deep-purple ★★★★★
()
Ответ на: комментарий от Xintrea

Вычислил я вот что.

Добавив вторую кнопку (стор) и поигравшись только с кнопками я увидел что координаты и размеры окна теперь (по крайней мере у меня) корректны. А вот при кликах на трее оно все еще бежит вверх.

Такое очучение что у тебя там бардак с showWindow(), hide(), saveAllState() и saveWindowGeometry(). Ты там то синкаешь конфиг то не синкаешь. У меня вот конфиг под мьютексом и каждый кто в него что-то пишет — синкает его.

Лови с двумя кнопками: http://dropmefiles.com/01TVC

deep-purple ★★★★★
()
Ответ на: комментарий от deep-purple

Еще — окно путает свои предыдущие положения и размеры после того, как был вызван его hide(). В моем приложении тоже. Однако под матэ оно просто доходит до верхней панели и выше не идет, никуда не заезжает, упирается.

И да, ничего не надо мутить с таймерами и ожиданием когда апп заэкзечится. Восстановить состояние окна можно ДО вызова его show(), а значит легко можешь воткнуть это в конструктор окна.

Ты не поверишь. Я просто перенес win.restoreWindowGeometry(); выше win.setWindowTitle(«MyTetra»); и все заработало.

deep-purple ★★★★★
()
Последнее исправление: deep-purple (всего исправлений: 1)
Ответ на: комментарий от Xintrea

На вот, у меня так:

void
GuiMainWindow::restoreUiState()
{
    Settings.lock();

    if (Settings.contains(GUI_SETTINGS_MAIN_WINDOW_GEOMETRY)) {
        restoreGeometry(
            Settings.value(
                GUI_SETTINGS_MAIN_WINDOW_GEOMETRY
            ).toByteArray()
        );
    }
    if (Settings.contains(GUI_SETTINGS_MAIN_WINDOW_STATE)) {
        restoreState(
            Settings.value(
                GUI_SETTINGS_MAIN_WINDOW_STATE
            ).toByteArray()
        );
    }
    if (Settings.contains(GUI_SETTINGS_MAIN_WINDOW_SHOW_STATUS_BAR)) {
        mp_statusBar.setVisible(
            Settings.value(
                GUI_SETTINGS_MAIN_WINDOW_SHOW_STATUS_BAR
            ).toBool()
        );
    }

    Settings.unlock();

    // set states by restored data
    if (!mp_statusBar.isHidden()) {
        setContentsMargins(
            GUI_MARGIN_SIZE,
            GUI_MARGIN_SIZE,
            GUI_MARGIN_SIZE,
            0
        );
        emit Signal.wantSetUpStatusBarToggler(true);
    } else {
        setContentsMargins(
            GUI_MARGIN_SIZE,
            GUI_MARGIN_SIZE,
            GUI_MARGIN_SIZE,
            GUI_MARGIN_SIZE
        );
        emit Signal.wantSetUpStatusBarToggler(false);
    }
    emit Signal.wantSetUpFullScreenToggler(isFullScreen());
}

void
GuiMainWindow::saveUiState()
{
    Settings.lock();

    Settings.setValue(
        GUI_SETTINGS_MAIN_WINDOW_GEOMETRY,
        saveGeometry()
    );
    Settings.setValue(
        GUI_SETTINGS_MAIN_WINDOW_STATE,
        saveState()
    );
    Settings.setValue(
        GUI_SETTINGS_MAIN_WINDOW_SHOW_STATUS_BAR,
        !mp_statusBar.isHidden()
    );

    Settings.sync();
    Settings.unlock();
}
Рестор дергается прямо перед шоу. Если в конфиге нет нужных данных то остаются дефолтные значения заданные в конструкторе. Если данные не валидны, то сами сеттеры кумайнвиндоу не примут их, т.е. опять останутся дефолты из конструктора. Стейт статусбара булевый, проверять незачем, либо показываем, либо нет. Сейв дергается в клозИвенте.

deep-purple ★★★★★
()

у меня было похожее, нужно было подгрузить картинки при старте, без блокировки виджета.

решается просто: в конструкторе делаем startTimer(300), и переопеределяем timerEvent(QTimerEvent*)

anonymous2 ★★★★★
()
Ответ на: комментарий от deep-purple

мне нужно было при старте просканировать рекурсивно директорию с кучей картинок, естественно при старте если я это делал в конструкторе виджета, то получал блокировку...

собственно решил просто, в конструкторе виджета запускаю таймер, t = startTimer(300), и в методе timerEvent(QTimerEvent*) я его получаю, останавливаю и делаю свою обработку картинок + запускаю между делом QCoreApplication::processEvents(), при этом мой виджет нормально при старте показывает всю обработку картинок в процессе....

anonymous2 ★★★★★
()
Последнее исправление: anonymous2 (всего исправлений: 1)
Ответ на: комментарий от anonymous2

Не, у тебя не совсем то. И вот я писал же выше:

Я просто перенес win.restoreWindowGeometry(); выше win.setWindowTitle(«MyTetra»); и все заработало.

Оно просто стало быть до вызова show()

deep-purple ★★★★★
()
Ответ на: комментарий от deep-purple

Просто на иконку в трее понажимай — с каждым нажатием окно «едет» все выше.

Нет, у меня не едет ни в KDE5, ни в LXDE.

Xintrea ★★★★★
() автор топика
Ответ на: комментарий от Xintrea

Не вижу тут особо бага. Установка значений должна быть ДО вызова метода show(). В противном случае, дальше, динамически могут устанавливаться другие значения и там уже painter все перерисовывает, т.к. ивенты во все поля, какраз после того как основной ивентлуп начался. Так что по мне все норм.

deep-purple ★★★★★
()
Ответ на: комментарий от deep-purple

Не вижу тут особо бага. Установка значений должна быть ДО вызова метода show()

Как это так не видишь?

Явная проблема. Если написать

restoreWindowGeometry();
setWindowTitle();

то геометрия восстановится корректно. Если напишешь
setWindowTitle();
restoreWindowGeometry();

То геометрия восстановится некорректно.

Оба эти вызова происходят до вызова show().

Xintrea ★★★★★
() автор топика
Последнее исправление: Xintrea (всего исправлений: 1)
Ответ на: комментарий от Xintrea

Т.е. ты утверждаешь (я этого не утверждал, возможно ты меня не так понял) что от рокировки этих двух строк начинаются косяки с геометрией? Сам проверил?

deep-purple ★★★★★
()
Ответ на: комментарий от deep-purple

Ну да, у меня именно так.

Ты же сам написал: Я просто перенес win.restoreWindowGeometry(); выше win.setWindowTitle(«MyTetra»); и все заработало.

Между win.setWindowTitle(«MyTetra») и win.restoreWindowGeometry() до исправления небыло show(). Да и после перестановки тоже нет никакого show().

Xintrea ★★★★★
() автор топика
Ответ на: комментарий от Xintrea

В тобой присланном коде было show() между этими стоками (и что-то еще не влияющее на порядок их выполнения). Я перенес рестор геометри над сет титло и все заработало. Больше я ничего не делал, а теперь и код весь твой уже давно удалил.

deep-purple ★★★★★
()

Что мешает сигнал/слот или событие использовать? Специально же придумано.

normann ★★★
()
17 января 2019 г.

Дядя!

Апаю темку. Тут такое дело. У меня появилась та же проблема с убеганием окна вниз на размер толщины заголовка окна.

Так вот. Обсосанное нами тут решение, после того как я включил компиз — не работало. А без компиза работало. Пишу как в прошлом потому что проблему с включенным компизом только-что решил.

Но, возможно, это специфичное костыляние ради только компиза (в матэ). Поэтому, опишу что сделать, а тебя прошу повторить порядок и проверить, работает ли оно у тебя корректно. Возможно я нашел идельно работающий вариант.

1) Не важно где ты вызовешь setWindowTitle() до show() или после — это ни на что не влияет.

2) Все ресторы вызываются ТОЛЬКО после show()

3) Сначала нужно вызвать restoreGeometry(geometryByteArray) — восстановит для окна отступ от верхнего левого угла и размер.

4) И только потом вызвать restoreState(stateByteArray) — восстановит состояние (фуллскрин или нет) и все размеры, положения и таббинг для док-виджетов, если таковые имеются.

Если поменять порядок вызовов рестора геометрии и стейта — то проблема появляется.

Прочекай там со своей стороны и сообщи о результатах.

deep-purple ★★★★★
()
Ответ на: комментарий от Xintrea

Да, на этом и проверяй. Если всё будет ок — так и составляй, матэ+компиз будет автоматом закрыт.

deep-purple ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.