GUI-Konzepte erstellen mit Pencil

Hallo,

inzwischen ist mein größeres Projekt, indem ich lediglich ein größeres Team unterstützt habe, zu Ende. Daher erstelle ich wieder eigene Anwendungen, mit allen Schritten, wie Analyse, Konzeption, Entwicklung, Test, Dokumentation – einfach alles, was so zum Erstell-Zyklus einer Anwendung gehört.

Dazu gehört ja meist auch entweder einen Klick-Dummy zu erstellen, oder ein GUI-Design. Jetzt kann man sicherlich bei Web-Anwendungen das ganze über HTML machen und mit Default-Werte füllen, bei Desktop- oder Embedded-Anwendungen sieht das meist schwieriger aus.

Vor etlichen Jahren, als der Cutworks-Webshop konzipiert wurde, wurde ein Usability-Konzept als Wireframe von Centigrade erstellt, was eigentlich genau richtig war, um die wesentlichen Sachen ohne großartig mit Farben ins Detail zu gehen, zu visualisieren. Mit welchem Tool Sie das erstellt haben, weiß ich leider nicht mehr, aber die Art und Weise hat mir damals sehr gut gefallen. Im Web habe ich dann mal gesucht, ob es Freeware oder Open-Source-Software gibt, die solche Wireframe-Grafiken erstellen kann und dabei bin ich auf das Pencil-Projekt von Evolus gestoßen, was für viele Betriebssysteme, d.h. sowohl Linux, Mac OS als auch Windows zur Verfügung steht.

Damit kann man wirklich erstklassig Gui-Konzepte erstellen und in verschiedene Formate exportieren. Inzwischen hat sich Pencil wirklich stark gemausert, es bietet in der aktuellen Version auch die Möglichkeiten, eigene bzw. fertige Collections einzubinden und Cliparts online zu suchen, mit denen man seine GUI-Entwürfe noch einfacher und schöner erstellen kann. Neben „Desktop-Guis“ könnt ihr auch Webseiten-Layouts, IPhone / IPad-Layouts und noch viele andere Arten von GUI’s entwerfen. So könnt ihr in wenigen Minuten Oberflächen gestalten und in PNG, PDF, Inkscape-Dateien, OpenOffice-Dokumente oder als Webseite exportieren.

Hier mal zwei Beispiele:

Die Bedienung ist relativ selbst-erklärend, lediglich mit dem Einbinden von eigenen Bildern hatte ich anfangs Probleme. Bilder bindet ihr ein, indem man diese per Drag & Drop ins Pencil zieht. Eine andere Möglichkeit habe ich bisher noch gar nicht gefunden.

Sogar einer meiner Kunden nutzt das inzwischen um seine Ideen den Entwicklern zu präsentieren.

In Kürze werde ich mal ein kurzes Tutorial über Pencil online stellen, damit ihr Eure ersten Gui-Entwürfe erstellen könnt.

Blog-Umzug und -umstellung

Hallo,

lange Zeit habe ich gehadert, wollte ich doch Flatpress eine weitere Chance geben, es sah ja eine Zeit lang auch so aus, dass ich den Blog in alter Form mit PHP7 weiter betreiben kann.
Leider musste ich aber feststellen, dass inzwischen einige Standard-Plugins (die mein Blog auch nutzt – z.B. Syntaxhighlighter) gar nicht mehr funktionieren. Da ein Syntax-Highlighting für meinen programmierlastigen Blog relativ wichtig ist und auch andere Plugin-Download-Links von Flatpress einfach mal tot sind, habe ich mich entschlossen, den Blog nun endgültig auf WordPress umzustellen.
So spare ich mir die bis dato doppelt Pflege der Blog-Beiträge und außerdem mögliche Sicherheitslücken durch langsame Update-Zyklen.

So sehr mir auch das Herz blutet, das Flatpress-Blog-System aufzugeben, bringt es meines Erachtens nichts, an einer Software festzuhalten, die quasi ein Zombie-Dasein fristet – weder tot noch wirklich lebendig.

Hiermit vielen Dank an Arvid, der viel Arbeit und Energie in das Weiterleben von Flatpress gesteckt hat und mir zumindest kurzzeitig die Hoffnung gegeben hat, das Flatpress noch weiter lebt.

