Версия для печати темы

Нажмите сюда для просмотра этой темы в обычном формате

Форум на CrossPlatform.RU _ Qt GUI _ QWizard

Автор: FladeX 10.3.2009, 23:49

При помощи QWizard создана программа - набор страниц, связанных "назад"/"далее" кнопками.
Задача - при помощи чекбоксов, выводимых на первой странице, выводить или не выводить последующие страницы. Как это реализовать?

Т.е. на первой странице чекбоксы "страница 2", "страница 3", "страница 4". Если мы отмечаем "страница 2" и "страница 4", то по нажатию "далее" будет показана страница 2, а затем 4, то есть страница 3 будет пропущена.

Автор: FladeX 13.3.2009, 15:37

Какие-нибудь идеи есть?

Автор: SABROG 13.3.2009, 16:23

Такая есть идея http://doc.trolltech.com/4.5/qwizard.html#creating-non-linear-wizards

Автор: FladeX 17.3.2009, 17:25

Теперь такой вопрос - как из первого листа назначить nextId() для всех остальных? Пробовал по-всякому, результата нет(

class Func01Page : public QWizardPage
{
    Q_OBJECT
public:
    Func01Page(QWidget *parent = 0);

    int nextId() const;
    int IDValue;
};

и аналогичный Func02Page.
Нужно в
int Func01Page::nextId() const
{
    return ExpertSystem::Page_Func02;
}

изменить nextId() для Func02Page. Как это сделать?

Автор: FladeX 19.3.2009, 15:24

class ExpertSystem : public QWizard
{
    Q_OBJECT

public:
    enum
    {
        Page_Intro,
        Page_Func01,
        // ...
        Page_Func27,
        Page_Outro
    };

    ExpertSystem(QWidget *parent = 0);

private slots:
    void showHelp();
};

Так и не понял, как обращаться к enum {}...
В документации нашел такую фразу:
Цитата
If you want to register an enum that is declared in another class, the enum must be fully qualified with the name of the class defining it. In addition, the class defining the enum has to inherit QObject as well as declare the enum using Q_ENUMS().

Подскажите плиз.

Автор: AD 19.3.2009, 15:40

ExpertsSystem:: Page_Intro


В данном случае лучше, чтобы enum имел иия.
Например, так:
enum PAGES
{
Page_Intro,
Page_Func01,
// ...
};

Тогда можно будет использовать в качестве типа:
ExpertsSystem:: PAGES
.
Если же Вы имели что-то другое, то, пожалуйста, конкретнее проблему опишите! :)

Автор: kwisp 19.3.2009, 15:42

Цитата(FladeX @ 19.3.2009, 15:24) *
Так и не понял, как обращаться к enum {}...

ну назови его
enum EPages{...};

и обращайся
в нутри класса
Page_Intro;

за пределами класса
ExpertSystem::Page_Intro;

Автор: FladeX 19.3.2009, 15:55

На основе http://doc.trolltech.com/4.5/qwizard.html#creating-non-linear-wizards сделал визард. Но отличие в том, что на первой странице список чекбоксов, которые и определяют, какие страницы будут показаны, а какие нет. Собственно проблема в том, что кроме первой страницы я не могу ничего изменить...

Итак, есть класс:

код класса
class ExpertSystem : public QWizard
{
    Q_OBJECT

public:
    enum
    {
        Page_Intro,
        Page_Func01,
        // ...
        Page_Func27,
        Page_Outro
    };

    ExpertSystem(QWidget *parent = 0);

private slots:
    void showHelp();
};

Есть первая страница с чекбоксами:
первая страница
class IntroPage : public QWizardPage
{
    Q_OBJECT

public:
    IntroPage(QWidget *parent = 0);

    int nextId() const;
    int IDValue;

//private:
public:
    QCheckBox *func01;
    QCheckBox *func02;
    //..
    QCheckBox *func26;
    QCheckBox *func27;
};

