Forum: PC-Programmierung C++ und Qt: wie Daten an GUI übergeben?


von Nesbit (Gast)


Lesenswert?

Hi,

ich bin dabei ein kleines Programm in c++ mit Qt zu programmieren. Im 
Wesentlichen soll es ein GUI-Aufsatz für ein Konsolenprogramm sein. 
Mittels QProcess (welches intern Pipse benutzt) wird das externe 
Konsolenprogramm gestartet. Danach kann mit write() eine Eingabe 
getätigt werden. Um die Ausgabe des Programms abzufangen, habe ich den 
Event "onReadyRead()" in einem entsprechenden Eventhandler abgefangen. 
Das funktioniert auch: jedes mal, wenn das externe Konsolenprogramm 
einen Wert ausgibt, wird onReadyRead() aufgerufen und ich kann meine 
Daten zusammen sammeln.

Beispiel:
1
void ConsoleInterface::onReadyRead()
2
{
3
    QString line = proc->readLine();
4
    QMap<QString, QString> mymap;
5
6
    QRegularExpression re("\\\{(title=\\\"(.*?)\\\",fullpath=\\\"(.*?)\\\")\\\}");
7
    QRegularExpressionMatchIterator it = re.globalMatch(result);
8
    while(it.hasNext())
9
    {
10
        QRegularExpressionMatch match = it.next();
11
12
        QString title = match.captured(2);
13
        QString fullpath = match.captured(3);
14
15
        mymap.insert(title, fullpath);
16
    }
17
    emit console_done(mymap);
18
}
Wenn also mein externes Programm fertig ist, würde auf der Konsole in 
stdout eine Liste von Files ausgegeben. Dies fange ich mit oben 
gezeigtem Code ab und zerlege die Liste mit einem regulären Ausdruck. 
Danach werden die einzelnen Teile in eine Map gefüllt. Wenn alles fertig 
ist, dann wird der Event console_done() aufgerufen.

Denn ich möchte das GUI und die "Businesslogik" streng trennen. In 
meiner GUI-Klasse würde ich dann mittels connect() einen Eventhandler 
implementieren, welcher dann von emit aufgerufen wird. Und in diesem 
Eventhandler möchte ich die Daten dann für die Präsentation im ListView 
aufbereiten.

Bloss, wie wäre es klug, die Daten zu übergeben? soll der Handler die 
Map als Referenz übernehmen, oder als Pointer (und die Map dann auf dem 
Heap alloziieren mit new?). Ich möchte schlussendlich meine Daten mit 
einem Model/View nsatz darstellen.

Könnt ihr mir da Ratschläge geben?

von Sven B. (scummos)


Lesenswert?

Ich würde es by-value machen -- spart dir das Memory-Management. Kopiert 
wird eh nichts, dafür sorgt QSharedData.

von Nesbit (Gast)


Lesenswert?

O.K. und wie würdest du die Daten aufbewahren?

mit einem Struct
1
typedef struct
2
{
3
QString title;
4
QString fullpath;
5
} record;

oder was wäre besser? Die Map ist von mir aus nicht zwingend nötig. Es 
gibt später noch andere Datensätze, welche auch noch Nummern beinhalten.

von Sven B. (scummos)


Lesenswert?

Kommt drauf an was du damit machen willst, die Map ist halt gut wenn du 
Einträge zu einem bestimmten Wert finden musst, ansonsten ist ein Vektor 
aus structs auch prima.

von Nesbit (Gast)


Lesenswert?

Ah, du würdest mit std::vector arbeiten?
spricht etwas gegen QList?

ich werde die Daten eh durchsuchen müssen, das Problem ist aber dass man 
mal nach dem Titel sucht, mal nach dem Full Path. Dann nützt die Map 
eher wenig.

von Sven B. (scummos)


Lesenswert?

Ich würde QVector nehmen, std::vector ist auch ok, wie du willst -- 
kommt ziemlich auf dasselbe raus. QList ist alt und schlecht und sollte 
überhaupt gar nie wieder benutzt werden.

von Nase (Gast)


Lesenswert?