Daneben ist auch der Blog von der URL umgezogen, ab sofort ist der blog direkt in der cssec-Domäne erreichbar, die alten URL’s leiten natürlich auf die neue URL um.

Solltet ihr den alten Blog lieber gemocht haben, ich werde ihn noch eine Weile als „Archiv“ aufrecht erhalten, auch wenn ich bereits alle Beiträge schon in den neuen Blog übernommen habe.

OCI-Treiber für Qt kompilieren

Hallo mal wieder,

vor etlicher Zeit, bestimmt so ca. vor 4-5 Jahren habe ich für mein Laserfeed-Projekt auf meinem PC ein Qt eingerichtet und für die Datenbank-Verbindung irgendwann einmal die SQL-Treiber kompiliert.
Ich bin froh, dass ich den PC immer noch nicht mal neu aufsetzen musste, irgendwie habe ich es bisher nicht geschafft die Entwicklungsumgebung mal auf Windows 10 zu heben und meine Projekte wirklich ohne Probleme zum Übersetzen / Laufen zu bringen.
Da mein PC noch auf Windows 7 läuft und demnächst ja leider der Support für Windows 7 endet, leider auch meine Festplatten-Konfiguration kein Upgrade auf Windows 10 zulässt, muss ich demnächst mal eine komplette Neuinstallation meines Rechners durchführen. Das ist echt Mist, aber leider nicht zu vermeiden.

Um das zu üben, habe ich jetzt erst einmal versucht, Qt 4.8.5 (ja, das ist echt alt, aber läuft immerhin noch) auf mein Notebook zu bekommen und dort mal meine Projekte zu übersetzen.
Dazu benötige ich dummerweise auch den Oracle-SQL-Treiber, da sowohl mein neues Tool als auch Laserfeed gegen eine Oracle Express-Datenbank laufen.

Zwar gibt es ein Tutorial: http://doc.qt.io/archives/qt-4.8/sql-driver.html, wie man die OCI-Treiber kompiliert, spannenderweise funktioniert der Windows-Compile so wie angegeben NICHT!!!

Daher mal ein kurzes Tutorial von mir.
Als erstes benötigt ihr ein instant-client-sdk, welchen ihr bei Oracle direkt herunterladen könnt. Wichtig hierbei ist, dass, wenn ihr wie ich einen mingw32-Compiler nutzt, auch eine 32-bit-Variante des instant_client herunterladet. Den findet ihr hier: https://www.oracle.com/technetwork/topics/winsoft-085727.html.
Hier braucht man das sowohl den „normalen“ instant-client (instantclient-basic-nt…) und auch das SDK (instantclient-sdk-nt).
Zum Übersetzen würde prinzipiell das sdk reichen, aber für die Ausführung der Anwendung benötigt ihr auch noch die dll’s des instantclient-basic-Paketes.

Nach Installation beider Pakete (am besten ins gleiche Verzeichnis) öffnet eine Kommandozeile und wechselt in das Quell-Verzeichnis des OCI-Plugins der Qt-Installation. In meinem Fall liegt Qt unter E:\Qt\4.8.5


cd /D E:\Qt\4.8.5\src\plugins\sqkdrivers\oci

Entgegen der offiziellen Anleitung funktioniert das Setzen der Umgebungsvariablen so nicht. Stattdessen habe ich mich einfach an die Linux/Mac-Anleitung gehalten.

Gebt auf der Kommandozeile folgendes ein:

qmake "INCLUDEPATH+=/sdk/include" "LIBS+=-L /sdk/lib/msvc -loci" oci.pro

Ersetzt dabei durch den Pfad, in den ihr euren Instantclient installiert habt. Achtet darauf, dort bitte Forward-Slashes, also „/“ statt „\“ zu nutzen. In dem Beispielaufruf gehe ich auch davon aus, dass die SDK-Komponente direkt in das Instant-Client-Basic-Verzeichnis installiert wurde, das SDK-Verzeichnis legt das entpacken des SDK-Zips dann entsprechend selbst an.

Wenn ihr Euch beim Verzeichnis nicht verschrieben habt (wie das in meinen ersten paar Versuchen der Fall war 🙂 ), sollte das problemlos funktionieren. Anschließend könnt ihr im gleichen Verzeichnis dann das make aufrufen.


mingw32-make release

bzw.


mingw32-make debug

