Seite 1 von 1

Aller Anfang ist schwer: QTableWidgetItem erweitern

Verfasst: 30. Juni 2010 22:17
von Astralix
Hallo,

habe schon wunde Finger vom Suchen und Schreiben, daher jetzt mal hier eine Frage vom Anfänger an die Profis. C ist absolt kein Thema, aber bei C++ bin ich (relativ) neu und bei Qt mache ich gerade meine ersten Schritte.

Das Projekt soll einige Daten von einem Sack von via USB angeschlossener Sensoren Darstellen. Nix großes, nix aufwändiges. Dazu habe ich die libUSB eingebunden und hatte gedacht, dass das der schwere Teil sein würde. Aber die Sensoren sehe ich alle und kann sie auch ansprechen und auslesen.

Um die Sensoren im Programm dar zu stellen, fand ich ein QTableWidget sehr praktisch. Also habe ich von der Klasse QTableWidgetItem eine eigene Klasse RtcDevice abgeleitet und diese dann um die Daten und Funktionen für einen solchen Sensor erweitert.

Code: Alles auswählen

rtc_class.h 
----------------
class RtcDevice: public QTableWidgetItem
{
    public:
        RtcDevice( struct usb_device *udev);
        ~RtcDevice();

        QTableWidgetItem* clone() const;

        usb_dev_handle *udev;

        System_t system;

        sensor_t tsensor[eTSensNum];

        int rtcFetchSingle();
        int rtcFetchRuntime();
};

rtc_class.c
----------------
RtcDevice::RtcDevice(struct usb_device *dev)
{
    udev = usb_open(dev);
    qDebug("Opening Device %s ...", dev->filename);

    if( udev)
    {
        QString btn;

        rtcFetchSingle();
        rtcFetchRuntime();
        qDebug("Fine!");

        btn = QString((char*)desc.DevName) + "\n" + QString((char*)desc.SerNum);
        this->setFlags(Qt::NoItemFlags | Qt::ItemIsEnabled);
        this->setSelected(false);
        qDebug("Init OK");
    }
    qDebug("Done.");
}

RtcDevice::~RtcDevice()
{
    qDebug("Destroy RtcDevice...");
    if( this->udev)
    {
        qDebug(".. Closing Device");
        usb_close( udev);
    }
    qDebug("Done.");
}

QTableWidgetItem *RtcDevice::clone() const
{
    return new RtcDevice(*this);
}
...

Nun bin ich davon ausgegangen, dass die abgeleitete Klasse auch die übergeordnete Klasse aufruft, was aber nicht passiert.
Ich kann in Debug sehen, dass immer ein neuer Sensor angelegt wird und seine Daten ausgelesen werden, aber meine QTableWidget bleibt leer.
Ich bin ein paar Jahre raus aus der Vererbungslehre, es ist sicher nur was ganz dummes, aber ich finde es nicht..

Ich muss noch hinzufügen, dass diese lösung nicht der optimale Weg sein muss. Wenn ich also komplett auf dem Holzweg bin, dann nehme ich auch gerne Vorschläge entgegen.

Danke schon mal vorab!
Gruß, Astralix

Verfasst: 1. Juli 2010 09:09
von solarix
Hmm.. folgende Hinweise:

1. Vorgehensweise
IMHO gehst du den falschen Weg. Ein QTableWidgetItem muss eigentlich nur dann abgeleitet werden, wenn man ein eigenes Item implementieren möchte (anderes Aussehen oder anderer Inhalt als mit einem QTableWidgetItem möglich ist). Wenn ich dich richtig verstehe, geht es dir nur darum, wie du die Datenbeschaffung (wobei die Daten nur QStrings sind) kapseln kannst.

IMHO wärst du mit einem einem eigenen Model viel besser bedient (QAbstractTableModel ableiten und zusammen mit QTableView verwenden).

2. Zum Problem
Ich weiss nicht so recht, was du erwartest.. ist das aller relevante Code (ist er Rest nur USB-Zeugs)? Ich meine: das Item muss ja auch irgendwann die Daten liefern... musst du die abgerufenen Daten also nicht entweder setzen (setData()) oder gleich selber liefern (QVariant data() überladen)?

