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

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

Страниц: [1] 2   Вниз
  Печать  
Автор Тема: Вызвать конструктор наследника.  (Прочитано 8526 раз)
Alex_C
Гость
« : Апрель 20, 2012, 10:47 »

Есть много окон, наследуемых от одного базового класса AbstractWindow.
При вызове соответствующего QAction происходит открытие или закрытие соответствующего окна

Код:
void MainForm::showCalculator()
{
    if(calcWindow == 0)
    {
        QAction *a = qobject_cast<QAction *>(sender());
        calcWindow = new CalcWindow(a, m_Settings, false, this);
        calcWindow->show();
    }
    else if(calcWindow->isVisible() && (lastFocusForm = f_CalacWindow))
        calcWindow->close();
    else
        calcWindow->show();
}

где calcWindow  - как раз одно из этих окон, наследник AbstractWindow.
Получается что такую ф-цию нужно писать для каждого открываемого окна.
Вопрос - а можно сделать что-то типа

Код:
void MainForm::showCalculator(AbstractWindow w)
{
  if(w == 0)
     w = new ... // а вот тут чтоб вызывался конструктор не AbstractWindow , а наследника?
 
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #1 : Апрель 20, 2012, 11:06 »

Код:
void MainForm::showCalculator(AbstractWindow w)
{
  if(w == 0)
     w = new ... // а вот тут чтоб вызывался конструктор не AbstractWindow , а наследника?
 
Нужно каким-то образом знать "а которого наследника". Можно напр поместить эту информацию в QAction::setData
Записан
alexis031182
Гость
« Ответ #2 : Апрель 20, 2012, 11:13 »

Можно через фабрику (QMetaType), правда понадобится имя класса в виде строки.
Код:
AbstractWindow *MainForm::instance(const QString &win_name)
{
int id = QMetaType::type(win_name.toUtf8());
if(id) return (AbstractWindow*)QMetaType::construct(id);

return NULL;
}
Примечание: все дочерние классы от AbstractWindow (если их создавать указанным способом) должны иметь конструктор по умолчанию, конструктор копирования, объявлены через Q_DECLARE_METATYPE и зарегистрированы через qRegisterMetaType().
Записан
V1KT0P
Гость
« Ответ #3 : Апрель 20, 2012, 11:15 »

Код:
void MainForm::showCalculator(AbstractWindow w)
{
  if(w == 0)
     w = new ... // а вот тут чтоб вызывался конструктор не AbstractWindow , а наследника?
 
Ну смотри как тут можно сделать, если w был изначально не AbstractWindow а наследником который нам и требуется, то надо сделать виртуальную функцию которая будет создавать новый объект своего типа и возвращать указатель приведенный к базовому классу. В результате каждый новый наследуемый класс будет переопределять эту функцию и все будет работать так как ты хочешь, если я правильно понял то что ты хочешь.
Записан
Alex_C
Гость
« Ответ #4 : Апрель 20, 2012, 14:56 »

Ок! Направление деятельности ясно.
Теперь вопрос :
как зарегистрировать класс, с конструктором
CalcWindow(QAction *a, QSettings *s, bool isModal, QWidget *parent)


Делаю так - выдает ошибку.
Код:
Q_DECLARE_METATYPE(CalcWindow(QAction *a, QSettings *s, bool isModal, QWidget *parent))
Записан
Alex_C
Гость
« Ответ #5 : Апрель 20, 2012, 14:58 »

Вообще как я понимаю, самый простой вариант через QAction->setData, но для этого нужно свои классы окон через Q_DECLARE_METATYPE зарегистрировать.
Записан
alexis031182
Гость
« Ответ #6 : Апрель 20, 2012, 15:00 »

Ок! Направление деятельности ясно.
Теперь вопрос :
как зарегистрировать класс, с конструктором
...
С таким конструктором этого не сделать. Должен быть т.н. конструктор по умолчанию (т.е. без параметров, либо с параметрами, имеющими значения по умолчанию). Это по сути всплывает проблема, которая обсуждалась в другой теме (по поводу получения имени класса в конструкторе).
Записан
Alex_C
Гость
« Ответ #7 : Апрель 20, 2012, 15:01 »

Единственное что у меня классы окон определены как
Код:
QPointer<CalcWindow> calcWindow;

как только их через QAction->setData() передавать...
Записан
Alex_C
Гость
« Ответ #8 : Апрель 20, 2012, 15:04 »

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

Мда. В таком случае получается так сделать нельзя. Конструктор по умолчанию мне не подходит.
Записан
alexis031182
Гость
« Ответ #9 : Апрель 20, 2012, 15:08 »

Мда. В таком случае получается так сделать нельзя. Конструктор по умолчанию мне не подходит.
Сами себе закрыли доступ к этому функционалу.
Записан
Alex_C
Гость
« Ответ #10 : Апрель 20, 2012, 15:29 »

Хорошо, а вот такой вариант
Код:
typedef void (*ShowFunc)(void);

QAction* MainForm::createActionForWindows(QString winName, ShowFunc f)
{
    QAction *a = new QAction(winName, 0);
    connect(a, SIGNAL(triggered()), SLOT(f));
    a->setCheckable(true);
    a->setObjectName(winName);
    if(m_Settings->value("MainSetup/" + winName, false).toBool())
        a->trigger();
    return a;
}

void MainForm::showCalculator()
{
    if(calcWindow == 0)
    {
        QAction *a = qobject_cast<QAction *>(sender());
        calcWindow = new CalcWindow(a, m_Settings, false, this);
        calcWindow->show();
    }
    else if(calcWindow->isVisible() && (lastFocusForm = f_CalacWindow))
        calcWindow->close();
    else
        calcWindow->show();
}


И создавать-восстанавливать вид окон так

Код:
QAction *w1 = createActionForWindows("Calculator", showCalculator);

Вот такой вариант? Да, для каждого окна ф-ция showCalculator - будет своя, но в принципе пока так сойдет.
Единственное что:
1. Вызов createActionForWindows("Calculator", showCalculator); - выдает ошибку.
2. А так прокатит:
Код:
QAction* MainForm::createActionForWindows(QString winName, ShowFunc f)
{
    ...
    connect(a, SIGNAL(triggered()), SLOT(f));
вызов SLOT(f) ?
Записан
mutineer
Гость
« Ответ #11 : Апрель 20, 2012, 15:33 »

2. А так прокатит:
Код:
QAction* MainForm::createActionForWindows(QString winName, ShowFunc f)
{
    ...
    connect(a, SIGNAL(triggered()), SLOT(f));
вызов SLOT(f) ?

нет, это не сработает - SLOT преобразовывает полученное в строку, а получит он "f"
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #12 : Апрель 20, 2012, 15:59 »

Вообще как я понимаю, самый простой вариант через QAction->setData, но для этого нужно свои классы окон через Q_DECLARE_METATYPE зарегистрировать.
Вам нужно просто "новое окно", а не сериализация, поэтому Q_DECLARE_METATYPE здесь не нужен. Сделайте скромную ф-цию типа
Код
C++ (Qt)
AbstractWindow * MyCreateWindow( int ID. ... )
{
switch (ID) {
  case ID_CALC: return new CalcWindow(..);
  ...
}
assert(0);
return 0;
}
 
И в QAction запишите просто ID. Не переживайте что это "не совсем круто/грамотно". Так или иначе такая конструкция неизбежна.
Записан
Alex_C
Гость
« Ответ #13 : Апрель 20, 2012, 21:43 »

Всем большое спасибо за советы - очень помогают в освоении Qt!
Целый день прикидывал и так, и так. В результате пришел к выводам некоторым.
Открываю сайт - спасибо Igors за невольную поддержку! Улыбающийся
Совершенно с Вами согласен - вроде как хочется "круто/грамотно", но это не должно все же идти в разрез здравой логики.
Думаю, то, что Вы предложили - самое  и простое, и верное.
Во всяком случае других, более простых и правильных вариантов по моему нет.
P.S. Однако много интересного про QMetaType прочел - тема была поднята не даром Улыбающийся
Записан
Alex_C
Гость
« Ответ #14 : Апрель 21, 2012, 08:09 »

Нашел весьма хорошее решение своей проблемы. Приведу его тут - может кому пригодится.
showWindow - это SLOT, вызываемый при клике на меню открытия окна

Код:
void MainForm::showWindow()
{
    QAction *a = qobject_cast<QAction *>(sender());

    AbstractLogWindow *w = findWindow(a->objectName());
    if(w != 0)
    {
        if(a->data().toInt() != int(lastFocusForm))
            w->show();
        else
            w->close();
    }
    else
    {
        w = createWindow(a);
        w->show();
    }
    a->setChecked(w ? true : false);
}

AbstractLogWindow* MainForm::createWindow(QAction *a)
{
    if(a->objectName() == "CalcWindow")
        return calcWindow = new CalcWindow(a, m_Settings, false, this);
    // здесь добавляем другие нужные нам конструкторы окон
    return 0;
}

AbstractLogWindow *MainForm::findWindow(QString winName)
{
    QObject *b;
    foreach(b, this->children())
        if(b->objectName() == winName)
        {
            AbstractLogWindow *l = qobject_cast<AbstractLogWindow *>(b);
            if(l != 0)
                return l;
        }
    return 0;
}
Записан
Страниц: [1] 2   Вверх
  Печать  
 
Перейти в:  


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