Qt Designer를 사용하지 않고 Source 상에서 QTabWidget, QTableView 등을 동적으로 생성하여 Tab별 데이터를 처리하는 방법을 다뤄보겠습니다. 데이터를 표시할 때 List나 Tree를 사용할 수 있지만 Table을 많이 이용합니다.

Table을 사용하려면 일단 QTableView를 생성하여야 하는데, 단순히 하나의 Table 뿐만 아니라 여러 개의 Tab에 각각 Table을 생성하거나,

기존에 존재하던 Tab에 새로운 Tab을 추가하여 그 안에 Table을 붙이고 싶을 경우도 있습니다.

상황에 따라 유동적으로 Tab과 Table을 제어할 수 있도록 연습하는 예제를 준비했습니다.

아래와 같은 구성을 가진 Tab을 만들어 보겠습니다.


우선 위의 모양을 가진 GUI를 생성하기 위해 void MainWindow::InitGUI()라는 함수를 만들어 구현해보겠습니다.

Parent가 되는 Main Tab을 생성하기 위해 부모가 되는 QTabWidget을 생성합니다.
setGeometry()는 GUI의 x,y좌표 및 사이즈를 조정하는 함수입니다.

    // Create Parent QTabWidget
    ptrParentTabWidget = new QTabWidget(this);
    ptrParentTabWidget->setGeometry(20, 50, 815, 686);


Parent의 Main Tab이 될 QTabWidget을 만들었으면 Tab에 parent 1~10번 까지의 page를 추가해야 합니다.
addTab() 함수를 호출할 경우  QWidget형이 필요하므로 1~10번까지의 page에 각각 Widget을 동적 생성하여 붙여줍니다.

    // Create Parent Widget & Add Widget to Parent QTabWidget
    for(int p_cnt = 0; p_cnt < 10 ; p_cnt++)
    {
        QString parentPage;
        parentPage = parentPage.sprintf("Parent %02d", p_cnt+1);

        prtParentWidget[p_cnt] = new QWidget(ptrParentTabWidget);
        ptrParentTabWidget->addTab(prtParentWidget[p_cnt], parentPage);
    }

 

여기까지 진행하면 parent 1~10번 까지의 Tab을 가진 QTabWidget이 만들어지게 됩니다.
이제 만들어진 1~5번 창에는 새로운 Tab창을 6~10번 창에는 단순히 TableView를 생성해보도록 하겠습니다.
parent 1~5번 창에 새로운 Tab을 붙이기위해 5개의 새로운 Tab을 만들어야 합니다. 

위에서 만들어 준 page창, 즉 Widget에 새로운 Tab을 삽입합니다.


이후 새롭게 만든 Tab에도 child 1~10번 까지의 page를 추가해야 합니다.

5개의 parent page에 각각 10개 child page를 가지는 Tab을 생성하였으므로 (parent: 5개 * child: 10개),

총 50개의 TableView를 만들어 줍시다. 

   for(int c_cnt = 0; c_cnt < 5; c_cnt++)
   {
        // Creart Child QTabWidget to 1~5 Parent QTabWidget
        ptrChildTabWidget[c_cnt] = new QTabWidget(prtParentWidget[c_cnt]);
        ptrChildTabWidget[c_cnt]->setGeometry(0, 0, 811, 651);

        for(int s_cnt = 0; s_cnt < 10; s_cnt++)
        {
            // Create Child Widget & Add Widget to Child QTabWidget
            QString childPage;
            childPage = childPage.sprintf("Child %02d", s_cnt+1);

            prtChildWidget[c_cnt][s_cnt] = new QWidget(ptrChildTabWidget[c_cnt]);
            ptrChildTabWidget[c_cnt]->addTab(prtChildWidget[c_cnt][s_cnt], childPage);

            // Creart Child QTableView to Child Widget
            prtChildTableView[c_cnt][s_cnt] = new QTableView(prtChildWidget[c_cnt][s_cnt]);
            prtChildTableView[c_cnt][s_cnt]-> setGeometry(0, 0, 807, 616);
        }
    }


parent 6~10번 창에는 새로운 Tab 생성없이 단순히 TableView만 붙이겠습니다.


    // Create Parent QTableView to 6~10 Parent QTabWidget
    for(int c_cnt = 0; c_cnt < 5; c_cnt++)
    {
        prtParentTableView[c_cnt] = new QTableView(prtParentWidget[c_cnt+5]);
        prtParentTableView[c_cnt]->setGeometry(0, 0, 811, 651);
    }


이제 데이터를 담을 Table들은 모두 완성하였습니다. 

완성된 QTableView에서 각각의 Cell마다 Data를 관리하기 위해서는 QStandardItemModel이라는 클래스를 사용해야 합니다.