3. Zum Programmierstil
Programmiere gleich von anfang an sauber.. überlege dir, welche Klassenmethoden/--Members "public", "protected" und "private" sind.. zur Zeit sieht die Klassendeklaration hässlich aus...

[Edit] Und überlege dir auch eine Strategie bei der Namensgebung von Variabeln.. dann hast du kein "dev..udev..this->udev"-Problem mehr...

hth!

Verfasst: 1. Juli 2010 10:30
von Astralix
solarix hat geschrieben:Hmm.. folgende Hinweise:

1. Vorgehensweise
IMHO gehst du den falschen Weg. Ein QTableWidgetItem muss eigentlich nur dann abgeleitet werden, wenn man ein eigenes Item implementieren möchte (anderes Aussehen oder anderer Inhalt als mit einem QTableWidgetItem möglich ist). Wenn ich dich richtig verstehe, geht es dir nur darum, wie du die Datenbeschaffung (wobei die Daten nur QStrings sind) kapseln kannst.
Ja das war der Ansatz. Die Idee war, das QTWItem gar nicht zu verändern, sondern nur meine eigenen 'Daten' anzuhängen. Ein Timer grast die Tabelle ab und aktualisiert die Sensordaten. Ein paar eigene mit an gehangene Methoden tauschen dann das Icon aus, ändern die Beschriftung. Zieht man einen Sensor ab, würde mit dem removeRow() auch mein Anhang mit gelöscht.
solarix hat geschrieben:IMHO wärst du mit einem einem eigenen Model viel besser bedient (QAbstractTableModel ableiten und zusammen mit QTableView verwenden).
Diese Überlegung verfolge ich inzwischen auch und stelle meinen Code dahingehend um. Letztendlich ist es auch nicht aufwändiger beide Bauteile, das eigene Model und das zugehörige QTWItem zu löschen, wenn ein Sensor verloren geht.
solarix hat geschrieben:2. Zum Problem
Ich weiss nicht so recht, was du erwartest.. ist das aller relevante Code (ist er Rest nur USB-Zeugs)? Ich meine: das Item muss ja auch irgendwann die Daten liefern... musst du die abgerufenen Daten also nicht entweder setzen (setData()) oder gleich selber liefern (QVariant data() überladen)?
Der restliche Code ist für den Aufbau der Tabelle nicht von belang und beschäftigt sich tatsächlich nur mit der USB Kommunikation. Der Ausschnitt oben ist der Kern des Problems. Ich bin halt davon ausgegangen, dass eine abgeleitete Methode automatisch auch die Konstruktoren ihrer Eltern aufruft, dann ihre eigenen. Das scheint aber nicht der Fall zu sein. Und daher bin ich davon ausgegangen, dass meine Ableitung nicht in Ordnung ist.
solarix hat geschrieben:3. Zum Programmierstil
Programmiere gleich von anfang an sauber.. überlege dir, welche Klassenmethoden/--Members "public", "protected" und "private" sind.. zur Zeit sieht die Klassendeklaration hässlich aus...

[Edit] Und überlege dir auch eine Strategie bei der Namensgebung von Variabeln.. dann hast du kein "dev..udev..this->udev"-Problem mehr...
Jaja, :) hast ja recht. Die Software, vor allem die Header, sind aus dem µController Code übernommen. Die Software selbst ist ursprünglich in ANSI-C unter Linux geschrieben und war von mir lediglich eine kleine Test-Suite für den selbst geschriebenen USB-Stack auf den Sensoren.
Ich migriere also nach Qt, was im aktuellen Stadium noch für etwas unschöne Deklarationen sorgt. Aber Du hättest den Code mal vor ner Woche sehen sollen.... :)

Ich stimme Dir aber zu, dass es so nicht bleiben darf.

Vielen Dank schon mal für die Denkansätze, das ganze Projekt ist ja nur zum Lernen gedacht, aber wie gesagt, das was ich mir komplex vorgestellt hatte, der USB-Teil, war am einfachsten... Überraschung :)

CU!

Verfasst: 1. Juli 2010 23:10
von Astralix
Also ich habe mal angefangen meine ganzen public Variablen zu kapseln, Zugriffsfunktionen darauf zu deklarieren und sie selbst nach private: zu verschieben. Ja, sicher, eigentlich sind die structe für sich jedes eine Klasse wert, aber immer Stück für Stück.