Sven B. schrieb:
> Ich würde QVector nehmen, std::vector ist auch ok, wie du willst --
> kommt ziemlich auf dasselbe raus.
Nein, std::vector ist etwas völlig anderes. Insbesondere ist der 
Kopie-Konstruktor von std::vector mitunter sehr aufwändig, verglichen 
mit den Qt-eigenen Containern.

std::vector wenn überhaupt, dann als Referenz. Da Referenzen nicht 
thread-übergreifend im Signal/Slot übergeben werden können, eher 
auto_ptr oder sowas. Also letztlich lieber QVector, der macht das 
automatisch.

> QList ist alt und schlecht und sollte
> überhaupt gar nie wieder benutzt werden.
Warum? QList ist nach wie vor die Standardliste.

Nesbit schrieb:
> typedef struct
> {
> QString title;
> QString fullpath;
> } record;
typedef sieht in C++ immer komisch aus. Erstens brauchst du es hier 
nicht und zweitens gibts bei Bedarf oft bessere Alternativen (using).

Verpack das Ding in eine kleine Klasse und gut ist.

von Sven B. (scummos)


Lesenswert?

Nase schrieb:
> Sven B. schrieb:
>> Ich würde QVector nehmen, std::vector ist auch ok, wie du willst --
>> kommt ziemlich auf dasselbe raus.
> Nein, std::vector ist etwas völlig anderes.
std::vector ist quasi genau dasselbe nur ohne das implicit sharing.

> Insbesondere ist der
> Kopie-Konstruktor von std::vector mitunter sehr aufwändig, verglichen
> mit den Qt-eigenen Containern.
Ja.

> std::vector wenn überhaupt, dann als Referenz.
Ja.

> eher auto_ptr oder sowas.
auto_ptr ist deprecated.
1
template <class X> class auto_ptr;
2
3
Automatic Pointer [deprecated]

>> QList ist alt und schlecht und sollte
>> überhaupt gar nie wieder benutzt werden.
> Warum? QList ist nach wie vor die Standardliste.
Nein, QList ist ein riesiger Murks und ich würde mal davon ausgehen dass 
in der Qt-API in 6.0 alle QList durch QVector ersetzt und QList 
vielleicht sogar deprecated wird.
Nimm einfach QVector. Es gibt keine Fall in dem du QList willst.

: Bearbeitet durch User
von Rolf Magnus (Gast)


Lesenswert?

Nase schrieb:
> typedef sieht in C++ immer komisch aus. Erstens brauchst du es hier
> nicht und zweitens gibts bei Bedarf oft bessere Alternativen (using).

Ich frage mich schon länger, warum man jetzt using statt typedef 
verwenden soll. Was ist daran besser, dass es die weniger intuitive 
Syntax rechtfertigt?

von Sven B. (scummos)


Lesenswert?

Rolf Magnus schrieb:
> Nase schrieb:
>> typedef sieht in C++ immer komisch aus. Erstens brauchst du es hier
>> nicht und zweitens gibts bei Bedarf oft bessere Alternativen (using).
>
> Ich frage mich schon länger, warum man jetzt using statt typedef
> verwenden soll. Was ist daran besser, dass es die weniger intuitive
> Syntax rechtfertigt?

Das using ist viel intuitiver, weil ich bei using T = U; sofort weiß 
dass T der neue Typ ist und U der alte und bei typedef A B weiß ich nach 
zehn Jahren immer noch nicht wie rum die Argumentreihenfolge ist :D

von Vincent H. (vinci)


Lesenswert?

Rolf Magnus schrieb:
> Nase schrieb:
>> typedef sieht in C++ immer komisch aus. Erstens brauchst du es hier
>> nicht und zweitens gibts bei Bedarf oft bessere Alternativen (using).
>
> Ich frage mich schon länger, warum man jetzt using statt typedef
> verwenden soll. Was ist daran besser, dass es die weniger intuitive
> Syntax rechtfertigt?

Weil using mit Templates kompatibel ist.

von Nase (Gast)


Lesenswert?