Beides hat dann funktioniert. Wichtig bei der Ausführung Eures Programmes ist dann, dass die dll’s des Instantclient entweder in Eurem Pfad oder (das mache ich meistens so) in den Projektverzeichnis liegen. Dann sollte auch eine Datenbankverbindung aufgebaut werden können.

Forward Declarations vs. Includes

Hallo da draußen,

heute mal wieder etwas C++-Sachen.

In der Regel inkludiert man in C++-Klassen, speziell wenn es um Qt geht, verschiedene andere Klassen, seien es jetzt QString’s, QList-Elemente oder eben selbst definierte Klassen, die verschiedene Business-Logik kapseln. Diese packt man dann manchmal auch in member-Variablen oder nutzt diese in privaten oder public-Methoden als Übergabe-Parameter.

Prinzipiell gibt es zwei Methoden, um Klassen einzubinden:

  •  direkte includes in der h-Datei
  •  Forward-Deklaration der Klasse

Für Neulinge erkläre ich mal die beiden Dinge:

Include in der h-Datei

Hier mal ein Beispiel-Code für ein Klassen-Header, die einen Methode hat, der ein QString übergeben wird. Die Datei speichere ich mal unter testklasse.h ab.

#ifndef TESTKLASSE_H
#define TESTKLASSE_H
#include <QObject>
#include <QString>
 
class TestKlasse : public QObject {
public:
  TestKlasse(); //Konstruktor der Klasse
  QString getHelloWorld() const;
}
#endif // TESTKLASSE_H

Normalerweise generiert Qt die meisten Sachen selbst. Trotzdem kurze Erklärung:

Die erste Zeile wird dazu genutzt, um h-Dateien nicht mehrfach zu laden. Wird eine h-Datei inkludiert, wird geprüft, ob das „Makro“ TESTKLASSE_H schon einmal definiert (also geladen) wurde. Ist dies nicht der Fall, wird dieses „Makro“ definiert (2.Zeile).
In der dritten und vierten Zeile werden die entsprechenden referenzierten / genutzten Klassen, nämlich QObject und QString geladen

Dann wird die Klasse definiert als Unterklasse von QObject und Konstruktor und eine Methode getHelloWorld() definiert.

Die letzte Zeile definiert dann das Ende des Makros (Laden der TestKlasse).

Soweit, sogut. Durch die „#include“-Anweisungen haben wir entsprechend die genutzten Klassen direkt im H-File geladen. Die Klassendefinition steht nun sowohl im h-File als auch im zugehörigen cpp-File (also der Implementierung) zur Verfügung.

Nun noch die zugehörige Cpp-Datei (testklasse.cpp)

#include "testklasse.h"
//Definition des Konstruktors
TestKlasse::TestKlasse() : public QObject() {
 
}
 
QString TestKlasse::getHelloWorld() {
  return QString("Hello World!");
}

Wie man sieht, benötigt man hier keinen Include von QString und QObject mehr.

Forward-Deklaration der Klasse

Prinzipiell kann man die includes aber auch in die Implementierung verlagern. Dazu mal wieder unsere modifzierte h-Datei testklasse.h:

#ifndef TESTKLASSE_H
#define TESTKLASSE_H
 
class QString;
class QObject;
 
class TestKlasse : public QObject {
public:
  TestKlasse(); //Konstruktor der Klasse
  QString getHelloWorld() const;
}
#endif // TESTKLASSE_H

Hier fällt auf, dass nun keinerlei Includes in der Datei vorhanden sind. Dafür werden die Klassen QString und QObject in Zeile 4 und 5 Forward-Deklariert. Die Forward-Deklaration reserviert quasi den Namen der Klasse, die „Implementierung“ erfolgt dann in der cpp-Klasse. Das heißt, in der cpp-Klasse müssen dann die eigentlichen Includes gemacht werden.

Somit sieht unsere testklasse.cpp folgendermaßen aus:

#include "testklasse.h"
#include <QObject>
#include <QString>
//Definition des Konstruktors
TestKlasse::TestKlasse() : public QObject() {
 
}
 
QString TestKlasse::getHelloWorld() {
  return QString("Hello World!");
}

Der Unterschied zum ersten Fall (include in h-Datei) ist, dass die includes in Zeile 2 und 3 nun im cpp-File zwingend definiert sein müssen.

Selbstverständlich kann man die Dinge auch mischen, d.h. bestimmte Klassen übers h-File laden und andere Klassen über Forward-Deklaration laden.

