Russian Qt Forum

Qt => Пользовательский интерфейс (GUI) => Тема начата: dd от Февраль 23, 2011, 03:36



Название: QUiLoader, layout и ориентация экрана
Отправлено: dd от Февраль 23, 2011, 03:36
Сейчас пишу приложение для мобильных устройств, но это темы касается косвенно.
Там при повороте экрана меняется разрешение.
Например с 300x200 на 200x300 (горизонтально / вертикально).
Есть желание использовать QUiLoader для построения форм. С использованием лайоутов и виджетов, разумеется.
Как и следовало ожидать при повороте экрана все съезжает и выглядит убого.
Нужно каждый раз менять лайоуты, не трогая виджеты.
Простой пример: в горизонтальном положении две кнопки будут расположены горизонтально,
в вертикальном - вертикально. В реальности формы сложнее, много лайоутов.
Есть ли какой-нибудь способ это делать? Например, наследовать свой лоадер или что-нибудь еще?
Сейчас приходится все прграммно делать.
В примерах динамическая компоновка делается программно тоже.


Название: Re: QUiLoader, layout и ориентация экрана
Отправлено: dd от Февраль 23, 2011, 05:35
Сам отвечу на этот вопрос =)
Вот результат, который мне удалось получить:

Код:
    ddWidgetMain w;  // создается главное окно
    w.resize(500, 250);
    w.move(400, 200);
    w.show();
    //w.showFullScreen();

    {
        QWidget *myWidget = NULL;

        {
            ddUiLoader loader;  // создается модифицированный лоадер
            QFile file(CEngine::Get()->getCurrentDir() + "/files/test1.ui");  // загружаем файл, соответствующий горизонтальному варианту
            file.open(QFile::ReadOnly);
            myWidget = loader.load(&file, &w);
            myWidget->show();
            file.close();
        }

        myWidget->findChild<QLabel*>()->setText("123");  // для наглядности меняем надпись на QLabel-е
        qDebug() << myWidget->findChild<QLabel*>();  // распечатываем указатель на этот виждет. Чтобы потом проверить, не изменился ли он

        // Сейчас иммитируем переворот экрана, загрузив новый файл интерфейса

        {
            ddUiLoader loader;
            QFile file(CEngine::Get()->getCurrentDir() + "/files/test2.ui");  // загружаем файл, соответствующий [b]вертикальному[/b] варианту
            file.open(QFile::ReadOnly);

            loader.setCurrent(myWidget);  // самое интересное. Даем лоадеру указатель на виджет, где уже есть итемы. Дабы он не создал новые
            myWidget = loader.load(&file, &w);
            myWidget->show();
            file.close();
        }

        qDebug() << myWidget->findChild<QLabel*>();
    }

Результат вывода дебага:
Код:
QLabel(0xe04f10, name = "label") 
QLabel(0xe04f10, name = "label")

Как видно, при перезагрузке интерфейсного файла метка не изменилась.
Что и требовалось получить =))
Лайоуты пересобрались, виджеты остались те же самые.
Можно например делать калькулятор с двумя разными вариантами интерыейса, и при перевертывании текущее состояние ввода не поменяется.

Теперь код модифицированного лоадера:

Код:
class ddUiLoader : public QUiLoader
{
    Q_OBJECT
public:
    explicit ddUiLoader(QObject *parent = 0);
    void setCurrent(QWidget *pWidget);

protected:
    QWidget *createWidget(const QString & className, QWidget * parent = 0, const QString & name = QString());

private:
    QWidget *m_pCurrentWidget;
};

...........................................

QWidget *ddUiLoader::createWidget(const QString & className, QWidget *parent, const QString &name)
{
    QWidget *pWidget = NULL;

    if(m_pCurrentWidget)
    {
        pWidget = m_pCurrentWidget->findChild<QWidget*>(name);
        if(pWidget) pWidget->setParent(parent);
    }

    if(!pWidget) pWidget = QUiLoader::createWidget(className, parent, name);

    return pWidget;
}

void ddUiLoader::setCurrent(QWidget *pWidget)
{
    m_pCurrentWidget = pWidget;
}

Но есть один нюанс!

Злобный QT Designer позволяет настраивать свойства (property) виджета.
Например, текст метки (QLabel).
Чем это грозит.
Мы создали в дезайнере QLineEdit.
Вводим туда данные, все хорошо.
И тут - опа, перевернули девайс. Загрузился новый файл интерфейса.
Сам объект QLineEdit не изменился.
Но QUiLoader перезапишет его свойство "text" на то, что мы ввели при дизайне,
сведя тем самым на нет наши старания.

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

Лечится просто.
Открываем файл *.ui и удаляем инициализацию свойст для тех лбъектов, где это нужно.
По факту это те виджеты, куда пользователь будет что-то воодить. Иначе потрется.

Вот как это выглядит для QLabel (файл *.ui):

Код:
...
    <widget class="QLabel" name="label">
     <property name="text">    // Удалить!
      <string>test</string>       // Удалить!
     </property>                     // Удалить!
    </widget>
...


Вроде все работает нормально!
Если у Вас есть другие идеи, как избежать правки файла вручную, буду рад посмотреть.

Хотя есть одна мысль.
Сделать функцию, которой можно передать имя файла и названия неизменяемых виджетов.
Эта функция просто будет находить тег <widget ........>...</widget> и удалять всё нутро.
(Пример с калькулятором - с кнопками ничего делать не надо, а вот поле ввода нужно сделать неизменяемым.
Иначе при ресайзе потрется введенное число)



Название: Re: QUiLoader, layout и ориентация экрана
Отправлено: iks от Февраль 27, 2011, 12:58
Создать все динамично не отрисовывая через дизайнер, которым я как-то вобще почти ни когда не пользуюсь, и все что вводит юзер сохраняется сразу по умолчанию, так как QLineEdit и т.п. создаются без включеных разных функций по умолчанию


Название: Re: QUiLoader, layout и ориентация экрана
Отправлено: dd от Февраль 27, 2011, 16:22
Уважаемый, вы наверное не прочли мое сообщение.
Я же сказал, сейчас и так все прграммно.
Вы можете написать мне код динамической компоновки с учетом ориентации экрана?
Сколько он займет? Просто ради интереса. Может у вас меньше.
А у меня для каждого окна получается гора лишнего кода более страницы.
А время, потраченное на программную компоновку каждого окна?
Я не от хорошей жизни решил UI лоадером пользоваться.
К тому же я пользую QtSCript, получается скрипт на много больше.
А так встроил механизм в виджет, указываешь только два файла - вертикальый и горизонтальный.
И забыл. Одна строка.


Потом есть еще один очень важный момент.
Я пользую QtScript без биндинга всего фреймворка. Экономлю память на моб. устройстве.
Из этого следует, что я не могу создавать виджеты в скрипте:

Код:
var myButton = new QPushButton("test");

Чтобы создать Qt объект, мне необходимо создать функцию - фабрику на С++, сунуть ее движку скрипта
и далее с помощью ее создавать виджеты.
Для каждого виджета это делать - просто глупо и расточительно.
А лоадер сам моздаст нужные виджеты, я из скрипта просто буду вызывать:

Код:
var value = myWidget.findChild("myLineEdit").value;

Собственно это наверное главная причина, почему я взялся за лоадер.
Раньше и я им не пользовался.