LINUX.ORG.RU

[Qt, слот-сигнал] где проводить connect — в дочке или родителе?


0

0

Есть вью, на нём можно делать всякие инструменты — выделения (как в гимпе) и т.п. Одновременно может существовать только один инструмент. Короче, в зависимости от действий пользователя создаётся-удаляется некий виджет.

Это сделано так:

if(event->button() == Qt::LeftButton) {
    if(m_anchor->isPossibleOrEnabled()) {
        m_is_creating_tool = true;
        switch(m_tool_to_create) {
        case QFrost::no_tool:
            m_is_creating_tool = false;
            event->ignore();
            return;
        case QFrost::rectangle_selection:
            m_tool = new RectangleSelection(this);
            break;
        case QFrost::ellipse_selection:
            m_tool = new EllipseSelection(this);
            break;
        case QFrost::cell_creator:
            m_tool = new CellCreator(this);
            break;
        }
        qfMainWindow()->emitCurrentToolsSettings();
    }
    event->accept();
}

m_tool — указатель на абстрактный объект класса Tool. Так вот, у каждого Tool есть какие-то свои слоты, которые должны ловить соответствующие сигналы (для каждого унаследованного от Tool класса это индивидуально). Где проводить connect — в конструкторе этих штук или в приведённом выше куске кода, сразу после new?

На данный момент провожу это в конструкторе этих виджетов, соответственно их слоты можно делать private, что вроде бы как делает всё безопаснее. Вроде бы как, удобно, но хотелось бы знать, как принято в больших проектах.

P.S: если кому-нибудь любопытно, qfMainWindow()->emitCurrentToolsSettings() — это функция, которая заставляет один из виджетов mainwindow послать сигналы о настройках инструментов (то есть сигналы о настройках всех возможных инструментов). Это нужно, чтобы инструмент знал о своих свойствах сразу же после создания. Ну а когда мы его создали, свойства можно ещё менять и он будет получать информацию из этих же сигналов.

★★★★★

Последнее исправление: Obey-Kun (всего исправлений: 2)

Где проводить connect — в конструкторе этих штук или в приведённом выше куске кода, сразу после new

Второй вариант.

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

Где проводить connect — в конструкторе этих штук или в приведённом выше куске кода, сразу после new

Второй вариант.

я могу так же необоснованно сказать «первый вариант». Попробуйте аргументировать, подозреваю, что ТС хотел именно аргументации.

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

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

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

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

нет-нет, разным инструментам нужны разные сигналы (хотя некоторые сигналы — общие)... вот конструкоры двух инструментов:

CellCreator::CellCreator(View* parent): RectangularTool(parent),
    m_cells_size(QSize(30, 40)),
    m_step(m_cells_size),
    m_quantity_of_primary_columns(0),
    m_quantity_of_primary_rows(0),
    m_initial_point(rectInScene().topRight()),
    m_last_point(rectInScene().bottomLeft()),
    m_primary_last_point(m_initial_point),
    m_tetris_in_primary(thereIsPotentialTetris(rectInScene())),
    m_tetris_in_complementary(thereIsPotentialTetris(rectInScene())),
    m_start_corner(Qt::TopLeftCorner)
{
    denyDraggingCenter();
    connect(view()->qfMainWindow(), SIGNAL(signalCellCreatorCellsSizeChanged(QSizeF)),
            SLOT(slotChangeCellsSize(QSizeF)));

    connect(view()->qfMainWindow(), SIGNAL(signalCellCreatorStartCornerChanged(Qt::Corner)),
            SLOT(slotChangeStartCorner(Qt::Corner)));

    connect(view()->qfMainWindow(), SIGNAL(signalSpacePressed(Qt::KeyboardModifiers)),
            SLOT(slotApply(Qt::KeyboardModifiers)));
}
EllipseSelection::EllipseSelection(View* parent): RectangularTool(parent)
{ 
    connect(view()->qfMainWindow(), SIGNAL(signalEllipseSelectionTypeChanged(QFrost::SelectionType)),
            SLOT(slotChangeSelectionType(QFrost::SelectionType)));

    connect(view()->qfMainWindow(), SIGNAL(signalSpacePressed(Qt::KeyboardModifiers)),
            SLOT(slotInverseSelection(Qt::KeyboardModifiers)));
}

Как видите, им нужен 1 общий сигнал (хотя коннект идёт к разным слотам, хотя не проблема сделать в их общем предке виртуальный слот, чтобы подобное можно было делать в приведённом выше коде родителя) + для одного из них нужно два своих сигнала, а для другого один.

Вообще, мне писали вот так:

вообще соединять в дочернем объекте сигнал родителя - плохая идея, т.к., как минимум, при чтении кода, сначала читается код верхнего, по иерархии, объекта. И лишь при возникновении непонимания лезишь внутрь дочернего. Поэтому такое соединение нужно делать в родительском объекте.

Но интересует мнение ещё пары людей или пример из какого-нибудь большого проекта.

Obey-Kun ★★★★★
() автор топика
Ответ на: комментарий от panter_dsd

тут вообще из ребёнка лезется в папашу папаши (view()->qfMainWindow()->сигналТакойТо()).

Плюс по сути qfMainWindow() сигналы испускает не сам по себе, а делает переадресацию (т.е. коннект сигнал-сигнал) из другого своего дочернего виджета. А этот дочерний виджет, в свою очередь, также переадресовывает сигналы, уже из нескольких дочек (нескольких виджетов настройки инструментов, они в QStackedWidget и показывается тот, который управляет выбранным в данный момент инструментом).

В общем-то, логика понятна, но тогда слоты в Tool-ах придётся делать публичными, не чревато ли это?

Obey-Kun ★★★★★
() автор топика