우선 parent tab 1~5번의 각각 child tab 1~10번에 대한 모델 생성 부분을 설명하겠습니다.
ptrChildModel[c_cnt][s_cnt] = new QStandardItemModel(0, 10, prtChildTableView[c_cnt][s_cnt])과 같이 선언을 하면

만들어진 QTableView의 가로축을 10등분하여 한줄 당 10개의 Data Header를 생성하겠다는 의미입니다.


이후의 선언한 명령어들은 헤더에 들어갈 String에 대한 명시 및 헤더의 Size를 정의해주는 부분입니다.

    // Create 1~5 Parent VerticalHeader Item 1~10 to Child QTableView
    for(int c_cnt = 0; c_cnt < 5; c_cnt++)
    {
        for(int s_cnt = 0; s_cnt < 10; s_cnt++)
        {
            ptrChildModel[c_cnt][s_cnt] = new QStandardItemModel(0, 10, prtChildTableView[c_cnt][s_cnt]);
            prtChildTableView[c_cnt][s_cnt]->setModel(ptrChildModel[c_cnt][s_cnt]);
            prtChildTableView[c_cnt][s_cnt]->setEditTriggers(QAbstractItemView::NoEditTriggers);

            for(int item_col = 0; item_col < 10; item_col++)
            {
                QString itemNum;
                itemNum = itemNum.sprintf("Item %02d", item_col+1);
                prtChildTableView[c_cnt][s_cnt]->model()->setHeaderData(item_col, Qt::Horizontal, itemNum);
            }

            prtChildTableView[c_cnt][s_cnt]->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
            prtChildTableView[c_cnt][s_cnt]->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
        }
    }


이제 QStandardItemModel과 VerticalHeader를 생성하였으며 직접 QTableView에 새로운 데이터를 추가하거나

기존의 데이터를 삭제할 수 있습니다.

현재는 생성한 직후의 상태로 아무 데이터가 존재하지 않기 때문에 삽입하는 과정부터 설명하겠습니다.
 
parent tab 1~5번부터 순차적으로 child tab 1~10번까지 각각 inserRow()로 행을 추가한 후 추가한 행의 각 10개의 열에

setData()로 데이터를 넣는 과정입니다. 

void MainWindow::ChildAddData()
{
    QModelIndex idx;
    // Parent Page 1~5
    for(int c_cnt = 0; c_cnt < 5; c_cnt++)
    {
        // Chile Tab Page 1~10
        for(int s_cnt = 0; s_cnt < 10; s_cnt++)
        {
            ptrChildModel[c_cnt][s_cnt]->insertRow(ptrChildModel[c_cnt][s_cnt]->rowCount());
            int row_cnt = prtChildTableView[c_cnt][s_cnt]->model()->rowCount();

            // Add Data One Line to Child QTableView
            for(int item_col = 0; item_col < 10; item_col++)
            {
                QString testData;
                testData = testData.sprintf("Data %02d", item_col+1);
                idx = ptrChildModel[c_cnt][s_cnt]->index(row_cnt-1, item_col, QModelIndex());
                ptrChildModel[c_cnt][s_cnt]->setData(idx, testData, Qt::AlignRight);
            }
        }
    }
}


데이터를 삽입하여 보았으니 이제 존재하는 데이터를 지워보도록 하겠습니다.

삭제의 메커니즘은 QTableView의 존재하는 행을 QModelIndexList에 처음 행부터 마지막 행까지 비교하여 아무런 행이

존재하지 않을 때까지 지우는 것입니다.

void MainWindow::ChildDeleteData()
{
    // Delete All Data from Child QTableView
    QModelIndexList index_cnt;
    for(int c_cnt = 0; c_cnt < 5; c_cnt++)
    {
        for(int s_cnt = 0; s_cnt < 10; s_cnt++)
        {
            // Add Delete Line to QModelIndexList
            for(int item_col = 0; item_col < prtChildTableView[c_cnt][s_cnt]->model()->rowCount(); item_col++)
            {
                prtChildTableView[c_cnt][s_cnt]->selectRow(item_col);
                index_cnt += prtChildTableView[c_cnt][s_cnt]->selectionModel()->selectedRows();
            }

            // Delete Line Added Index
            while (!index_cnt.isEmpty())
            {
                prtChildTableView[c_cnt][s_cnt]->model()->removeRows(index_cnt.last().row(), 1);
                index_cnt.removeLast();
            }
        }
    }
}

 

parant 1~5번까지 생성된 tab의 child tab 1~10번까지의 데이터를 다뤄보았습니다.
parent 1~5번은 6~10번과 달리 child tab을 추가로 생성하였기 때문에 지금까지 진행한 부분까지만 따라오셨다면

나머지 parent 6~10번의 생성 과정은 단순 TableView에 Item을 추가한 작업이므로 훨씬 단조롭습니다.

 