Enum с страницами связан так:
конструктор
ExpertSystem::ExpertSystem(QWidget *parent)
    : QWizard(parent)
{
    setPage(Page_Intro, new IntroPage);
    setPage(Page_Func01, new Func01Page);
    setPage(Page_Func02, new Func02Page);
    //..
    setPage(Page_Func27, new Func27Page);
    setPage(Page_Outro, new OutroPage);

    setStartId(Page_Intro);
}

Как теперь изменить значение int nextId() const к примеру для Page_Func27 ? Изменять отсюда (ибо чекбоксы только на первой странице):
int IntroPage::nextId() const
{

Автор: FladeX 20.3.2009, 20:47

Ну подскажите же эту строчку)

Автор: kwisp 21.3.2009, 10:04

FladeX,
по аналогии действуй

Раскрывающийся текст

int IntroPage::nextId() const
{
     if (evaluateRadioButton->isChecked()) {
         return LicenseWizard::Page_Evaluate;
     } else {
         return LicenseWizard::Page_Register;
     }
}

int EvaluatePage::nextId() const
{
     return LicenseWizard::Page_Conclusion;
}

int RegisterPage::nextId() const
{
     if (upgradeKeyLineEdit->text().isEmpty()) {
         return LicenseWizard::Page_Details;
     } else {
         return LicenseWizard::Page_Conclusion;
     }
}

int DetailsPage::nextId() const
{
     return LicenseWizard::Page_Conclusion;
}

int ConclusionPage::nextId() const
{
     return -1;
}

It would also be possible to put all the logic in one place, in a QWizard::nextId() reimplementation. For example:
 int LicenseWizard::nextId() const
{
     switch (currentId()) {
     case Page_Intro:
         if (field("intro.evaluate").toBool()) {
             return Page_Evaluate;
         } else {
             return Page_Register;
         }
     case Page_Evaluate:
         return Page_Conclusion;
     case Page_Register:
         if (field("register.upgradeKey").toString().isEmpty()) {
             return Page_Details;
         } else {
             return Page_Conclusion;
         }
     case Page_Details:
         return Page_Conclusion;
     case Page_Conclusion:
     default:
         return -1;
     }
}


получается тебя надо вернуть Page_Outro или я так чего то и не понял.

Автор: FladeX 21.3.2009, 14:22

Уже что-то, спасибо!
Допустим есть конструкция вида

 int LicenseWizard::nextId() const
{
     switch (currentId()) {
     }
}

Как оттуда узнать состояния чекбоксов (напоминаю, что они на первой странице визарда расположены)?

Автор: Litkevich Yuriy 21.3.2009, 19:13

Цитата(FladeX @ 21.3.2009, 17:22) *
узнать состояния чекбоксов
насколько я помню, в мастерах Qt есть понятие "полей", и используется манипуляция с полями, а не с виджетами, т.е. ты их должен создавать/инициализировать/изменять. Поля видны на всех страницах мастера.

П.С. с терминами могу ошибаться

http://www.wiki.crossplatform.ru/index.php/The_Wizard_Magically_Reappears, хоть и на аглицком

Автор: FladeX 21.3.2009, 21:57

Спасибо, вроде именно то что надо.
Только вот не компилиться теперь:

moc_expertsystem.o:(.rodata._ZTV10Func02Page[vtable for Func02Page]+0xf0): undefined reference to `Func02Page::nextId() const'
moc_expertsystem.o:(.rodata._ZTV10Func01Page[vtable for Func01Page]+0xf0): undefined reference to `Func01Page::nextId() const'
moc_expertsystem.o:(.rodata._ZTV9IntroPage[vtable for IntroPage]+0xf0): undefined reference to `IntroPage::nextId() const'

Это что за ошибка такая?

Автор: Litkevich Yuriy 21.3.2009, 22:16

Цитата(FladeX @ 22.3.2009, 0:57) *
Это что за ошибка такая?
Цитата(FladeX @ 22.3.2009, 0:57) *
moc_expertsystem.o
возможно ты добавил где-то макрос Q_OBJECT, а qmake при этом не вызывал. Либо объявление класса и его реализацию поместил в один файл неподключив соответсявующий, автоматически создаваемый, файл (в твоем случае moc_expertsystem.cpp)

Автор: kalombo 22.3.2009, 10:13

Вклинюсь сюда тоже со своим вопросом, надеюсь никому не помешаю. Вопрос такой. Создал я значит свой класс производный от QWizard, для него сделал несколько страниц производных от класс QWizardPage. И вот на одной из страниц у меня есть QTableWIdget, как к нему обращаться с других страниц к примеру? В моем случае мне надо обращаться к этому виджету в методе класса QWizard::accept(), что позволило мне решить проблему следующим образом. Я создал для страницы на которой QTableWidget паблик метод, возвращающий ссылку на этот виджет и потом в классе QWizard вызываю этот метод. Такой способ решения мне кажется не очень красивым, а как с другой страницы получить доступ к этому виджету вообще непонятно.

Автор: SABROG 22.3.2009, 11:32

Полагаю, что одним из этих методов:

QTableWidget *tbw = wizard()->page(id)->getTableWidgetPointer();
QTableWidget *tbw = wizard()->page(id)->findChild<QTableWidget *>("mytablewidgetname");
QTableWidget *tbw = wizard()->page(id)->justPublicTableWidgetPointer;


Вообще в QWizardPage есть методы registerField() и field(). Это глобальные методы, если ты регистрируешь свое поле, то оно доступно через метод field() из любой страницы, загвоздка в том, что это работает только на следующие поля-классы:

Цитата
QAbstractButton
QAbstractSlider
QComboBox
QDateTimeEdit
QLineEdit
QListWidget
QSpinBox


Есть еще метод QWizard::setDefaultProperty(), который позволяет расширить список классов. Например:

    setDefaultProperty("QTableWidget", "currentItem", SIGNAL(currentItemChanged()));
    ...
    QTableWidgetItem *item = field("QTableWidget").value<QTableWidgetItem *>();


По идее любому своему объекту (QObject) можно добавлять динамические свойства и получать их из любой страницы. На самом деле это эквивалентно такому:

Wizard::Wizard(QWidget *parent) : public QWizard(parent)
{
    setProperty("pointerToMyTable", qVariantFromValue(tableWidgetPtr));
}

WizardPage1::WizardPage1(QWidget *parent) : public QWizardPage(parent)
{
    QTableWidget *table = parent()->property("pointerToMyTable").value<QTableWidget *>();
}


Но это в теории, сам я с этим классом никогда не работал.

Автор: FladeX 25.3.2009, 13:07

То есть для чекбоксов получается так:

    setDefaultProperty("QCheckBox", "currentItem", SIGNAL(currentItemChanged()));

    func01 = new QCheckBox(QObject::trUtf8("Чекбокс"));

    registerField("introfunc01", func01);

И потом обращаться как:
field(introfunc01)->isChecked(true)

Но не получается (

Автор: SABROG 25.3.2009, 15:00

Цитата(FladeX @ 25.3.2009, 13:07) *
field(introfunc01)->isChecked(true)


Че за бред :wacko:

field возвращает QVariant, а там уже может быть что-угодно, а не указатель на QCheckBox. Ты уж определись, что ты используешь registerField или setDefaultProperty. Эти функции делают одно и то же по сути.

Автор: FladeX 3.4.2009, 16:05

То есть получается что для чекбоксов registerField вообще не подходит?
А с setProperty так и не разобрался, как их использовать в случае чекбоксов...

Автор: FladeX 14.5.2009, 11:07

Подскажите что можно сделать с чекбоксами :)
На первой странице несколько чекбоксов, каждый из которых соответсвует одной странице. При checked страница будет доступна в визарде, при !checked не будет. >_<

Автор: Kagami 14.5.2009, 11:57

Что-то все коллективно тупят :)

Цитата
The QCheckBox widget provides a checkbox with a text label. More...

#include <QCheckBox>
Inherits QAbstractButton.

Автор: SABROG 14.5.2009, 12:13

Цитата(FladeX @ 14.5.2009, 12:07) *
Подскажите что можно сделать с чекбоксами :)
На первой странице несколько чекбоксов, каждый из которых соответсвует одной странице. При checked страница будет доступна в визарде, при !checked не будет. >_<

Я тебя правильно понимаю, проблема в том, что в "нелинейном" визарде переход на страницу либо осуществляется либо нет, но она доступна. Ты хочешь динамически генерить и удалять страницы в зависимости от выбора в чекбоксе?

Автор: FladeX 14.5.2009, 13:27

Kagami, и тем не менее, через registerFields у меня не получилось.
SABROG, генерировать и удалять не нужно. Есть конечный список предопределенных страниц, каждой из которых сопоставлен чекбокс. Если чекбокс отмечен, то эта страница будет отображаться в визарде по кнопке Next. Если чекбокс не отмечен, то страница эта не будет отображаться. Для такой реализации нужно просто перезадать nextId для каждой из страниц. Однако при этом я столкнулся с трудностью - область видимости переменных - чекбоксы-то только на первой странице, а nextId по одному на каждой (каждая страница - отдельный класс, унаследованный от QWizardPage).

Автор: Kagami 14.5.2009, 14:06

В конструкторе страницы делаем:

registerField("checkBox1", checkBox1);

В nextID():
if (field("checkBox1").toBool()) {
//Если выбрано
...
} else {
//Иначе
...
}

P.S. Если читать документацию внимательней, то необходимость в 80% темах отпадает :)

Автор: FladeX 18.5.2009, 14:30

Да, теперь все работает. Благодарю!

Еще хочу выводить в QLineEdit (на каждой странице свой) какой-нибудь текст, причем текст зависит от значений QComboBox и QCheckBox, расположенных на странице. Я все написал, но у меня теперь показывает лишь первое значение, которое характерно для дефолтного состояния QComboBox и QCheckBox, а при их изменении значение QLineEdit не изменяется, хотя должно. Подскажите, почему так?

Автор: Kagami 18.5.2009, 20:05

Немного подробностей реализации бы не помешало :)

Автор: FladeX 19.5.2009, 11:03

cpp
Func09Page::Func09Page(QWidget *parent)
    : QWizardPage(parent)
{
    setTitle(QObject::trUtf8("Заголовок"));
    setSubTitle(QObject::trUtf8("<br>"));
    setPixmap(QWizard::LogoPixmap, QPixmap(":/images/logo.png"));
    setPixmap(QWizard::WatermarkPixmap, QPixmap(":/images/func09.png"));

    check0901 = new QCheckBox(QObject::trUtf8("Текст"));
    label0901 = new QLabel(QObject::trUtf8("Текст"));
    combo0901 = new QComboBox;

    label0902 = new QLabel(QObject::trUtf8("Текст"));
    combo0902 = new QComboBox;

    check0903 = new QCheckBox(QObject::trUtf8("Текст"));
    label0903 = new QLabel(QObject::trUtf8("Текст"));
    combo0903 = new QComboBox;

    check0904 = new QCheckBox(QObject::trUtf8("Текст"));
    label0904 = new QLabel(QObject::trUtf8("Текст"));
    combo0904 = new QComboBox;

    summa09 = new QLineEdit;
    registerField("summafunc09", summa09);

    combo0901->setMaximumWidth(250);
    combo0902->setMaximumWidth(250);
    combo0903->setMaximumWidth(250);
    combo0904->setMaximumWidth(250);

    combo0901->insertItem(1, "10");
    //...
    combo0901->insertItem(50, "500");

    combo0902->insertItem(1, "1.5");
    //...
    combo0902->insertItem(14, "8.0");

    combo0903->insertItem(1, "10");
    //...
    combo0903->insertItem(50, "500");

    combo0904->insertItem(1, "10");
    //....
    combo0904->insertItem(50, "500");

    double h, s1, s2, s3, summa;
    QString hstring, s1string, s2string, s3string;
    hstring = combo0902->currentText();
    s1string = combo0901->currentText();
    s2string = combo0903->currentText();
    s3string = combo0904->currentText();
    h = hstring.toDouble();
    s1 = s1string.toDouble();
    s2 = s2string.toDouble();
    s3 = s3string.toDouble();
    summa = 0;
    if (check0901->isChecked())
    {
        if ((h>=1.5) && (h<2.0))
        {
            // сверхтонкая стяжка
            summa += 1578.1 * s1;
        }
        else if ((h>=2.0) && (h<3.5))
        {
            // тонкая стяжка
            summa += 1623.9 * s1;
        }
        else if ((h>=3.5) && (h<=8.0))
        {
            // нормальная стяжка
            summa += 2040.5 * s1;
        }
    }
    if (check0903->isChecked())
    {
        summa += 4 * sqrt(s2) * 273.0;
    }
    if (check0904->isChecked())
    {
        summa += 2040.5 * s3;
    }

    QString value = QString::number(summa);
    summa09->setText(value);

    QGridLayout *layout = new QGridLayout;
    layout->addWidget(check0901, 0, 0);
    layout->addWidget(label0901, 1, 0);
    layout->addWidget(combo0901, 2, 0);
    layout->addWidget(label0902, 3, 0);
    layout->addWidget(combo0902, 4, 0);
    layout->addWidget(check0903, 5, 0);
    layout->addWidget(label0903, 6, 0);
    layout->addWidget(combo0903, 7, 0);
    layout->addWidget(check0904, 0, 1);
    layout->addWidget(label0904, 1, 1);
    layout->addWidget(combo0904, 2, 1);
    layout->addWidget(summa09, 3, 1);
    setLayout(layout);
}

h
class Func09Page : public QWizardPage
{
    Q_OBJECT
public:
    Func09Page(QWidget *parent = 0);

private:
    QLabel *label0901;
    QLabel *label0902;
    QLabel *label0903;
    QLabel *label0904;
    QCheckBox *check0901;
    QCheckBox *check0903;
    QCheckBox *check0904;
    QComboBox *combo0901;
    QComboBox *combo0902;
    QComboBox *combo0903;
    QComboBox *combo0904;
    QLineEdit *summa09;
};

А использовать пытаюсь здесь:
cpp
OutroPage::OutroPage(QWidget *parent)
    : QWizardPage(parent)
{
    setTitle(QObject::trUtf8("Заголовок"));
    setSubTitle(QObject::trUtf8("Подзаголовок"));
    setPixmap(QWizard::LogoPixmap, QPixmap(":/images/logo.png"));
    setPixmap(QWizard::WatermarkPixmap, QPixmap(":/images/outro.png"));

    label9901 = new QLabel(QObject::trUtf8("Текст:"));
    line9901 = new QLineEdit;

    QString summa09 = field("summafunc09").toString();
    line9901->setText(summa09);

    QVBoxLayout *layout = new QVBoxLayout;
    layout->addWidget(label9901);
    layout->addWidget(line9901);
    setLayout(layout);
}

Автор: Kagami 19.5.2009, 11:37

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

Раскрывающийся текст
    double h, s1, s2, s3, summa;
    QString hstring, s1string, s2string, s3string;
    hstring = combo0902->currentText();
    s1string = combo0901->currentText();
    s2string = combo0903->currentText();
    s3string = combo0904->currentText();
    h = hstring.toDouble();
    s1 = s1string.toDouble();
    s2 = s2string.toDouble();
    s3 = s3string.toDouble();
    summa = 0;
    if (check0901->isChecked())
    {
        if ((h>=1.5) && (h<2.0))
        {
            // сверхтонкая стяжка
            summa += 1578.1 * s1;
        }
        else if ((h>=2.0) && (h<3.5))
        {
            // тонкая стяжка
            summa += 1623.9 * s1;
        }
        else if ((h>=3.5) && (h<=8.0))
        {
            // нормальная стяжка
            summa += 2040.5 * s1;
        }
    }
    if (check0903->isChecked())
    {
        summa += 4 * sqrt(s2) * 273.0;
    }
    if (check0904->isChecked())
    {
        summa += 2040.5 * s3;
    }

    QString value = QString::number(summa);
    summa09->setText(value);

Автор: Гость 24.11.2012, 17:12

посмотрите пожалуйста мою тему, аналогичная ситуация, не получается выцепить значение с предыдущей страницы, хотя все как в примере делаю
http://www.forum.crossplatform.ru/index.php?showtopic=8974

Форум Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)