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

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

Страниц: [1] 2   Вниз
  Печать  
Автор Тема: connect и динамический отправитель  (Прочитано 8798 раз)
Rosster
Гость
« : Апрель 26, 2013, 14:59 »

Всем привет. У меня в connect отправитель все время меняется. Вот пример:
Код:
Class Main
{
   public slots:
      virtual void add() = 0;
}
Class A : public Main
{
   public slots:
      void add() {cout << "A";}
}
Class B : public Main
{
   public slots:
      void add() {cout << "B";}
}

MainWindow::MainWindow(QWidget *parent)
{
   A *a = new A();
   B *b = new B();
   Main *main = a;
   // connect пишется после инициализации main, иначе это критическая ошибка.
   connect(sender, SIGNAL(signal()), main, SLOT(add()));
   emit sender->signal();
   main = b;
   emit sender->signal();
}
Вместо sender - виджет с его сигналом. Так вот: в первом случае вывод будет "А", и во втором тоже "А", а мне нужно понятное дело "В". Как быть? disconnect и снова connect не вариант. Так как A, B.... и виртуальных функций может быть много, в итоге неимоверный код. Сейчас приходится прибегать к тому, что создавать в MainWindow слот и уже там писать одну лишь строку {main->add()}. Но код то это не красит...
Есть ли решение?
Второй вопрос: почему даже если я поставлю в классе Main доступ private slots, то компилятор не ругнется и программа при запуске сработает нормально, хотя connect не имеет права уже этот метод использовать?
Спасибо.
P.S. Сорри за краткость и неполноту кода, главное что смысл понятен.
Записан
Serr500
Гость
« Ответ #1 : Апрель 26, 2013, 15:24 »

QMetaObject::invokeMethod не подойдёт?
Записан
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #2 : Апрель 26, 2013, 15:44 »

Может и не нужны никакие сигналы?

Можно сделать так, как вариант:

Код
C++ (Qt)
#include <iostream>
#include <functional>
 
struct base  {
   virtual ~base() {}
   virtual void print() = 0;
};
 
struct derived1 : public base {
   virtual void print() { std::cout << "A" << std::endl; }
};
 
struct derived2 : public base {
   virtual void print() { std::cout << "B" << std::endl; }
};
 
 
int main()
{
   base *d1 = new derived1;
   base *d2 = new derived2;
 
   std::function<void(base*)> func(&base::print);
 
   func(d1);
   func(d2);
 
   return 0;
}
 
Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
mutineer
Гость
« Ответ #3 : Апрель 26, 2013, 15:48 »

Для слота не принципиально private он или public, потому что при вызове через сигнал/слот вызывает слот сам объект-владелец слота, а не кто-то снаружи.

Твое решение не работает, потому что connect привязывается к конкретному объекту в момент коннекта, а не в момент испускания сигнала. Рассматривай варианты выше
Записан
Rosster
Гость
« Ответ #4 : Апрель 26, 2013, 16:22 »

QMetaObject::invokeMethod не подойдёт?
Можно поподробней? Почитал про него, он нужен при потоках и просто вызывает слот.Тоже самое я могу сделать в слоте
MainWindow::add() {QMetaObject::invokeMethod(main, "add");} вместо
MainWindow::add() {main->add();}
Или я чет не догоняю?

Может и не нужны никакие сигналы?
Не подойдет. У меня sender - это кнопка в интерфейсе, кнопка мультизадачная, на нее нужно повесить слот add(), который вызывается на другом виджете.
Короче это похоже на задачу: одна кнопка "добавить запись" и куча виджетов для которых эта кнопка пригодится.
Записан
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #5 : Апрель 26, 2013, 16:34 »

Не подойдет. У меня sender - это кнопка в интерфейсе, кнопка мультизадачная, на нее нужно повесить слот add(), который вызывается на другом виджете.
Короче это похоже на задачу: одна кнопка "добавить запись" и куча виджетов для которых эта кнопка пригодится.

Если sender в курсе (в смысле знает) у какого именно объекта в данный момент должен быть вызван слот, то проблемы не вижу..
Сигнал кнопки можно повесить на слот (как некий посредник), в котором уже вызывать std::function с нужным указателем на виджет...
Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
mutineer
Гость
« Ответ #6 : Апрель 26, 2013, 17:02 »

QMetaObject::invokeMethod не подойдёт?
Можно поподробней? Почитал про него, он нужен при потоках и просто вызывает слот.Тоже самое я могу сделать в слоте
MainWindow::add() {QMetaObject::invokeMethod(main, "add");} вместо
MainWindow::add() {main->add();}
Или я чет не догоняю?

1) Он нужен не только при потоках, а и например тогда, когда нужно вызвать метод по его строковому имени у неизвестного QObject
2) Сделать можешь, но если main указывает на QObject, то придется кастить
Записан
Rosster
Гость
« Ответ #7 : Апрель 26, 2013, 18:58 »

Сигнал кнопки можно повесить на слот (как некий посредник), в котором уже вызывать std::function с нужным указателем на виджет...
Сделать можешь, но если main указывает на QObject, то придется кастить
У меня уже есть посредник (смысл мне в std::function?), сейчас я и пишу:
Код:
MainWindow::MainWindow()
{
   connect(button, SIGNAL(clicked()), this, SLOT(slAdd()));
   // connect(button, SIGNAL(clicked()), main, SLOT(add())); // хочу так
}
MainWindow::slAdd() {main->add();}
Работает без проблем. Сам main наследован от QWidget. И если main будет указывать уже на другой виджет, то вызовется требуемый add().
Тока как я и говорил если у меня куча кнопок а также виртуальных функций,мне для каждого делать посредника? Не люблю лишних функций
Раз нельзя сделать динамический connect, то как это обойти даже с помощью QMetaObject::invokeMethod() я не понял.
Записан
Bepec
Гость
« Ответ #8 : Апрель 26, 2013, 20:42 »

Rosster, опиши пожалуйста в общих словах, что ты хочешь сделать. Если нехватает слов, нарисуй.

PS ваша проблема скорее всего происходит из плохой архитектуры. Расскажите свою идею, а мы (ну или только я) подскажу как её можно реализовать просто и наглядно.
Записан
Rosster
Гость
« Ответ #9 : Апрель 26, 2013, 21:20 »

Хорошо:) Есть QMainWindow. На нем штук 10 кнопок. В QTabWidget куча вкладок с виджетами. Их может быть и 3 и 10. И каждый виджет должен работать с этими кнопками. Поэтому было принято решение наследоваться этим виджетам от главного, в котором виртуальные функции для каждой из кнопок. Благодаря этому каждому виджету придется (и это правильно) работать с этими кнопками. В итоге такой код:
Код:
Class Manager : public QWidget
{
public slots:
   virtual void add() = 0;
   virtual void del() = 0;
   ...
}

Class ListWidget : public Manager
{
public slots:
   void add() {}
   void del() {};
   ...
}

Class TreeWidget : public Manager
{
public slots:
   void add() {}
   void del() {};
   ...
}

Class MainWindow : public QMainWindow
{
   MainWindow();
public slots:
   void slChange();
   void slAdd();
   Manager *_manager;
}
//.cpp
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
{
    ui->setup(this); //допустим там 2 виджета ui->listWidget и ui->treeWidget

    manager = ui->listWidget;

    connect(ui->tabWidget, SIGNAL(currentChanged(int)), this, SLOT(slChange()));

    // connect(ui->button, SIGNAL(clicked()), manager, SLOT(add())); // не катит
    connect(ui->button, SIGNAL(clicked()), this, SLOT(slAdd())); //все отлично
}

MainWindow::slChange()
{
   if (ui->tabWidget->currentWidget() == ui->listWidget)
       manager = ui->listWidget;
   
   if (ui->tabWidget->currentWidget() == ui->treeWidget)
       manager = ui->treeWidget;
}

MainWindow::slAdd() {manager->add();}

Получается для add(), del() и т.д. нужно писать отдельный слот. Для типичной реализации нужно для трех классов писать кучу слотов. Причем MainWindow - посредник. Получается как минимум некрасивый код.
Записан
thechicho
Гость
« Ответ #10 : Апрель 28, 2013, 17:34 »

