пятница, 19 октября 2012 г.

Обмен данными между формами

Почти всегда стоит задача создать более одной формы. И чаще всего эти формы должны обмениваться данными.
Для примера мы с вами создадим две формы. В первой будет поле ввода и кнопка. В поле ввода мы с вами будем вводить ФИО, а при нажатии на кнопку у нас будет открываться вторая форма, в которой ФИО вводится в 3-х полях, фамилия, имя и отчество. При этом из первой формы во вторую необходимо передать уже введённые данные, дабы не выводить пустую форму. А по нажатию кнопки "Ок" во второй форме передать введённые данные в первую форму.

Итак, для начала создадим стандартное Gui приложение Qt.

Теперь на главной форме в Дизайнере добавим поле ввода (QLineEdit) и кнопку(QBushButton), как показано на рисунке. Имена объектов оставим стандартными, дабы не запутаться.

Теперь нам необходимо создать и добавить вторую форму.
Переходим Файд -> Новый файл или проект... -> Qt -> КлассФормы Qt Designer.
Выбираем Widget, оставляем стандартное имя Form и добавляем в наш проект.
На второй форме добавляем 3 QLineEdit и ButtonBox, имена оставляем стандартными.



Теперь будем действовать по порядку. Первый делом добавим новую форму в уже существующее главное окно. Для этого в mainwindow.h подключим заголовочный файл формы:

#include "form.h"

И создадим указатель на нашу будущую форму:

private:
    Form *myform;

Пол дела сделано. Теперь необходимо создать эту самую форму. Для этого, в конструкторе главного окна добавим создание нового объекта.


MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    myform = new Form(); // создаем нашу форму
}

Теперь нам необходимо по нажатию на кнопку показать 2-ю форму. Для в том же конструкторе подключим сигнал clicked() кнопки к слоту show() нашей формы.
    connect(ui->pushButton, SIGNAL(clicked()), myform, SLOT(show())); // подключаем сигнал к слоту

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

Теперь нам необходимо передать уже введенные данные из первой формы во вторую. Для этого в mainwindow.h добавим сигнал, который будет отправлять введённые данные, и слот, который будет реагировать на нажатие кнопки и вызывать этот сигнал.

signals:
    void sendData(QString str);
private slots:
    void onButtonSend();

Сигнал определять не надо! (у некоторых такое бывает :) )
А вот слот надо определить.

void MainWindow::onButtonSend()
{
    emit sendData(ui->lineEdit->text()); // вызываем сигнал, в котором передаём введённые данные
}

Запись emit вызывает сигнал sendData, который в свою очередь передаёт "куда-то" данные из lineEdit.
Теперь необходимо подключить к слоту onButtonSend сигнал клика по кнопке, дабы он сработал. Лично я все подключения делаю в конструкторе.


connect(ui->pushButton, SIGNAL(clicked()), this, SLOT(onButtonSend())); // подключаем клик по кнопке к определенному нами слоту

Во второй форме необходимо к чему-то подключить этот сигнал, да ещё и обработать.
В
form.h объявляем слот-приемник.


public slots:
    void recieveData(QString str);

Там же подключите класс 
QStringList, он нам потребуется. Этот класс предназначен для работы со списком строк.

#include <QStringList>

В form.cpp определяем логику его работы.

void Form::recieveData(QString str)
{
    QStringList lst = str.split(" "); // Объявляем список строк lst. Разбиваем полученную строку на несколько строк. Разделитель строк - пробел
    if (lst.size() > 0) ui->lineEdit->setText(lst.at(0)); // Если в списке 1 и более строк, то 0-ую записываем в первое поле ввода
    if (lst.size() > 1) ui->lineEdit_2->setText(lst.at(1)); // Если в списке 2 и более строк, то 1-ую записываем в первое поле ввода
    if (lst.size() > 2) ui->lineEdit_3->setText(lst.at(2)); // Если в списке 3 и более строк, то 2-ую записываем в первое поле ввода
}

Осталось подключить к этому слоту сигнал из главной формы. Для этого в конструкторе в mainwindow.cpp :

    connect(this, SIGNAL(sendData(QString)), myform, SLOT(recieveData(QString))); // подключение сигнала к слоту нашей формы

Теперь можете компилить и запускать. После ввода в первой форме какого-нибудь текста через пробел и нажатия кнопки, во второй форме будут эти данные разбиты по полям ввода.
Я думаю вы и сами сможете завершить задачу... Надеюсь вторую часть вы напишите сами.. А если не получится, обращайтесь :)

P. S. Если есть пожелание что ещё расписать, пишите, не стесняйтесь. А то я опять уйду в дебри.


