Forum: PC-Programmierung Qt4 -> Qt5/6: scrollbar signal handling


von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Ich versuche gerade,

https://github.com/aitjcize/QCamber

auf Qt5/Qt6 zu bringen.

https://github.com/aitjcize/QCamber/issues/23

"This app was build onto of Qt 4.7. I've never tried compiling for Qt 5. 
You are welcome to help port it to Qt 5.0."

Nun, das Projekt ist ein Opensource-ODB++-Viewer – einen anderen konnte 
ich zumindest auf die Schnelle nicht finden. Insofern könnte sich ein 
wenig Aufwand ja mal lohnen.

Am Ende stolpere ich über die Art und Weise, wie dort Scrollbar-Events 
gehandhabt werden:
1
  connect(horizontalScrollBar(), SIGNAL(valueChanged(int)),
2
      this, SLOT(updateLayerViewport(void)));

Das wirft eine Reihe von Fehlern, die wohl in erster Linie darauf hin 
deuten, dass sich das erste Argument nicht in einen QObject* wandeln 
ließe.
1
../../src/graphicsview/odbppgraphicsminimapview.cpp:55:3: error: no matching member function for call to 'connect'
2
   55 |   connect(horizontalScrollBar(), SIGNAL(valueChanged(int)),
3
      |   ^~~~~~~
4
/usr/local/include/qt5/QtCore/qobject.h:222:36: note: candidate function not viable: cannot convert argument of incomplete type 'QScrollBar *' to 'const QObject *' for 1st argument
5
  222 |     static QMetaObject::Connection connect(const QObject *sender, const char *signal,
6
      |                                    ^       ~~~~~~~~~~~~~~~~~~~~~
7
/usr/local/include/qt5/QtCore/qobject.h:225:36: note: candidate function not viable: cannot convert argument of incomplete type 'QScrollBar *' to 'const QObject *' for 1st argument
8
  225 |     static QMetaObject::Connection connect(const QObject *sender, const QMetaMethod &signal,
9
      |                                    ^       ~~~~~~~~~~~~~~~~~~~~~
10
/usr/local/include/qt5/QtCore/qobject.h:481:41: note: candidate function not viable: cannot convert argument of incomplete type 'QScrollBar *' to 'const QObject *' for 1st argument
11
  481 | inline QMetaObject::Connection QObject::connect(const QObject *asender, const char *asignal,
12
      |                                         ^       ~~~~~~~~~~~~~~~~~~~~~~
13
/usr/local/include/qt5/QtCore/qobject.h:242:43: note: candidate template ignored: substitution failure [with Func1 = const char *, Func2 = const char *]: no type named 'Object' in 'QtPrivate::FunctionPointer<const char *>'
14
  242 |     static inline QMetaObject::Connection connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal,
15
      |                                           ^                                                         ~~~~~~
16
/usr/local/include/qt5/QtCore/qobject.h:283:13: note: candidate template ignored: requirement 'int(QtPrivate::FunctionPointer<const char *>::ArgumentCount) >= 0' was not satisfied [with Func1 = const char *, Func2 = const char *]
17
  283 |             connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal, const QObject *context, Func2 slot,
18
      |             ^
19
/usr/local/include/qt5/QtCore/qobject.h:322:13: note: candidate template ignored: substitution failure [with Func1 = const char *, Func2 = const char *]: no type named 'Object' in 'QtPrivate::FunctionPointer<const char *>'
20
  322 |             connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal, const QObject *context, Func2 slot,
21
      |             ^                                                         ~~~~~~
22
/usr/local/include/qt5/QtCore/qobject.h:274:13: note: candidate function template not viable: requires 3 arguments, but 4 were provided
23
  274 |             connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal, Func2 slot)
24
      |             ^       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
25
/usr/local/include/qt5/QtCore/qobject.h:314:13: note: candidate function template not viable: requires 3 arguments, but 4 were provided
26
  314 |             connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal, Func2 slot)
27
      |             ^       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
28
../../src/graphicsview/odbppgraphicsminimapview.cpp:57:3: error: no matching member function for call to 'connect'
29
   57 |   connect(verticalScrollBar(), SIGNAL(valueChanged(int)),
30
      |   ^~~~~~~
31
/usr/local/include/qt5/QtCore/qobject.h:222:36: note: candidate function not viable: cannot convert argument of incomplete type 'QScrollBar *' to 'const QObject *' for 1st argument
32
  222 |     static QMetaObject::Connection connect(const QObject *sender, const char *signal,
33
      |                                    ^       ~~~~~~~~~~~~~~~~~~~~~
34
/usr/local/include/qt5/QtCore/qobject.h:225:36: note: candidate function not viable: cannot convert argument of incomplete type 'QScrollBar *' to 'const QObject *' for 1st argument
35
  225 |     static QMetaObject::Connection connect(const QObject *sender, const QMetaMethod &signal,
36
      |                                    ^       ~~~~~~~~~~~~~~~~~~~~~
37
/usr/local/include/qt5/QtCore/qobject.h:481:41: note: candidate function not viable: cannot convert argument of incomplete type 'QScrollBar *' to 'const QObject *' for 1st argument
38
  481 | inline QMetaObject::Connection QObject::connect(const QObject *asender, const char *asignal,
39
      |                                         ^       ~~~~~~~~~~~~~~~~~~~~~~
40
/usr/local/include/qt5/QtCore/qobject.h:242:43: note: candidate template ignored: substitution failure [with Func1 = const char *, Func2 = const char *]: no type named 'Object' in 'QtPrivate::FunctionPointer<const char *>'
41
  242 |     static inline QMetaObject::Connection connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal,
42
      |                                           ^                                                         ~~~~~~
43
/usr/local/include/qt5/QtCore/qobject.h:283:13: note: candidate template ignored: requirement 'int(QtPrivate::FunctionPointer<const char *>::ArgumentCount) >= 0' was not satisfied [with Func1 = const char *, Func2 = const char *]
44
  283 |             connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal, const QObject *context, Func2 slot,
45
      |             ^
46
/usr/local/include/qt5/QtCore/qobject.h:322:13: note: candidate template ignored: substitution failure [with Func1 = const char *, Func2 = const char *]: no type named 'Object' in 'QtPrivate::FunctionPointer<const char *>'
47
  322 |             connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal, const QObject *context, Func2 slot,
48
      |             ^                                                         ~~~~~~
49
/usr/local/include/qt5/QtCore/qobject.h:274:13: note: candidate function template not viable: requires 3 arguments, but 4 were provided
50
  274 |             connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal, Func2 slot)
51
      |             ^       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
52
/usr/local/include/qt5/QtCore/qobject.h:314:13: note: candidate function template not viable: requires 3 arguments, but 4 were provided
53
  314 |             connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal, Func2 slot)
54
      |             ^       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Kann mir jemand mit tieferen Qt-Kenntnissen hier auf die Sprüngen 
helfen, wie man das heutzutage ausdrücken würde?

von Urban (ubn)


Lesenswert?

Jörg W. schrieb:
> /usr/local/include/qt5/QtCore/qobject.h:222:36: note: candidate function
> not viable: cannot convert argument of incomplete type 'QScrollBar *' to
> 'const QObject *' for 1st argument

Er kennt QScrollBar nicht. Evtl. einfach QWidgets includen?
1
#include <QtWidgets>

Im Übrigen würde ich empfehlen die SIGNAL() und SLOT() durch 
Funktionspointer-Schreibweise zu ersetzen, weil es nicht mehr zu 
Runtime-Errors führt (sondern compile-time) falls die signal/slot 
connection nicht erstellt werden kann. Ungefähr so (ungetestet):
1
  connect(horizontalScrollBar(), &QScrollBar::valueChanged,
2
          this, &ODBPPGraphicsMiniMapView::updateLayerViewport);

Falls es wegen inkompatiblen Argumenten nicht klappt, Lambdas verwenden:
1
  connect(horizontalScrollBar(), &QScrollBar::valueChanged,
2
          this, [this](int){ updateLayerViewport(); });

: Bearbeitet durch User
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Danke für die schnelle Antwort, aber jetzt ist es schon etwas spät. ;-) 
Ich schau mir das an.

von Oliver S. (oliverso)


Lesenswert?

Urban schrieb:
> Er kennt QScrollBar nicht. Evtl. einfach QWidgets includen?

Besser QScrollbar ;)

Oliver

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Urban schrieb:
> Er kennt QScrollBar nicht. Evtl. einfach QWidgets includen?

Danke nochmal. Dass man QtWidgets explizit angeben muss (schon im 
qmake-Projekt und dann in den includes) ist offenbar eine der 
wesentlichen API-Änderungen zwischen Qt4 und Qt5 gewesen. Das include 
fehlte in diesem Projekt an vielen Stellen.

Jetzt habe ich es soweit mit Qt5 am Compilieren. Es gibt aber immer noch 
ein paar deprecation warnings, die würde ich auch noch aufräumen, dann 
sollte es vielleicht auch gleich Qt6-kompatibel sein.

von Rolf M. (rmagnus)


Lesenswert?

Bei Qt hat man mit der Zeit immer weiter die sich gegenseitig 
inkludierenden Header reduziert, so das etwas, das in Qt4 noch implizit 
durch einen anderen Header mit reingezogen wurde, in Qt5 ggf. explizit 
eingebunden werden muss.
Ich würde aber auch nicht QtWidgets inkludieren, weil das ein 
Rundumschlag mit allem ist. Besser wäre es, nur die Widgets, die auch 
tatsächlich benötigt werden, einzeln einzubunden.

: Bearbeitet durch User
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Rolf M. schrieb:
> Ich würde aber auch nicht QtWidgets inkludieren, weil das ein
> Rundumschlag mit allem ist. Besser wäre es, nur die Widgets, die auch
> tatsächlich benötigt werden, einzeln einzubunden.

Das wäre jetzt schon sehr viel Feinschliff. Im Moment habe ich es mit 
QtWidgets (und ein paar anderen Änderungen) wenigstens erstmal mit Qt5 
zum Compilieren bekommen. Qt6 braucht noch paar Handgriffe mehr (u.a. 
weil QRegExp dann wegfällt oder nur noch compat ist).

Erstmal möchte ich es aber auch sinnvoll laufen sehen. Soweit ist es 
leider im Moment noch nicht.

von Bernd B. (bbrand)


Angehängte Dateien:

Lesenswert?

Hi,

angehängt ist ein Patch gegen den aktuellen Stand in GitHub. Damit lässt 
sich QCamber zumindest unter Linux sowohl mit Qt5 als auch Qt6 (aber 
nicht mehr mit Qt4) bauen. Die regulären Ausdrücke sind auf 
QRegularExpression umgestellt, die Compat-Library wird daher unter Qt6 
nicht benötigt.
Es scheint soweit auch zu laufen, allerdings habe ich es bisher nur mit 
der Beispieldatei von odbplusplus.com getestet.
(https://odbplusplus.com/wp-content/resource-docs/designodb_rigidflex.tgz)

Gruß,
Bernd

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Danke! Magst du den selbst noch als pull request eintüten oder soll ich 
das für dich machen?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Qt4 hatte ich auch in meinem PR schon zu den Akten gelegt. Ist schon so 
lange obsolet, dass man das wohl nicht mehr unterstützen muss.

von Bernd B. (bbrand)


Lesenswert?

Jörg W. schrieb:
> Magst du den selbst noch als pull request eintüten oder soll ich
> das für dich machen?

Mach Du ruhig, ich habe keinen GitHub Account :)

von Bernd B. (bbrand)


Lesenswert?

Hmm, ich habe es jetzt mal mit einer aus KiCad exportierten ODB++-Datei 
getestet. Dabei stürzt QCamber direkt ab. Das Fehlerhandling der 
aktuellen Implementierung ist eher bescheiden, die Software verlässt 
sich sehr darauf, dass der Input genauso aussieht wie erwartet. Im 
konkreten Fall wird versucht, eine Datei "steps/pcb/attrlist" zu laden, 
die im KiCad-Export aber gar nicht existiert. Das wird aber nicht 
abgefangen, sondern führt in der Folge zu einem NULL-Pointer Zugriff.

Es ist auch nicht schön, dass QCamber nur ODB++-Dateien im Format 
.tar.gz lesen kann. KiCad exportiert aber nur .zip, das muss dann erst 
mal umgepackt werden.

Korrektur: Stimmt gar nicht, KiCad kann auch .tgz exportieren.

: Bearbeitet durch User
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Ja, mindestens einen der Nullpointer-Abstürze habe ich hier im Debugger 
schon analysiert und korrigiert. Ich vermute, dass das noch nicht der 
letzte ist.

Man müsste das Teil auch noch umstellen vom externen Aufruf eines 
tar-Kommandos auf zlib, dann wäre sowohl .tar.gz als auch .zip 
gleichermaßen handhabbar, und man muss nicht noch auf Windows extra ein 
tar.exe beilegen, wie es die Demo-Binaries machen (die bei mir unter 
Wine aber auch nur crashen).

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Jörg W. schrieb:
> Ja, mindestens einen der Nullpointer-Abstürze habe ich hier im Debugger
> schon analysiert und korrigiert. Ich vermute, dass das noch nicht der
> letzte ist.

Scheint wirklich nur der eine zu sein:
1
diff --git a/src/parser/odbpp/featuresparser.cpp b/src/parser/odbpp/featuresparser.cpp
2
index 13dfba8..111b7e1 100644
3
--- a/src/parser/odbpp/featuresparser.cpp
4
+++ b/src/parser/odbpp/featuresparser.cpp
5
@@ -122,6 +122,8 @@ FeaturesDataStore* FeaturesParser::parse(void)
6
 
7
 void FeaturesParser::putAttrlist(const StructuredTextDataStore* ds)
8
 {
9
+  if (ds == NULL)
10
+    return;
11
   const StructuredTextDataStore::ValueType d = ds->getValueData();
12
   for (StructuredTextDataStore::ValueType::const_iterator it = d.begin();
13
       it != d.end(); ++it) {

Nachdem ich rausgefunden habe, wie man die Farben umstellt (wenngleich 
mir nicht klar ist, warum es nur 6 Farben sind und wie diese den Lagen 
zugeordnet werden), kann ich jetzt auch einen Kicad-Export damit 
anzeigen.

von Bernd B. (bbrand)


Lesenswert?

In einem anderen Projekt habe ich vor kurzem die KArchive-Library 
(https://github.com/KDE/karchive) benutzt.
Damit müsste man die Dateien noch nicht mal auf die Platte einpacken, 
sondern könnte die Inhalte direkt aus dem Archiv lesen. Könnte 
allerdings einen größeren Umbau erfordern. KArchive stammt zwar vom 
KDE-Projekt; anders als der Name vermuten lässt werden aber nur Qt- und 
keine KDE-Libraries benötigt. Ausserdem erfordert zumindest der aktuelle 
Stand von KArchive Qt6.

von Bernd B. (bbrand)


Lesenswert?

Jörg W. schrieb:
> kann ich jetzt auch einen Kicad-Export damit
> anzeigen.

Stimmt, funktioniert mit dem Patch prima :)

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Bernd B. schrieb:
> Jörg W. schrieb:
>> kann ich jetzt auch einen Kicad-Export damit
>> anzeigen.
>
> Stimmt, funktioniert mit dem Patch prima :)

Bin jetzt (mit diesen Demo-ODB++-Daten von oben) in noch einen Crash 
mehr getapst.
1
diff --git a/src/symbol/usersymbol.cpp b/src/symbol/usersymbol.cpp
2
index 8a96e90..4b00393 100644
3
--- a/src/symbol/usersymbol.cpp
4
+++ b/src/symbol/usersymbol.cpp
5
@@ -34,13 +34,14 @@ UserSymbol::UserSymbol(const QString& def, const Polarity& polarity,
6
 {
7
   QString path = ctx.loader->featuresPath("symbols/" + def);
8
   FeaturesDataStore* ds = CachedFeaturesParser::parse(path);
9
-
10
-  for (QList<Record*>::const_iterator it = ds->records().begin();
11
-      it != ds->records().end(); ++it) {
12
-    Symbol* symbol = (*it)->createSymbol();
13
-    addChild(symbol);
14
-    m_symbols.append(symbol);
15
-  }
16
+  
17
+  if (ds != NULL)
18
+    for (QList<Record*>::const_iterator it = ds->records().begin();
19
+        it != ds->records().end(); ++it) {
20
+      Symbol* symbol = (*it)->createSymbol();
21
+      addChild(symbol);
22
+      m_symbols.append(symbol);
23
+    }
24
 
25
   setHandlesChildEvents(true);
26
 }

Ich denke, das ganze Projekt könnte mal einen Audit gebrauchen auf alle 
"ds->".

Bernd B. schrieb:
> In einem anderen Projekt habe ich vor kurzem die KArchive-Library
> (https://github.com/KDE/karchive) benutzt.
> Damit müsste man die Dateien noch nicht mal auf die Platte einpacken,
> sondern könnte die Inhalte direkt aus dem Archiv lesen.

Naja gut, für ein größeres Projekt ist es sicher gar nicht so schlecht, 
die Platte gewissermaßen als Cache zu haben. Zip lässt sich ja random 
access zugreifen, aber .tar.gz nicht, das geht immer nur im ganzen 
Stream.

Außerdem ist zlib halt (wie wir seit dem letzten security issue wissen 
;-) praktisch mittlerweile omnipräsent.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Außerdem bräuchte das Ganze wohl zumindest eine leicht zu findende 
Hilfe-Funktion und vielleicht ja auch noch ein paar Standard-Dinge wie 
File -> Exit. Das Herausfinden der Bedienung war jedenfalls teilweise 
schon mal eine Herausforderung.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Ich sehe auch gerade, dass im Projekt sonst an vielen Stellen "if (!ds)" 
als Test benutzt wird. Das würde ich daher aus stilistischen Gründen 
dann auch so machen.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Jörg W. schrieb:
> Bin jetzt (mit diesen Demo-ODB++-Daten von oben) in noch einen Crash
> mehr getapst.

Ich sehe gerade, dass du den ja auch schon korrigiert hattest.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Bernd B. schrieb:

> angehängt ist ein Patch gegen den aktuellen Stand in GitHub.

Ich habe das als PR eingereicht.

Ich hatte gerade noch festgestellt, dass bei mir der Zoom via Mausrad 
nicht funktionierte. Ich habe in

ODBPPGraphicsView::wheelEvent()

mal ->pixelDelta durch ->angleDelta ersetzt. So, wie ich die Doku 
verstehe, gibt es angleDelta immer (ggf. halt mit großen Sprüngen), 
während pixelDelta nicht immer ein Ergebnis bringen muss. Extra noch 
eine Fallunterscheidung fand ich für das bissel Zoom jetzt auch nicht 
sinnvoll.

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.