Sven B. schrieb:
>>> QList ist alt und schlecht und sollte
>>> überhaupt gar nie wieder benutzt werden.
>> Warum? QList ist nach wie vor die Standardliste.
> Nein, QList ist ein riesiger Murks und ich würde mal davon ausgehen dass
> in der Qt-API in 6.0 alle QList durch QVector ersetzt und QList
> vielleicht sogar deprecated wird.
Glaube ich nicht, ich denke das ist zu pauschal.

Grundsätzlich halte ich das Konzept, solch eine Brot-und-Butter-Liste 
für alle Fälle zu haben, für garnicht so verkehrt. Effizient 
programmieren bedeutet nämlich auch, effizient zum Ziel zu kommen. 
Natürlich kann man quasi für jeden Anwendungsfall von QList eine bessere 
Alternative finden. Dasselbe gilt aber für QVector/std::vector genauso. 
Manchmal sind es z.B. speichersparendere Alternativen, manchmal 
schnellere.
Für die 99% der Anwendungsfälle, wo das aber keinen Unterschied macht, 
ist die "beste" Alternative m.M.n. aber tatsächlich ersteinmal die 
universellste/intuitivste/naheliegendste/... Und das ist per Konvention 
in Qt halt die QList. Sollte die mal Performanceprobleme machen, was ja 
durchaus denkbar ist, kann man schließlich jederzeit noch optimieren und 
etwas schnelleres/kleineres/... substituieren.

Und das:
> Nimm einfach QVector. Es gibt keine Fall in dem du QList willst.
ist, so finde ich, eben zu pauschal. Du sollst dir bei QList ja 
explizit keine Gedanken darum machen, wie sie implementiert ist. Darum 
gibt es ja auch keine Garantien über ihr Speicherlayout wie bei QVector 
zum Beispiel. Oder eine Charakterisierung wie bei QLinkedList.
Nimm QList, wenn du eine Liste ohne besondere Ansprüche brauchst, 
fertig.

Ich gebe dir aber vollumfänglich Recht darin, dass die aktuelle 
Implementierung von QList nicht besonders gelungen ist. Aber wenn man 
QList nimmt, meint man ja auch keine besondere Implementierung.

QList wird so schnell auch nicht verschwinden. Viel eher würde ich mir 
wünschen, dass sie einfach besser implementiert wird. Man könnte sie 
z.B. problemlos als QVector implementieren. Der Unterschied wäre dann 
semantischer Natur, was ja völlig in Ordnung ist. Vielleicht kommt ja 
irgendwann wieder eine andere Implementierung.

Mir gefällt QList einfach als Universalliste, solange man nichts anderes 
braucht. Also explizit kein Vektor und keine verkettete Liste oder 
sowas. Soll sie sich doch selbst aussuchen, wie sie sich am besten 
implementiert.


Grundsätzlich hat Qt halt noch mehr solcher Relikte. Das ganze 
Signal/Slot-Konzept mit Meta-Object-Compiler datiert noch aus einer 
Zeit, wo Templates nicht so breit unterstützt wurden. Ebenfalls 
historisch unbenutzt sind aktuelle Mechanismen wie expliziter Besitz 
(unique_ptr, ...).

von Sven B. (scummos)


Lesenswert?

Nase schrieb:
> Sven B. schrieb:
>>>> QList ist alt und schlecht und sollte
>>>> überhaupt gar nie wieder benutzt werden.
>>> Warum? QList ist nach wie vor die Standardliste.
>> Nein, QList ist ein riesiger Murks und ich würde mal davon ausgehen dass
>> in der Qt-API in 6.0 alle QList durch QVector ersetzt und QList
>> vielleicht sogar deprecated wird.
> Glaube ich nicht, ich denke das ist zu pauschal.

>>  QList will not be Qt 6. Not in the current form. – Marc Mutz -
>> mmutz Mar 1 '16 at 22:32

Naja, der Mensch will immer alles löschen, und ich finde das auch nicht 
immer besonders gut, aber er hat einen gewissen Einfluss.

> Grundsätzlich halte ich das Konzept, solch eine Brot-und-Butter-Liste
> für alle Fälle zu haben, für garnicht so verkehrt.