24 комментария:

  1. Здравствуйте Сергей, спасибо за статью. Но есть вопрос всё сделал как вы описали и всё прекрасно работало. Но потом по взмаху волшебной палочки всё перестало работать 10 раз проверил, но безуспешно. Может у вас есть какие нибудь мысли по этому поводу?

    ОтветитьУдалить
  2. У меня не выходит обратная передача данных из формы в главное окно, в конекте пишу
    connect(this,SIGNAL(form(double)), MainWindow ,SLOT(res(double)));
    но выдает ошибку, как мне обратиться к главному окну, в слот определенный в главном окне передать сигнал из формы?

    ОтветитьУдалить
    Ответы
    1. Понимаю, что вы уже скорей всего решили эту проблему, но для других людей с такой же проблемой отвечу:
      Соединение всех слотов и сигналов надо выполнять в главном окне, так как именно оно знает всё о формах, которые в нём используются. Форма может вообще ничего не знать о других окнах. Поэтому следует делать сразу пару соединений:
      connect(this, SIGNAL(sendData(QString)), myform, SLOT(recieveData(QString))); // посылаются данные в форму
      connect(myform, SIGNAL(sendData(QString)), this, SLOT(recieveData(QString))); //получаются данные из формы. Вместо this может стоять указатель на другую форму, которой передаём строку.
      Возможен такой вариант:
      connect(myform, SIGNAL(sendData(QString)), labelFromMainWindow, SLOT(setText(QString))); //полученные из формы данные отобразятся в виджете labelFromMainWindow

      Удалить
    2. Спасибо. Вот ты реально выручил.

      Удалить
  3. Этот комментарий был удален автором.

    ОтветитьУдалить
  4. Не совсем понял суть вопроса...
    Зачем setCentralWidget? почему нельзя для отображения просто использовать метод show() ?

    ОтветитьУдалить
  5. Не компилится
    C:/2Form/mainwindow.h:28: error: extra qualification 'MainWindow::' on member 'onButtonSend'

    ОтветитьУдалить
    Ответы
    1. Значит где-то сделали ошибку. По данной информации я могу лишь предположить что вы описали метод внутри классов при этом используя ::

      Удалить
  6. Четко! Сергей, пожалуйста продолжайте выпускать статьи, у Вас хорошо получается!)

    ОтветитьУдалить
  7. QObject::connect: No such slot Form::onButtonSend() in ..\untitled\mainwindow.cpp:14
    QObject::connect: No such slot Form::recData(Qstring) in ..\untitled\mainwindow.cpp:15
    Подскажите пожалуйста, у меня все компилится, но не передает в дополнительную форму, что не так?

    ОтветитьУдалить
    Ответы
    1. Вам же чётко сообщают - нет таких слотов.
      Методы Form::onButtonSend() и Form::recData(Qstring) в описании класса Form должны располагаться в секции public slots, а у вас они, скорей всего, описаны как public

      Удалить
  8. Есть ещё одна маленькая фишка, о которой новички узнают не сразу, ибо она не очевидна, хотя и описана в документации.
    Фома может узнать о том, кто передал ей данные (обычно это именно тот, кому данные надо будет вернуть) непосредственно из слота Form::recieveData(QString):

    voidForm::recieveData(QString)
    {
    QObject *obj = QObject::sender();
    // в obj будет лежать указатель на того, кто вызвал этот слот
    // Делай с ним, что считаешь нужным. Но это усиливает связь между объектами
    }

    ОтветитьУдалить
  9. Ну и в завершение.
    Советую всем обратить внимание, что в Qt в качестве параметров функций используется "const QString &", а не "QString". Винда не обращает на это внимание, а вот Линукс засыплет вас ворнингами, среди которых искать сообщения об ошибках становится проблематично.

    ОтветитьУдалить
  10. У меня вопрос, как сделать консоль, что бы после ввода данных, в окошко ввода, их можно было посчитать, там сделать определенные расчеты и в окошко вывода получить ответ, при этом ответ получаем при нажатии на кнопку.

    ОтветитьУдалить
    Ответы
    1. В Pro-файле прописать CONFIG += console. Сам случайно где-то высмотрел. До этого сколько не пытался вывести qDebug() << - ом информацию в консоль, ничего не получалось. Главное, что в примерах нигде этого не видел (кроме этого одного раза) :)

      Удалить
  11. Здравствуйте!!Пожалуйста помогите в вопросе.как можно передать значения ячеек строки (массив) tablewidgetа в другую форму при клике по ячейке из этой строки.Пробовал по клику кнопки-получилось.По клику ячейки -нет..

    ОтветитьУдалить
  12. Я так и не понял, как из другой формы передать данные в первую форму? Из первой во вторую я и без вас могу. И без слотов все это делается.
    Я из второй в первую как делать незнаю. Ибо в первой форме:
    #include "form2.h"
    А во второй форме такой херни нету, и она не знает о существовании второй формы.

    ОтветитьУдалить
  13. Ошибка...(((
    QObject::connect: No such slot Dialog_io::recieveData(QString) in ../testSql/mainwindow.cpp:124
    QObject::connect: (sender name: 'MainWindow')
    QObject::connect: (receiver name: 'Dialog_io')


    Метод в Dialog_io.h
    public slots:
    void reciveData(QString str);

    Как победить? Спасибо.

    ОтветитьУдалить
  14. Подскажите что делать, если я хочу передать класс, а не стандартные переменные?

    ОтветитьУдалить
  15. Не понимаю, в чем проблема, либо выдает ошибку Cannot connect MainWindow::sendData(bool) to (nullptr)::recieveData(bool), либо вообще крашит программу с текстом "программа экстренно завершила работу, т.к. получила сигнал от системы sigmetation fail"

    Что я делаю не так? Мне просто нужно из второй формы (модальной) получить значение булевой переменной. В гл. форме обЪявляю переменную, передаю ее значение во вторую, а оттуда обратно. Еще пробовал просто передать из второй формы значение переменной и уже в главной изменить значения (все connect в главной форме как и писали)

    ОтветитьУдалить
  16. Интересно, не ругается, но и не работает...

    ОтветитьУдалить