Manager *_manager;
manager = ui->listWidget;

чот я нифига не понял.
есть QMainWindow. на нем QTabWidget. на какой-то вкладке есть QListWidget
так же на QMainWindow есть кнопка добавить pushButtonAdd
MainWindow::on_pushButtonAdd_clicked()
{
    if (ui->tabWidget->currentIndex() == 0) {
    ....
    } else if (ui->tabWidget->currentIndex() == 1) {
        ui->listWidgetN->addItem(ui->lineEditN->text());    
    } else if (ui->tabWidget->currentIndex() == N) {
    ...
    }
}
« Последнее редактирование: Апрель 28, 2013, 17:36 от thechicho » Записан
Bepec
Гость
« Ответ #11 : Апрель 28, 2013, 21:16 »

Кхм. Правильно я всё таки спросил Веселый

У вас вот какая ситуация (если неправ, поправьте).

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

Зачем вам выносить этот код в каждый виджет? Сделайте проще - 3 слота (add, delete, insert, допустим). И в каждом не жёстко задаёте свой виджет, а используете лишь текущий видимый.

Т.е. примерно так (псевдокод)

Код:
void add()
{
listWidget* point = ui.tabWidget->currentWidget();
// получаем текущий виджет и присваиваем указателю.

// и далее реализуем всё что угодно.
}

PS если кнопки одинаковы, то и реакция на них должна быть одинаковая? Если для каждого виджета реакция должна быть разная, тут никуда не уйти от виртуальных функций.
Записан
Rosster
Гость
« Ответ #12 : Апрель 29, 2013, 09:53 »

MainWindow::on_pushButtonAdd_clicked()
{
    if (ui->tabWidget->currentIndex() == 0) {
    ....
    } else if (ui->tabWidget->currentIndex() == 1) {
        ui->listWidgetN->addItem(ui->lineEditN->text());    
    } else if (ui->tabWidget->currentIndex() == N) {
    ...
    }
}
И вот так для каждой кнопки расписывать?зачем?Виртуальная функция позволяет это все проделать одной строкой. Но все равно мой код считаю не есть гуд.

PS если кнопки одинаковы, то и реакция на них должна быть одинаковая? Если для каждого виджета реакция должна быть разная, тут никуда не уйти от виртуальных функций.
Именно) Для каждого виджета разная. Нажатие кнопки влияет только на текущую вкладку, и этот add() для каждого виджета разный и этот метод обязателен в классе виджета.
Записан
Bepec
Гость
« Ответ #13 : Апрель 29, 2013, 10:29 »

Тогда от виртуальных методов никуда не деться.

Совмещайте - получаем текущий виджет и вызываем у него метод кнопочный.

Не связывая слоты.

Типа код:
Код:
// текущий виджет получаем.
QWidget * point = ui.tabWidget->currentWidget();
// и слот такой вот вызываем
QMetaObject::invokeMethod(point, "add");
// строку меняем "add" на "del" или другое любое наименование слота.
« Последнее редактирование: Апрель 30, 2013, 14:05 от Bepec » Записан
thechicho
Гость
« Ответ #14 : Апрель 29, 2013, 11:37 »

//И вот так для каждой кнопки расписывать?зачем?Виртуальная функция позволяет это все проделать одной строкой.
как?

зачем городить классы-наследники, если к виджетам уже есть доступ.
можно же сразу все описать в 1 месте в зависимости от текущего индекса таба.
так же будет понятнее и по "затрам" дешевле.
или я не так понял задачу?

if (ui->tabWidget->currentWidget() == ui->listWidget)
       manager = ui->listWidget;

а как тут может быть равенство?

QWidget * QTabWidget::currentWidget () const
Returns a pointer to the page currently being displayed by the tab dialog. The tab dialog does its best to make sure that this value is never 0 (but if you try hard enough, it can be).

currentWidget() возвращает же указатель на вкладку, а не на виджет, расположенный на этой вкладке (тем более их может быть несколько).
или я опять не так понял?
Записан
Страниц: [1] 2   Вверх
  Печать  
 
Перейти в:  


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