Ja, ich ja auch nicht, aber das ist QVector bzw. sollte QVector sein. 
Nicht QList.

> Und das:
>> Nimm einfach QVector. Es gibt keine Fall in dem du QList willst.
> ist, so finde ich, eben zu pauschal. Du sollst dir bei QList ja
> explizit keine Gedanken darum machen, wie sie implementiert ist. Darum
> gibt es ja auch keine Garantien über ihr Speicherlayout wie bei QVector
> zum Beispiel. Oder eine Charakterisierung wie bei QLinkedList.
> Nimm QList, wenn du eine Liste ohne besondere Ansprüche brauchst,
> fertig.

Nimm QVector, wenn du eine Liste ohne besondere Ansprüche brauchst, 
fertig. Ist in 99% der Fälle wo du nicht darüber nachdenken willst 
besser. Warum willst du unbedingt diese wirre QList haben -- nur weil 
sie "list" im Namen hat? Der Vektor legt einfach alle Items 
nebeneinander, das ist doch das einfachste was man sich vorstellen kann 
...

> Grundsätzlich hat Qt halt noch mehr solcher Relikte. Das ganze
> Signal/Slot-Konzept mit Meta-Object-Compiler datiert noch aus einer
> Zeit, wo Templates nicht so breit unterstützt wurden.

Naja, das ist ja in Qt5 deutlich renoviert worden. Der moc macht auch 
andere Sachen, und sparen könnte man den auch mit modernem C++ nicht.

: Bearbeitet durch User
von Rolf M. (rmagnus)


Lesenswert?

Sven B. schrieb:
> Rolf Magnus schrieb:
>> Nase schrieb:
>>> typedef sieht in C++ immer komisch aus. Erstens brauchst du es hier
>>> nicht und zweitens gibts bei Bedarf oft bessere Alternativen (using).
>>
>> Ich frage mich schon länger, warum man jetzt using statt typedef
>> verwenden soll. Was ist daran besser, dass es die weniger intuitive
>> Syntax rechtfertigt?
>
> Das using ist viel intuitiver, weil ich bei using T = U; sofort weiß
> dass T der neue Typ ist und U der alte und bei typedef A B weiß ich nach
> zehn Jahren immer noch nicht wie rum die Argumentreihenfolge ist :D

Typedef ist das einfachste, was man sich da vorstellen kann, weil es 
genau wie eine entsprechende Variablendefinition aussieht, nur eben mit 
typedef davor:
1
int array[5]; // Ein Array aus 5 Integern
2
typedef int array[5]; // Ein neuer Name für den Typ "Array aus 5 Integern"
3
using array = int[5]; // Ein "array" vom Typ using, das mit dem 5. Element von "int" initialisiert wird?

Vincent H. schrieb:
>> Ich frage mich schon länger, warum man jetzt using statt typedef
>> verwenden soll. Was ist daran besser, dass es die weniger intuitive
>> Syntax rechtfertigt?
>
> Weil using mit Templates kompatibel ist.

Und typedef nicht?

von Nesbit (Gast)


Lesenswert?

Hallo

