Идеология Qt. Как сделать конкретное приложение?

Elina74

Решил я создать такое приложение:
На окне две вкладки QTabWidget. На одной из них находятся некоторые контролы, например текстовое поле и кнопка. На другой находится другое текстовое поле. Я хочу "передать" информацию с первой вкладки на вторую по нажатию кнопки. Просто чтобы она туда скопировалась. Как идеологически правильно это делать? Что там? Сигналы? Функции?
Я пытался написать это, используя сигналы, но оказывается, каждый таб это экземпляр отдельного класса, значит сигналы должны идти между объектами разных классов.
Желательно было бы увидеть в конце концов не только схему, но и саму рабочую программу.

Olenenok

не только схему, но и саму рабочую программу.
сильно

spitfire

Чтение туториалов и примеров не спасло отца русской демократии?

Elina74

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

yolki

кстати, есть какой-нибудь "дизайнер форм", типа как в дельфях? интересует для кутэ и гтк

Elina74

Есть, ставится вместе с Qt, называется Designer, но он не такой как в дельфи, там только расставляешь кнопки по формам, писать код надо отдельно.

evgen5555

каждый экземпляр таба это свой отдельный класс,
дельфинизмы в реальной жизни =)

Elina74

каждый таб это экземпляр отдельного класса - так точнее?

Olenenok

Для гтк есть кроссплатформенный Glade. Для линукс, по крайней мере, есть ещё Gazpacho. Как у него насчёт кроссплатформенности не знаю.
Для Qt есть Qt designer, есть более-менее удобный плагин для Эклипса.

spitfire

Есть вариант сделать свой класс, который будет своего рода "транспортным агентом" для сигнала между двумя табами. То есть связь типа сигнал первого таба -> слот этого класса -> сигнал этого класса -> слот другого таба.

yolki

глюк оговорился, тем более что в дельфях это не так.

Helga87