Где проводить connect — в конструкторе этих штук или в приведённом выше куске кода, сразу после new?

конечно после new

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

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

дык здесь, из-за наличия у вью public функции qfMainWindow(), которая возвращает указатель на его родителя, дочки вполне себе видят всё что им надо

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

при таком ракурсе можно конечно было бы использовать и текущее решение оставляя слоты приватными, однако выглядит нелогично, всмысле никому в голову не придёт что в new делается байндинг

лучше в классе сделайте функцию connect, типа

EllipseSelection::Connect(Parent* parent) {
    connect(parent, SIGNAL(signalEllipseSelectionTypeChanged(QFrost::SelectionType)), 
            SLOT(slotChangeSelectionType(QFrost::SelectionType))); 
}

сразу говорю, код поясняет только идею, работать не обязан :) название connect мне не очень нравится, больше подходит bind

и тогда будет что-то типа

        case QFrost::rectangle_selection: 
            m_tool = new RectangleSelection(this); 
            m_tool->Connect(this);
            break; 

тоже малец коряво, но зато видно что происходит

Вы эта, точнее задачу описывайте на входе штоле

shty ★★★★★
()
Ответ на: комментарий от Obey-Kun

дык здесь, из-за наличия у вью public функции qfMainWindow(), которая возвращает указатель на его родителя, дочки вполне себе видят всё что им надо

ну это понятно... теперь :) когда я писал пост того ещё не было :)

по поводу байндинга - да, так можно сделать, но меня такой подход немного пугает, я тоже в одном проекте походим образом сделал (типа умные функции, всё знают, вызвал её - она все данные набрала, посчитала и результат куда надо положила), так вот практика выявила что такой подход порочем чуть более чем полностью ибо потом:

1) очень трудно читать код (здесь: предполагается что функция new ответственна за создание объекта, если её нагружать сторонними штуками - это, со стороны, неочевидно и выглядит как шаманство)

2) когда появляются неожиданные вещи приходится городить лес из костылей (к примеру, вот у Вас встретится ситуация когда в определённом случае надо будет создать один из имеющихся классов, но connect не делать, что тогда будете делать?)

3) нагороженный лес очень трудно отлаживать

плюс стопитцот пунктов

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

Немного странно выглядит (функция ради функции?), но зато при чтении действительно проясняет суть дела.

Obey-Kun ★★★★★
() автор топика
Ответ на: комментарий от shty

ясно, тогда реализую в Tool виртуальную функцию Bind() и исправлю код на такой:

if(event->button() == Qt::LeftButton) { 
    if(m_anchor->isPossibleOrEnabled()) { 
        m_is_creating_tool = true; 
        switch(m_tool_to_create) { 
        case QFrost::no_tool: 
            m_is_creating_tool = false; 
            event->ignore(); 
            return; 
        case QFrost::rectangle_selection: 
            m_tool = new RectangleSelection(this); 
            break; 
        case QFrost::ellipse_selection: 
            m_tool = new EllipseSelection(this); 
            break; 
        case QFrost::cell_creator: 
            m_tool = new CellCreator(this); 
            break; 
        }
        m_tool->Bind();
        qfMainWindow()->emitCurrentToolsSettings(); 
    } 
    event->accept(); 
} 

Ъ?

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

Немного странно выглядит (функция ради функции?)

ну типа да :)

но зато при чтении действительно проясняет суть дела.

+ позволяет:

1) не нагрудать new чуждой ему функциональностью -> гибко подходить к созданию/связыванию класса

2) сократить число «букаф» по сравнению с написанием connect

3) не тащить наружу слоты

:)

shty ★★★★★
()
Ответ на: комментарий от Obey-Kun

ну, можно так :)

хотя меня немного смущает вынесенный m_tool->Bind(), но это как раз при случае легко поправить будет

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

shty ★★★★★
()

Мне больше нравится концепция, когда потомок соединяет все нужные ему сигналы со своими слотами. Набор сигналов родителя более менее статичен, набор слотов у каждого отдельного виджета может различаться, да и взаимосвязи могут быть разными. Если использовать коннекты в констуркторе, то объект получается «самодостаточным», и не требует выполнения инициализирующих (которые по логике вещей должны быть именно в конструкторе) операций после создания экземпляра.

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

а чем может быть плохо вынесение в отдельный класс? к примеру как то так:

class IWindow{template <typename Window, typename  Tool> friend class ToolFactory;};
class MyWindow : public IWindow{};

class ITool{template <typename Window, typename  Tool> friend class ToolFactory;};
class RectangleSelection : public ITool{};

template<typename Window, typename  Tool>
class ToolFactory{
public:
    static tool* createTool(Window*){
        Tool* tool = new Tool();
        //Global connects & settings
        return tool;
    }
};

template<>
class ToolFactory<MyWindow,RectangleSelection>{
public:
    static RectangleSelection* createTool(MyWindow* _window){
        RectangleSelection* rc_tool = ToolFactory<IWindow, RectangleSelection>::createTool(_window);
        //Custom connects & settings
        return rc_tool;
    }
};
...
case QFrost::rectangle_selection:  
            m_tool =ToolFactory<MyWindow,RectangleSelection>::createTool(this);  
            break;

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

а чем может быть плохо вынесение в отдельный класс?

суть не в вынесении или не вынесении чего-либо куда-либо, а в том что по умолчанию предполагается что методы new или createTool только создают объект и, в большинстве случаев, пресловутый connect внутри этих методов совсем неочевиден, что при использовании таких классов означает ошибки или, скажем, неожиданную реакцию системы (типа я только объект создал, а у меня уже чёта окошко пляшет, а как это отключить?)

да, есть классы типа socket, там операции связывания прозрачны, но даже там они не происходят втихомолку, а в конструктор передаётся номер порта

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