나머지 부분은 따로 설명하지 않고 전체 소스만 첨부하도록 하겠습니다.

데이터가 추가된 GUI의 모습입니다. 데이터를 삭제하는 부분은 전체 소스에서 주석처리 하였습니다.

 

마지막으로 객체 생성 시 Parent를 지정하지 않고 생성하거나,

hide() 혹은 아무런 옵션이 없는 close()만 사용 후 다시 객체를 생성한다면 동적으로 생성했던 객체들을 메모리에서

반드시 해제 시켜주어야 합니다.

 

mainwindow.h와 mainwindow.cpp 파일을 하나의 스크립트에 올리도록 하겠습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
// mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
 
#include <QMainWindow>
#include <QTableView>
#include <QStandardItem>
 
namespace Ui {
class MainWindow;
}
 
class MainWindow : public QMainWindow
{
    Q_OBJECT
 
public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();
    void InitGUI();
    void SetParentDataHeader();
    void ParentAddData();
    void ParentDeleteData();
    void SetChildDataHeader();
    void ChildAddData();
    void ChildDeleteData();
 
private:
    Ui::MainWindow *ui;
 
    QTabWidget *ptrParentTabWidget;
    QWidget *prtParentWidget[10];
    QTableView *prtParentTableView[5];
 
    QTabWidget *ptrChildTabWidget[5];
    QWidget *prtChildWidget[5][10];
    QTableView *prtChildTableView[5][10];
 
    QStandardItemModel *ptrParentModel[5];
    QStandardItemModel *ptrChildModel[5][10];
};
 
#endif // MAINWINDOW_H
 
 
// mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
 
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{    
    ui->setupUi(this);
    InitGUI();
 
    SetParentDataHeader();
    ParentAddData();
    ParentAddData();
    ParentAddData();
    //ParentDeleteData();
 
    SetChildDataHeader();
    ChildAddData();
    ChildAddData();
    //ChildDeleteData();
}
 
MainWindow::~MainWindow()
{
    delete ui;
}
 
void MainWindow::InitGUI()
{
    // Create Parent QTabWidget
    ptrParentTabWidget = new QTabWidget(this);
    ptrParentTabWidget->setGeometry(2050815686);
 
    // Create Parent Widget & Add Widget to Parent QTabWidget
    for(int p_cnt = 0; p_cnt < 10 ; p_cnt++)
    {
        QString parentPage;
        parentPage = parentPage.sprintf("Parent %02d", p_cnt+1);
 
        prtParentWidget[p_cnt] = new QWidget(ptrParentTabWidget);
        ptrParentTabWidget->addTab(prtParentWidget[p_cnt], parentPage);
    }
 
    for(int c_cnt = 0; c_cnt < 5; c_cnt++)
    {
        // Creart Child QTabWidget to 1~5 Parent QTabWidget
        ptrChildTabWidget[c_cnt] = new QTabWidget(prtParentWidget[c_cnt]);
        ptrChildTabWidget[c_cnt]->setGeometry(00811651);
 
        for(int s_cnt = 0; s_cnt < 10; s_cnt++)
        {
            // Create Child Widget & Add Widget to Child QTabWidget
            QString childPage;
            childPage = childPage.sprintf("Child %02d", s_cnt+1);
 
            prtChildWidget[c_cnt][s_cnt] = new QWidget(ptrChildTabWidget[c_cnt]);
            ptrChildTabWidget[c_cnt]->addTab(prtChildWidget[c_cnt][s_cnt], childPage);
 
            // Creart Child QTableView to Child Widget
            prtChildTableView[c_cnt][s_cnt] = new QTableView(prtChildWidget[c_cnt][s_cnt]);
            prtChildTableView[c_cnt][s_cnt]-> setGeometry(00807616);
        }
    }
 
    // Create Parent QTableView to 6~10 Parent QTabWidget
    for(int c_cnt = 0; c_cnt < 5; c_cnt++)
    {
        prtParentTableView[c_cnt] = new QTableView(prtParentWidget[c_cnt+5]);
        prtParentTableView[c_cnt]->setGeometry(00811651);
    }
}
 
void MainWindow::SetParentDataHeader()
{
    // Create 6~10 Parent VerticalHeader Item 1~10 to Parent QTableView
    for(int p_cnt = 0; p_cnt < 5; p_cnt++)
    {
        ptrParentModel[p_cnt] = new QStandardItemModel(010, prtParentTableView[p_cnt]);
        prtParentTableView[p_cnt]->setModel(ptrParentModel[p_cnt]);
        prtParentTableView[p_cnt]->setEditTriggers(QAbstractItemView::NoEditTriggers);
 
        for(int item_col = 0; item_col < 10; item_col++)
        {
            QString itemNum;
            itemNum = itemNum.sprintf("Item %02d", item_col+1);
            prtParentTableView[p_cnt]->model()->setHeaderData(item_col, Qt::Horizontal, itemNum);
        }
        prtParentTableView[p_cnt]->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
        prtParentTableView[p_cnt]->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
    }
}
 
void MainWindow::ParentAddData()
{
    QModelIndex idx;
    // Parent Page 6~10
    for(int p_cnt = 0; p_cnt < 5; p_cnt++)
    {
        ptrParentModel[p_cnt]->insertRow(ptrParentModel[p_cnt]->rowCount());
        int row_cnt = prtParentTableView[p_cnt]->model()->rowCount();
 
        // Add Data One Line to Parent QTableView
        for(int item_col = 0; item_col < 10; item_col++)
        {
            QString testData;
            testData = testData.sprintf("Data %02d", item_col+1);
            idx = ptrParentModel[p_cnt]->index(row_cnt-1, item_col, QModelIndex());
            ptrParentModel[p_cnt]->setData(idx, testData, Qt::AlignRight);
        }
    }
}
 
void MainWindow::ParentDeleteData()
{
    // Delete All Data from Parent QTableView
    QModelIndexList index_cnt;
    for(int p_cnt = 0; p_cnt < 5; p_cnt++)
    {
        // Add Delete Line to QModelIndexList
        for(int item_col = 0; item_col < prtParentTableView[p_cnt]->model()->rowCount(); item_col++)
        {
            prtParentTableView[p_cnt]->selectRow(item_col);
            index_cnt += prtParentTableView[p_cnt]->selectionModel()->selectedRows();
        }
 
        // Delete Line Added Index
        while (!index_cnt.isEmpty())
        {
            prtParentTableView[p_cnt]->model()->removeRows(index_cnt.last().row(), 1);
            index_cnt.removeLast();
        }
    }
}
 
void MainWindow::SetChildDataHeader()
{
    // Create 1~5 Parent VerticalHeader Item 1~10 to Child QTableView
    for(int c_cnt = 0; c_cnt < 5; c_cnt++)
    {
        for(int s_cnt = 0; s_cnt < 10; s_cnt++)
        {
            ptrChildModel[c_cnt][s_cnt] = new QStandardItemModel(010, prtChildTableView[c_cnt][s_cnt]);
            prtChildTableView[c_cnt][s_cnt]->setModel(ptrChildModel[c_cnt][s_cnt]);
            prtChildTableView[c_cnt][s_cnt]->setEditTriggers(QAbstractItemView::NoEditTriggers);
 
            for(int item_col = 0; item_col < 10; item_col++)
            {
                QString itemNum;
                itemNum = itemNum.sprintf("Item %02d", item_col+1);
                prtChildTableView[c_cnt][s_cnt]->model()->setHeaderData(item_col, Qt::Horizontal, itemNum);
            }
 
            prtChildTableView[c_cnt][s_cnt]->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
            prtChildTableView[c_cnt][s_cnt]->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
        }
    }
}
 
void MainWindow::ChildAddData()
{
    QModelIndex idx;
    // Parent Page 1~5
    for(int c_cnt = 0; c_cnt < 5; c_cnt++)
    {
        // Chile Tab Page 1~10
        for(int s_cnt = 0; s_cnt < 10; s_cnt++)
        {
            ptrChildModel[c_cnt][s_cnt]->insertRow(ptrChildModel[c_cnt][s_cnt]->rowCount());
            int row_cnt = prtChildTableView[c_cnt][s_cnt]->model()->rowCount();
 
            // Add Data One Line to Child QTableView
            for(int item_col = 0; item_col < 10; item_col++)
            {
                QString testData;
                testData = testData.sprintf("Data %02d", item_col+1);
                idx = ptrChildModel[c_cnt][s_cnt]->index(row_cnt-1, item_col, QModelIndex());
                ptrChildModel[c_cnt][s_cnt]->setData(idx, testData, Qt::AlignRight);
            }
        }
    }
}
 
void MainWindow::ChildDeleteData()
{
    // Delete All Data from Child QTableView
    QModelIndexList index_cnt;
    for(int c_cnt = 0; c_cnt < 5; c_cnt++)
    {
        for(int s_cnt = 0; s_cnt < 10; s_cnt++)
        {
            // Add Delete Line to QModelIndexList
            for(int item_col = 0; item_col < prtChildTableView[c_cnt][s_cnt]->model()->rowCount(); item_col++)
            {
                prtChildTableView[c_cnt][s_cnt]->selectRow(item_col);
                index_cnt += prtChildTableView[c_cnt][s_cnt]->selectionModel()->selectedRows();
            }
 
            // Delete Line Added Index
            while (!index_cnt.isEmpty())
            {
                prtChildTableView[c_cnt][s_cnt]->model()->removeRows(index_cnt.last().row(), 1);
                index_cnt.removeLast();
            }
        }
    }
}