Das macht den Code nun schöner und pflegbarer, löst aber nicht das ursprüngliche Problem. Wenn ich ein QTableWidgetItem ableite, führt der Aufruf der Ableitung nicht zur Darstellung dess QTableWidgetItem...

Dann habe ich hier gelesen, dass das QTWItem kein Q_OBJECT ist und ich daher in meiner Ableitung keine Signals und Slots erzeugen kann. Das wäre aber schon schön, denn meine Sensoren haben durchaus was mit zu teilen.

Aber wenn ich dann meine Sensoren als eigenständige Klasse definiere, wie synchronisiert man dann diese mit einem QTableWidget?

Nur zu Erinnerung, ich möchte eine Liste aller (automatisch) gefundenen Sensoren links am Rand haben und rechts dann die Details zu dem jeweils aktivierten Sensor aus der Tabelle.

Schubst mich mal einer in die richtige Richtung, bitte.

danke!

Verfasst: 1. Juli 2010 23:43
von solarix
Astralix hat geschrieben: Schubst mich mal einer in die richtige Richtung, bitte.
<Kräftiger-Schubs-weg-von-QTWItem-Richtung-QAbstractTableModel-mit-QTableView>

Verfasst: 2. Juli 2010 16:42
von Astralix
Ok, ich sehe es ja ein... :)

Ich mache mir aktuell also erst mal Gedanken darüber, wie man das ganze Modelliert... Liege ich da mit folgender Idee in etwas richtig?

Eine Klasse (ScanUSB) wäre die USB-Bus Verbindung. Sie braucht ja Signals und Slots, denn sie bekommt die Aufforderung Sensoren zu suchen, vorhandene zu Pollen und verloren gegangene zu löschen.

Eine weitere Klasse (Sensor) wäre dann der Sensor, der von der USB-Klasse erstellt wird, sobald ein Sensor neu erkannt wird.

Der Sensor selbst besteht auch aus ein paar Klassen, aber das ist hier nicht Gegenstand.

Sehe ich das jetzt richtig, dass ScanUSB auf einer QAbstractTable basiert?
Ein Q...Item hat ja keine Signals und Slots. Das Ginge nur in der Table.

Liege ich da richtig?

Verfasst: 2. Juli 2010 17:24
von solarix
Astralix hat geschrieben: Ein Q...Item hat ja keine Signals und Slots. Das Ginge nur in der Table.
Die erste Aussage ist richtig, die zweite falsch.. aber darauf brauchen wir nicht näher einzugehen, weil du gar kein QTWItem brauchst.

Aber sonst hat sich dein Weg verbessert: es ist immer eine gute Idee, ein Datenformat, ein Protokoll oder eine Technologie in einer Klasse zu kapseln. Ich würde allerdings die Usb-Klasse von der Model-Klasse nochmals trennen:

Code: Alles auswählen

                   kennt                              kennt                       erstellt
QTableView---------->UsbSensorModel--------->UsbScanner--------->UsbSensor
                                      |                                   |                           |
                                      |  erbt von                     |  erbt von             | erbt von
                                     \/                                  \/                          \/
                          QAbstractTableModel              QObject                    QObject
Das kannst du dann hübsch miteinander verketten.. Wenn der UsbScanner ein neuer Sensor hat, könnte der dann ein Signal werfen, welches wiederum an die View weitergeleitet wird und ihr somit mitteilt, dass die Tabelle geändert wurde:

Beispiel:

Code: Alles auswählen

UsbSensorModel::UsbSensorModel(....)
 ....
{
  // weiterleiten des Signals an die View:
  connect(&mUsbScanner, SIGNAL(newSensor()), this, SIGNAL(layoutChanged()));
}

int UsbSensorModel::rowCount() const
{
  return mUsbScanner.sensorCount();
}

....
Ob es notwendig ist, den Sensor nochmals in einer Klasse zu kapseln weiss ich nicht, dafür kenne ich das Ziel zu wenig. Aber der Weg stimmt schonmal :wink:

hth!

Verfasst: 2. Juli 2010 17:27
von Astralix
Danke!

Werde das auch berücksichtigen. Es ist aber einfach viel zu Warm zum Denken... Kannst Du mir vielleicht nebenbei noch ein gutes (Online)Buch empfehlen?

CU