danke für die ausführlichen Antworten. Ich habe das jetzt entsprechend
implementiert und es funktioniert. Nun habe ich noch ein anderes 
Qt-Problem,
ich hoffe ihr könnt mir da auch helfen. Und zwar möchte ich Elemente in 
einem
TreeView darstellen. Dazu habe ich ein eigenes Datenmodel erstellt:
1
struct breakpoint
2
{
3
    QString flie;
4
    long long address;
5
    QString function;
6
    int line;
7
    bool enabled;
8
};
9
10
class DataModel : public QAbstractTableModel
11
{
12
    //Q_OBJECT
13
14
private:
15
    QMap<int, struct breakpoint> breakpointmap;
16
17
public:
18
    DataModel(QObject *parent = 0) : QAbstractTableModel(parent) { }
19
    ~DataModel() { }
20
21
    int rowCount(const QModelIndex &parent) const
22
    {
23
        return this->breakpointmap.keys().count();
24
    }
25
26
    int columnCount(const QModelIndex &parent) const
27
    {
28
        return 6;
29
    }
30
31
    QVariant data(const QModelIndex &index, int role) const
32
    {
33
        if (!index.isValid())
34
                return QVariant();
35
36
            if (role == Qt::TextAlignmentRole)
37
            {
38
                return int(Qt::AlignLeft);
39
            }
40
            else if(role == Qt::DisplayRole)
41
            {
42
                QList<int> keys = breakpointmap.keys();
43
                int i = index.column();
44
                int key = keys.at(index.row());
45
46
47
                ///qDebug() << flags(index);
48
49
                switch(i)
50
                {
51
                case 1:
52
                    return key;
53
54
                case 2:
55
                    return breakpointmap.value(key).flie;
56
57
                case 3:
58
                    return breakpointmap.value(key).address;
59
60
                case 4:
61
                    return breakpointmap.value(key).function;
62
63
                case 5:
64
                    return breakpointmap.value(key).line;
65
66
                }
67
            }
68
            else if((role ==  Qt::CheckStateRole) && (index.column() == 0))
69
            {
70
                QList<int> keys = breakpointmap.keys();
71
                int key = keys.at(index.row());
72
                if(breakpointmap.value(key).enabled)
73
                    return Qt::Checked;
74
                else
75
                    return Qt::Unchecked;
76
            }
77
            return QVariant();
78
    }
79
80
    QVariant headerData(int section, Qt::Orientation /* orientation */, int role) const
81
    {
82
        if (role == Qt::DisplayRole)
83
        {
84
            switch(section)
85
            {
86
                case 1:
87
                return QString("#");
88
89
                case 2:
90
                return QString("file");
91
92
                case 3:
93
                return QString("address");
94
95
                case 4:
96
                return QString("function");
97
98
                case 5:
99
                return QString("line");
100
101
                case 0:
102
                return QString("");
103
104
            }
105
        }
106
        return QVariant();
107
    }
108
109
    bool setData(const QModelIndex &index, const QVariant &value, int role)
110
    {
111
        if((role ==  Qt::CheckStateRole) && (index.column() == 0))
112
        {
113
            QList<int> keys = breakpointmap.keys();
114
            int i = index.column();
115
            int key = keys.at(index.row());
116
            breakpoint bp = breakpointmap.value(key);
117
            bp.enabled = value.toBool();
118
            breakpointmap.insert(key, bp);
119
        }
120
    }
121
122
    void insert(int i, struct breakpoint bp)
123
    {
124
        int cnt = this->breakpointmap.keys().count();
125
        //if(cnt!=0)
126
            beginInsertRows(QModelIndex(), cnt, cnt);
127
 this->breakpointmap.insert(i, bp);
128
       endInsertRows();
129
130
    }
131
132
    Qt::ItemFlags flags(const QModelIndex &index) const
133
    {
134
        Qt::ItemFlags flags = QAbstractItemModel::flags(index);
135
        flags |= Qt::ItemNeverHasChildren;
136
        if(index.isValid())
137
        {
138
        /*if (index.column() == 1)
139
            flags |= Qt::ItemIsEditable;*/
140
        if (index.column() == 0)
141
            flags |= Qt::ItemIsUserCheckable;
142
        }
143
        return flags;
144
    }
145
146
147
};

Für den booleschen Wert von einem breakpoint möchte ich eine Checkbox im
Treeview haben. Da ich gelesen habe, dass man hierzu einen Delegate 
benötigt,
habe ich einen erstellt:
1
class CheckboxDelegate : public QStyledItemDelegate
2
{
3
public:
4
    CheckboxDelegate(QObject *parent = 0) : QStyledItemDelegate(parent)
5
    {
6
7
    }
8
9
    QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem & option , const QModelIndex &index) const
10
    {
11
        QCheckBox *editor = new QCheckBox("test", parent);
12
13
        //editor->setText(QString(index.row()));
14
15
16
        return editor;
17
    }
18
19
    void setEditorData(QWidget *editor, const QModelIndex &index) const