Da ich primär aus dem Java-Bereich komme und mir C++ später erst selbst angeeignet habe, war für mich die erste Methode immer die Wahl, auch wenn das nicht immer zu 100% funktioniert hat. Speziell, wenn man Kreuzabhängigkeiten zwischen Klassen besitzt, z.B. Klasse A beinhaltet eine Liste von Klasse B, die wiederum einen Rück-Verweis (als Pointer) auf die Klasse A besitzt. Wenn beide jeweils in der h-Datei auf das jeweilig andere verweisen, kommt ein großes Kuddelmuddel heraus und der C++-Compiler steigt aus.
In solchen Fällen [b]MUSS[/b] man eine Forward-Deklaration benutzen.

Wann nutzt man also nun welche Methode?

Vorteil der ersten Variante ist die klar definierte Struktur. Durch Anschauen der h-Datei bekommt man sofort einen Überblick, welche weiteren h/cpp-Dateien entsprechend genutzt werden. Speziell zur Erzeugung von Klassenbäumen ist das nett.
Großer Nachteil ist, dass bei irgendeiner Änderung der h-Datei automatisch auch alle abhängigen Klassen mit übersetzt werden (inkl. h-Dateien). Das führt im schlimmsten Fall dazu, dass bei Änderung einer h-Datei das gesamte Projekt neu übersetzt werden muss, wenn denn z.B. die h-Datei eine essentielle, überall genutzte Datei ist. Bei größeren Projekten geht so ziemlich viel Compile-Zeit verloren.

Das ist wiederum der Vorteil der zweiten Variante. Dort werden nur die Klassen, die direkt die geänderte Klasse (mit dem geänderten h-File) nutzen übersetzt, die h-Datei bleibt entsprechend unangerührt. Klassen, wiederum auf diese h-Datei verweisen müssen dann nicht mehr übersetzt werden.
Hier spart man sich wirklich viel Compile-Zeit.
Ein weiterer Vorteil ist, dass man z.B. unterschiedliche Implementierungen vornehmen kann, je nach Qt-Version, Makros.

Ich persönlich nutze aktuell eine Misch-Variante. Da sich Qt-Klassen i.d.R. nicht ändern, inkludiere ich diese über die erste Methode, also direkt im h-File und nutze die Forward-Deklaration für meine eigenen Klassen.
Das spart dann Compile-Zeit und zumindest, was an Qt-Klassen inkludiert wird, kann ich direkt aus dem h-File ersehen.

Signals einfacher mit QT-Creator

Heute mal seit langer Zeit wieder ein längerer Beitrag mit einem kleineren Tutorial.

Aktuell bin ich mal wieder dabei eine Qt-(Desktop)-Anwendung für einen Kunden zu erstellen, mal wieder ganz von Null auf, inkl. Analyse, Wireframe-Konzept, Klickdummy, Umsetzung, Dokumentation und Test.

Bis auf Nutzung als C++-Editor habe ich im Qt-Creator lange Zeit nichts mehr mit Oberflächen-/Formularen entwickelt, die anfängliche Idee, die neue Anwendung mit QML zu entwickeln habe ich nach einer Weile QML-Tutorials anschauen und kurzer Überlegung dann verworfen und entwickle das neue Tool nun wieder als Qt-Widget-Anwendung. Wie sagt man so schön:

Schuster bleib bei Deinen Leisten

In meiner bisher umfangreichsten Qt-Desktop-Anwendung „Laserfeed“ ist vieles historisch gewachsen, manche Dinge müsste ich eigentlich mal dringend umstellen, u.a. alle Tab-Inhalte als separate Widget-Klassen definieren. Aktuell ist da der Großteil noch im Haupt-Form untergebracht, was diese Klasse echt katastrophal zu pflegen macht.

Mit Umsetzung der neuen Anwendung möchte ich das diesmal gleich von Anfang an richtig machen. Daher habe ich jeden Screen erst mal als eigene Formular-Klasse definiert und das entsprechende Layouting vorgenommen. Anschließend habe ich in dem MainWindow ein StackWidget angelegt und dort die entsprechende Anzahl an Seiten eingefügt. In jede Seite kommt ein Widget und wird über ein Layout entsprechend auf die Größe des StackWidgets gesetzt.