На окне две вкладки QTabWidget. На одной из них находятся некоторые контролы, например текстовое поле и кнопка. На другой находится другое текстовое поле. Я хочу "передать" информацию с первой вкладки на вторую по нажатию кнопки. Просто чтобы она туда скопировалась. Как идеологически правильно это делать?
Как раз для таких случаев и придумана концепция Model-View-Controller. Идея такова: у нас есть объект, который хранит данные (например, textData:String). Каждый таб, когда в нем меняется текст, изменяет поле textData, а при каждом изменении textData (или, как вариант, при каждом переключении или отрисовке таба значение текстового поля в табе берется из textData.
Таким образом ты абстрагируешься от способа отображения информации и даешь себе возможность поддерживать свои табы в консистентном состоянии

Maurog

я бы сказал, что тут подходит паттерн Mediator ака посредник.
пытался описать поведение этого паттерна.
Я бы ему еще дал (в контексте данной задачи) названия Synchronizer, Notifier.

Phoenix

вот исходники:
src/main.cpp

#include <stdio.h>
#include <stdlib.h>
#include <QtGui>
#include <QObject>
#include <QApplication>

#include "mainwnd.h"

int main ( int argc, char ** argv )
{
QApplication app ( argc,argv );
QTextCodec::setCodecForCStrings ( QTextCodec::codecForName ( "UTF8" ) );
MyWidget * widget;


widget = new MyWidget;
widget->show ;

printf("(1)\n");
//QObject::connect ( widget->lineEdit_2,SIGNAL ( returnPressed widget,SLOT ( OnChange ) );
printf("(2)\n");

return app.exec ;

}


src/mainwnd.cpp
 
//fdaf
#include "mainwnd.h"
#include <stdio.h>


MyWidget::MyWidget {
int i;

ui.setupUi(this);

//QObject::connect ( lineEdit_2,SIGNAL ( returnPressed this,SLOT ( OnChange ) );
QPushButton * pButton = findChild<QPushButton*>("pushButton");

if(pButton) {
connect (pButton, SIGNAL(clicked this, SLOT(clicked_slot;
}

return;
}

MyWidget::~MyWidget {
return;
}

void MyWidget::clicked_slot {
QLineEdit * pEditfrom = findChild<QLineEdit*>("lineEdit_from");
QLineEdit * pEditto = findChild<QLineEdit*>("lineEdit_to");

if(!pEditfrom || !pEditto)
return;

pEditto->setText(pEditfrom->text;

return;
//printf("test\n");
}


src/mainwnd.h

//fdaf
#ifndef MAINWND_H
#define MAINWND_H

#include <QtGui/QMainWindow>
#include "ui_mainwnd.h"


class MyWidget : public QMainWindow, public Ui::MainWindow {

Q_OBJECT

public:
MyWidget;
~MyWidget;

private:
Ui::MainWindow ui;
public slots:
void clicked_slot;
};

#endif // MAINWND_H


src/mainwnd.ui
 
<ui version="4.0" >
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow" >
<property name="geometry" >
<rect>
<x>0</x>
<y>0</y>
<width>733</width>
<height>553</height>
</rect>
</property>
<property name="windowTitle" >
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget" >
<widget class="QTabWidget" name="tabWidget" >
<property name="geometry" >
<rect>
<x>0</x>
<y>10</y>
<width>721</width>
<height>491</height>
</rect>
</property>
<property name="currentIndex" >
<number>0</number>
</property>
<widget class="QWidget" name="tab" >
<attribute name="title" >
<string>Tab 1</string>
</attribute>
<widget class="QLineEdit" name="lineEdit_from" >
<property name="geometry" >
<rect>
<x>80</x>
<y>100</y>
<width>113</width>
<height>20</height>
</rect>
</property>
</widget>
<widget class="QPushButton" name="pushButton" >
<property name="geometry" >
<rect>
<x>400</x>
<y>210</y>
<width>75</width>
<height>24</height>
</rect>
</property>
<property name="text" >
<string>PushButton</string>
</property>
</widget>
</widget>
<widget class="QWidget" name="tab_2" >
<attribute name="title" >
<string>Tab 2</string>
</attribute>
<widget class="QLineEdit" name="lineEdit_to" >
<property name="geometry" >
<rect>
<x>90</x>
<y>100</y>
<width>113</width>
<height>20</height>
</rect>
</property>
</widget>
</widget>
</widget>
</widget>
<widget class="QMenuBar" name="menubar" >
<property name="geometry" >
<rect>
<x>0</x>
<y>0</y>
<width>733</width>
<height>26</height>
</rect>
</property>
</widget>
<widget class="QStatusBar" name="statusbar" />
</widget>
<resources/>
<connections/>
</ui>


CMakeLists.txt
 
PROJECT(tab2)


IF (NOT CMAKE_BUILD_TYPE)
SET(CMAKE_BUILD_TYPE Debug) #build with debug support
ENDIF (NOT CMAKE_BUILD_TYPE)




SET(QT_USE_QTMAIN True)
FIND_PACKAGE(Qt4 REQUIRED) # find and setup Qt4 for this project
INCLUDE(${QT_USE_FILE})

INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR})
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})


SET(main_SRCS
src/main.cpp
src/mainwnd.cpp
)

SET(main_HDRS
src/mainwnd.h
)

SET(main_UIS
src/mainwnd.ui
)

SET(main_MOC_HDRS
src/mainwnd.h
)




SET(tab2_SRCS
${main_SRCS}
)

SET(tab2_MOC_HDRS
${main_MOC_HDRS}
)

SET(tab2_HDRS
${main_HDRS}
)

SET(tab2_UIS
${main_UIS}
)
MESSAGE(STATUS "!")

MESSAGE(STATUS "SRC: ${tab2_SRCS}")
MESSAGE(STATUS "MOC_HDRS: ${tab2_MOC_HDRS}")
MESSAGE(STATUS "HDRS: ${tab2_HDRS}")
MESSAGE(STATUS "UIS: ${tab2_UIS}")
#SET(aa1_RES
#)

#QT4_ADD_RESOURCES(aa1_RES_GEN ${a1a1_RES})

QT4_WRAP_UI(tab2_UIS_H ${tab2_UIS})
QT4_WRAP_CPP(tab2_MOC_SRCS ${tab2_MOC_HDRS})


MESSAGE(STATUS "SRC: ${tab2_SRCS}")
MESSAGE(STATUS "MOC_HDRS: ${tab2_MOC_HDRS}")
MESSAGE(STATUS "HDRS: ${tab2_HDRS}")
MESSAGE(STATUS "UIS: ${tab2_UIS}")
MESSAGE(STATUS "UIS_H: ${tab2_UIS_H}")
MESSAGE(STATUS "MOC_SRCS: ${tab2_MOC_SRCS}")


IF (WIN32)

ADD_EXECUTABLE(tab2 WIN32
${tab2_UIS_H}
${tab2_MOC_SRCS}
${tab2_SRCS}
${tab2_HDRS}
)

TARGET_LINK_LIBRARIES(tab2 ${QT_LIBRARIES} ${tab2_LIBS})

ELSE (WIN32)
ADD_EXECUTABLE(tab2
${tab2_UIS_H}
${tab2_MOC_SRCS}
${tab2_SRCS}
${tab2_HDRS}
)
TARGET_LINK_LIBRARIES(tab2 ${QT_LIBRARIES} ${tab2_LIBS})
ENDIF (WIN32)

собcтвенно, всё
для компиляции:
# mkdir build
#cd build
#cmake ..
#make
#./tab2
ps: если есть способ сделать тоже самое проще, то с удовольствием выслушаю, потому как не всё в выше изложенном мне видится правильным

Werdna

CMakeLists.txt
респектище только за это уже :)
#cd build
#cmake ..