20
    {
21
        bool data = index.model()->data(index, Qt::CheckStateRole).toBool();
22
        QCheckBox *cb = static_cast<QCheckBox*>(editor);
23
        cb->setChecked(data);
24
    }
25
26
    void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
27
    {
28
        QCheckBox *cb = static_cast<QCheckBox*>(editor);
29
        model->setData(index, cb->isChecked(), Qt::CheckStateRole);
30
    }
31
32
    void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &/* index */) const
33
    {
34
        editor->setGeometry(option.rect);
35
    }
36
};

Das funktioniert auch so weit. Mein TreeView wird dargestellt und ich 
kann die
Checkboxen toggeln. Was allerdings nicht geht: ich möchte als Text neben 
jeder
Checkbox die Zeilennummer. Das mache ich hier:

editor->setText(QString(index.row()));

das hat aber nicht funktioniert, weshalb ich mal einen Teststring rein 
gesetzt
habe. Hat auch nicht funktioniert. Dann habe ich mal ein in meinem 
CheckboxDelegate
gedebuggt. Der Konstruktor wird aufgerufen; ich übergebe den Delegate 
mit

treeview->setItemDelegateForColumn(0, mydelegate)

und dann wird aber in meinem Delegate überhaupt nie was aufgerufen. Ich 
habe ihn
dann testweise mal entfernt, und die Checkboxen sind trotzdem da! d.h. 
die
Checkboxen kommen vom Standard Datenmodell irgendwo her und mein 
Delegate wird
gar nie ausgeführt. Was mache ich da falsch?

von Sven B. (scummos)


Lesenswert?

Du hast sehr viel Code gepostet, aber nicht den, wo du das Delegate 
erzeugst und setzt, und ich denke das wäre der interessante Teil.

Warum ein eigenes Delegate, wenn der Treeview die Checkbox schon kann 
als Editor?

von Nesbit (Gast)


Lesenswert?

Ja in der Tat, diesen Teil habe ich vergessen. Hier ist er:
1
DataModel dm;
2
CheckboxDelegate dg;
3
4
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
5
{
6
    ui->setupUi(this);
7
    ui->treeView->setModel(&dm);
8
    ui->treeView->setItemDelegateForColumn(0,&dg);
9
}

Dass der TreeView die Checkbox schon von Haus aus kann wusste ich, aber 
ich habe nicht herausgefunden wie. Dann habe ich gelesen, man brauche 
dazu einen Delegate, und jetzt wo ich diesen erstellt habe, geht es 
irgend auf eine magische Weise doch ohne :o

von Nase (Gast)


Lesenswert?

Sven B. schrieb:
> Ja, ich ja auch nicht, aber das ist QVector bzw. sollte QVector sein.
> Nicht QList.
> [...]
Weiß ich nicht. Bei großen statischen Strukturen ist QVector eben 
elendig ineffizient beim Einfügen in der Mitte usw.

QList ist halt ein Mittelweg a.k.a. Kompromiss. U.a. lag damals ja ein 
Schwerpunkt darauf, dass QList zu möglichst wenig Code expandiert, und 
das tut es letztlich ja auch.


Sven B. schrieb:
> Naja, der Mensch will immer alles löschen, und ich finde das auch nicht
> immer besonders gut, aber er hat einen gewissen Einfluss.
Er hat auch durchaus die rechtfertigenden Kompetenzen :-)

Sven B. schrieb:
> Naja, das ist ja in Qt5 deutlich renoviert worden. Der moc macht auch
> andere Sachen, und sparen könnte man den auch mit modernem C++ nicht.
Joa, man ist von der String-basierten Syntax weg.
Aber z.B. sig++ gibts ja nun auch schon ein paar Jährchen.

von Bernd K. (prof7bit)


Lesenswert?

Rolf M. schrieb:
> using array = int[5]; // Ein "array" vom Typ using, das mit dem 5.
> Element von "int" initialisiert wird?

Böse Zungen behaupten C++ hätte überhaupt keine Grammatik ;-)

von Nase (Gast)


Lesenswert?

Bernd K. schrieb:
> Böse Zungen behaupten C++ hätte überhaupt keine Grammatik ;-)
Ne, das war Perl.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.