Diese Widgets werden dann als „Delegate“ eingerichtet, das macht man, indem man im Qt-Creator rechte Maustaste auf das Widget klickt und „Als Platzhalter für benutzerdefinierte Klasse festlegen…“ auswählt.

Das öffnet einen neuen Dialog, in dem man dann den entsprechenden Klassennamen hinterlegt.

Tragt dort unter Klassenname den Namen Eurer Form-Klasse ein, passt ggf. die include-Datei an (ich habe z.B. alle forms im Unterverzeichnis forms angelegt) und vergesst nicht mit „Hinzufügen“ diese Klasse einzutragen. Wählt dann den neuen Eintrag in dem oberen Treeview aus und bestätigt die Auswahl mit „Anwenden“.

Nun sind die Formulare zwar als separate Formular-Klasse hinterlegt werden aber trotzdem im MainWindow „verlinkt“.

Für meine Anwendung soll es eine Startseite geben, auf der eine entsprechende Aktion gewählt werden kann. Diese Aktion öffnet dann ein entsprechendes Formular. Im Formular gibt es u.a. einen OK- und Abbrechen-Button.
Wird auf OK bzw. Abbrechen geklickt, soll wieder zum Startbildschirm gewechselt werden. Wie bringt man aber die Delegates dazu, mit dem Haupt-Screen zu interagieren? Ja klar, über Signal -> Slots.

Dazu definiere ich in den Formular-Klassen jeweils signals für ok und abbrechen, z.B:

    signals:
     void okSelected();
     void abbrechenSelected();

Im entsprechenden Formular werden dann die Button-Drücke mit Aktionen hinterlegt. Das macht man im Designer, indem man auf den entsprechenden Button rechtsklickt und „Slot anzeigen…“ wählt.

Es öffnet sich wieder ein Fenster, dort wählt man das entsprechende Signal aus, dass man implementieren möchte, in unserem Fall einfach clicked() ohne Parameter (ist ja kein Togglebutton).

Drückt man OK, legt Qt automatisch ein entsprechenden Slot an. Im Code schicken wir dann wieder ein Signal, je nach Button.

OK-Button:

emit okSelected();

Abbrechen-Button:

emit abbrechenSelected();

Nun müssen die Signale noch im Hautpwindow ausgewertet werden. Das kann man entweder auf händische Weise machen, indem man händisch die entsprechenden Slots im Header des Hauptfensters anlegt:

  private slots:
      void widget1OkSelectedSlot();
      void widget1AbbrechenSelectedSlot();

und im Konstruktor die Dinge miteinander verknüpft (wenn unser Formular als widget1 „referenziert ist“):

   connect(ui->widget1, SIGNAL(okSelected()), this, SLOT(widget1OkSelectedSlot()));
   connect(ui->widget1, SIGNAL(abbrechenSelected()), this, SLOT(widget1AbbrechenSlot()));

Alternativ bzw. einfacher geht das direkt im Designer. Irgendwie habe ich den Weg gar nicht mehr in Erinnerung gehabt, aber so geht es eigentlich viel besser.

Öffnet dazu wiederum das HauptWindow im Designer und klickt mit rechter Maustaste auf das entsprechende Formular-Widget.
Dort sollte es einen Eintrag: „Signale/Slots ändern…“.

Ist das nicht der Fall überprüft bitte noch einmal, ob dem Widget auch wirklich mit der Platzhalter-Methode weiter oben eine eigene Formular-Klasse zugewiesen wurde.

Im nachfolgenden Dialog legt ihr um unteren Teil: Signale mit dem +-Symbol zwei Signale an und zwar okSelected() und abbrechenSelected().

Nun können wir analog zu den Button-Klick-Signale auch die beiden neuen Signale implementieren, indem wir wiederum auf dem Widget die rechte Maustaste klicken und wiederum Slot anzeigen… anklicken.

Nun öffnet sich wiederum der Dialog mit der Auswahl und siehe da, dort werden auch unsere beiden Signale angezeigt.

Wählt das entsprechende Signal aus (z.B. okSelected()), klickt auf OK und Qt-Creator erzeugt für Euch den entsprechenden Slot im Haupt-Window. Dort könnt ihr die entsprechende Logik hinterlegen, was passieren soll, wenn der OK-Button des entsprechenden Widgets gedrückt wurde, ohne manuell Slots anzulegen und mit connect-Statements die Sachen händisch zu verknüpfen.