а вот это беру на вооружение.

Missi4ka

значит сигналы должны идти между объектами разных классов
Можешь объяснить для тех, кто сейчас в танке, но когда-то был знаком с QT, в чем проблема передавать сигнал между экземплярами разных классов?

Elina74

круто, оно работает... щас буду разбираться, задам еще несколько вопросов по конкретной реализации

Elina74

Можешь объяснить для тех, кто сейчас в танке, но когда-то был знаком с QT, в чем проблема передавать сигнал между экземплярами разных классов?
В мануале и примерах такой ситуации не нашел... ну тупой я.

Missi4ka

Так все-таки, проблема передавать сигнал между разными классами есть или нет?

Elina74

mainwind.cpp в объявлении конструктора класса MyWidget:
QPushButton * pButton = findChild<QPushButton*>("pushButton");

if(pButton) {
connect (pButton, SIGNAL(clicked this, SLOT(clicked_slot;
}
здесь pButton добывается из недр класса Ui_MainWindow, от которого наследуется MainWindow, а clicked_slot - член класса MyWidget, который потомок Ui::MainWindow
проблемы, видимо, нет, но сама процедура замороченная, извините за резкость

Phoenix

класс Ui::MainWindow генерится на основе mainwnd.ui файла.
собственно ui_mainwnd.h - содержит функцию setupUi
можно было всё, что там написано руками написать, но мне проще в designer нарисовать и при этом не заморачиваться с uic mainwnd.ui > ui_mainwnd.h
например, я передвинул кнопочку, пишем make, кнопочка в бинарнике передвинулась.
когда-то давно(когда я ещё не прогал, но кто-то прогал на qt) после каждой перерисовки формочки приходилось запускать uic, либо дописывать в конструктор класса.
в uic4 разделили визуальные объекты, создаваемые designer и то, что пишет человек. т.е. сейчас ui_*.h файлы не содержат пользовательского кода, а генерятся из .ui файлов.
в результате, согдаётся потомок Ui::MainWindow, который уже содержит пользовательские данные, и вызывается функция setupUi, которая создаёт все элементы и коннектит всё что сконнекченно визуально на формочке
QPushButton * pButton = findChild<QPushButton*>("pushButton"); - это я так нашёл кнопочку.
если кто знает, как это сделать проще - пожалуйста, скажите.
я не нашёл.
clicked_slot - это та функция которая должна быть вызвана, когда нажимается кнопочка.
мне намного больше нравится дельфи(бюлдер)-способоб кликнул на кнопочку попал в то место, где писать код, но..

Elina74

mainwind.cpp в объявлении конструктора класса MyWidget:
QPushButton * pButton = findChild<QPushButton*>("pushButton");

if(pButton) {
connect (pButton, SIGNAL(clicked this, SLOT(clicked_slot;
}
здесь pButton добывается из недр класса Ui_MainWindow, от которого наследуется MainWindow, а clicked_slot - член класса MyWidget, который потомок Ui::MainWindow
проблемы, видимо, нет, но сама процедура замороченная, извините за резкость
за функцию findChild огромное спасибо, не подозревал о ее существовании, я только начинаю изучение Qt

FRider

ак сделать конкретное приложение?

Для этого нужно поставить чисто конкретную IDE на реальный комп, взять дерзкую мышь и клаву и в путь

Phoenix

нормальное ide для qt нетy.
под нормальной понимается ,например msvs c виз. редактором, дебагером и прочими радостями.
то, что я запостил выше можно экспортировать в проект и дальше прогать там:
кстати, по поводу cmake
#mkdir build-kdev
#cd build-kdev
#cmake -GKDevelop3 ..
для msvs тоже можно, но я парамент для -G не помню(можно посмотреть cmake -h)

Missi4ka

мне намного больше нравится дельфи(бюлдер)-способоб кликнул на кнопочку попал в то место
Есть как за, так и против идеологии визуального проектирования. Я тоже по началу юзал QTDesigner, но потом сам факт, что какая-то прога прогает за меня, стал настораживать. И действительно, код, написанный руками и читается лучше, и занимает, как правило, меньше строк, причем, раза в полтора.

Missi4ka

под нормальной понимается ,например msvs c виз. редактором, дебагером и прочими радостями
Кстати, под линуксами альтернатива всему этому тулкиту - набор стандартных прог типа gdb, make плюс симпатичный текстовый редактор, распознающий скобки и способный подсвечивать синтаксис (а также "обучаться" ему посредством языковых файликов). Например, редакторы Kate и Anjuta вполне пригодны для кодинга в разумных масштабах. В силу кроссплатформенности QT можно вести разработку, сидя под линуксами.

Phoenix

ну я вот сейчас прогаю одну совтину.
задача:
нужен визуальный редактор регистров одной железки.
решение:
создаётся виджет smartylineedit - это обычный lineedit, но с добавлением некотрых сигналов и слотов.
создаётся виджет controlbox, который взаимодействует с библиотекой.
теперь самое интересное, создаётся плагин к designer, и новые едиты со свойством адреса регистра можно добавлять прям на формочку.
добаление новых полей регистров сводится к перетаскиванию виджета с панельки на форму и вбиванием одного числа.
все коннекты сделаны в классе формочки.
визуальные редакторы - это не самоцель, это лишь средство экономии времени.
самое забавное, что я нормально не разобрался, как оно всё работает, пока руками всё не сделал.

Phoenix

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

Missi4ka

задолбаешь это всё в gdb писать
эт-точно. Правда, не всегда есть возможность отлаживаться в визуальной среде. Приходится подчас собираться на каком-нибудь серваке и запускать прогу на больших данных. Тащить файл в десятки гигов себе на десктоп нет возможности, да и сервак 64-разрядный. Приходится юзать putty + gcc + gdb :(

6yrop

файл в десятки гигов себе на десктоп нет возможности, да и сервак 64-разрядный.
имхо, VS может аттачится к удаленной машине (подробности не знаю, как там 64-разрядами тоже не в курсе)

Werdna

Приходится юзать putty + gcc + gdb
На десктоп ставь Линукс :)

Olenenok

имхо, VS может аттачится к удаленной машине (подробности не знаю, как там 64-разрядами тоже не в курсе)
Лол, на удалённой машине, особенно если она 64 битная, почти наверное не венда стоит ;)

Olenenok

На десктоп ставь Линукс :)
плюсадин, у нас на работе все так и делают: либо линукс, либо макось. Венда в явном меньшинстве.

feliks28

И что же на линуксе предлагается вместо "putty + gcc + gdb" ?

6yrop

Лол, на удалённой машине, особенно если она 64 битная, почти наверное не венда стоит
у нас винда :p

Olenenok

sshfs, gnomevfs и пр. радости жизни. Правда, без gdb никак, всё равно нужно аттачиться к запущенному процессу именно удалёно. И компилить тоже нужно удалённо.

FRider

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

Missi4ka

На десктоп ставь Линукс
Собственно, в процессе принятия решения. Изначально просто винда стояла, а тратить день-другой на переезд все никак не хотелось.

olga1969

Как раз для таких случаев и придумана концепция Model-View-Controller. Идея такова: у нас есть объект, который хранит данные (например, textData:String). Каждый таб, когда в нем меняется текст, изменяет поле textData, а при каждом изменении textData (или, как вариант, при каждом переключении или отрисовке таба значение текстового поля в табе берется из textData.
Таким образом ты абстрагируешься от способа отображения информации и даешь себе возможность поддерживать свои табы в консистентном состоянии
Ну, а если это начать программировать (я имею ввиду в общем случае, может в Qt это не совсем так, я с ним не работал то возникнет куча деталей, которые могут отбить всякую охоту использовать MVC.
Например, изменение текста контолируется нажатием кнопки, то есть соотв. методом (типа onClick (С# кажется или еще в др. яз.) или actionPerformed в Яве). Значит, это обработчик должен "видеть" объект tab'a, и эту связь нужно организвать, что в итоге (если делать все напрямик) приводит к тому, что нужно наследовать от стандартного класса "кнопка" и добавлять туда ссылку на tab. Tab в свою очередь должен иметь ссылку на текстовое поле, чтобы вытащить оттуда текст. Кроме того, каждый tab должен иметь ссылку на объект, который хранит данные и textData:String в том числе, а это опять же - наследование от стандартного виджета. Кроме того, это жесткая связь с типом данных: если этот тип поменять, то придется менять все виджеты, которые используют этот тип данных. (Кстати, возится с textData:String могла бы и кнопка...)
В общем, основная проблема в том, как делать обработчики событий и как передать инфо из них другим частям приложения, которые зависят от данного события. В любом случае обработчик должен нужно связывать с другими объектами, то есть расширять стандартную функциональность с помощью наследования. Для пары табов это еще ничего, а когда еще куча меню и прочих контролов и еще диалоги, можно запариться создавать отдельный класс для каждого виджета и еще и инициализировать и отслеживать все связи между ними.
В Яве можно выкручиваться с помощью анонимных классов в качестве обработчиков. Если все виджеты запихать в один класс, то тогда анонимные классы будут иметь к ним простой доступ как к полям класса. Но... если опять же куча виджетов, получится класс-монстр, и бегать потом по нему в поисках нужного обработчика или виджета тоже не особо приятно.
Ну, и еще конечно нужен механизм оповещения об изменениях. Пример со вторым tab'ом будет работать, поскольку tab будет автоматически перерисовываться, когда мы кликнем на нем. Но в общем случае все виджеты, которые должы знать об изменении текста, должны быть оповещены. Это делается с помощью паттерна Observer, который должен быть реализован в объекте, которыей управляет всеми данными, потому что изменения происходят там. Но с Observer'ом свои заморочки...

evgen5555

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

olga1969

Запариваться надо скорее над гуём, чтобы не делать лишних меню, контролов и диалогов.

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

bleyman

сам факт, что какая-то прога прогает за меня, стал настораживать.
Странно! Многие люди только тем и занимаются, что пишут проги, которые за них будут писать другие проги, а тебя настораживает это...

Missi4ka

Мне было просто не совсем очевидно, что генеримое автоматически - это в точности что надо.
Оставить комментарий
Имя или ник:
Комментарий: