PostgreSQL-Installation oder -Update auf FreeBSD läuft auf Fehler: >>could not load locale „sr_YU.ISO8859-5″<<

Nachdem ich PostgreSQL 16 auf Version 17 aktualisieren wollte, trat beim initdb folgender Fehler auf:

root@marge:~ # service postgresql oneinitdb
initdb postgresql
The files belonging to this database system will be owned by user "postgres".
This user must also own the server process.

The database cluster will be initialized with this locale configuration:
  locale provider:   libc
  LC_COLLATE:  C
  LC_CTYPE:    C.UTF-8
  LC_MESSAGES: C.UTF-8
  LC_MONETARY: C.UTF-8
  LC_NUMERIC:  C.UTF-8
  LC_TIME:     C.UTF-8
The default text search configuration will be set to "english".

Data page checksums are disabled.

fixing permissions on existing directory /server/database/postgres ... ok
creating subdirectories ... ok
selecting dynamic shared memory implementation ... posix
selecting default "max_connections" ... 100
selecting default "shared_buffers" ... 128MB
selecting default time zone ... UTC
creating configuration files ... ok
running bootstrap script ... ok
performing post-bootstrap initialization ... 2025-08-25 11:53:19.740 UTC [71247] FATAL:  could not load locale "sr_YU.ISO8859-5.bak"
2025-08-25 11:53:19.740 UTC [71247] STATEMENT:  SELECT pg_import_system_collations('pg_catalog');

child process exited with exit code 1
initdb: removing contents of data directory "/server/database/postgres"

Die einzige Möglichkeit, wie ich das Problem umgehen konnte, war, dass ich die Datei „/usr/share/locale/sr_YU.ISO8859-5“ in ein anderes Verzeichnis verschoben habe und dann initdb erneut ausführte. Danach habe ich die Datei zurückverschoben.

Testaufgaben nach einem Bewerbungsgespräch

Ich habe jetzt länger überlegt, ob ich darüber überhaupt bloggen möchte. Ich mache es doch einmal, denn es gab für mich etwas sehr interessantes und es ist schon eine ganze Weile her.

Ich war auf Jobsuche, schrieb hier und da eine Bewerbung. Dann hatte ich ein Vorstellungsgespräch und der Herr am anderen Ende der Telefonleitung meinte, es könnte passen. Nachdem er über meine Konditionen nachdachte und das mit der Geschäftsleitung durchsprach, bekam ich einen Anruf, dass diese passen würden, die Firma aber eine Testaufgabe für mich hätte. Darum soll es hier gehen, nämlich um diese eine Testaufgabe und um Testaufgaben im Allgemeinen.

Nachdem wir den Techstack abgeklärt hatten und es darum um PHP und Symfony ging und ich auch sagte, dass ich von beidem keine Ahnung habe (PHP habe ich das letzte Mal vor 20 Jahren gemacht), wurde mir versichert, dass vor dem persönlichen Kennenlernen nur noch eine einfache Testaufgabe zu meistern wäre, nämlich die Umsetzung eines Kontonummer-Bankleitzahlen-IBAN-Konverters. Ich sagte: kein Problem, das musste ich auch schon in einem echten Projekt vor einigen Jahren machen, er soll mir die Aufgabe zukommen lassen. Kurze Zeit später war dann das Dokument mit der Testaufgabe da. Fünf vollgeschriebene DinA4-Seiten. Dabei ging es nicht um die Entwicklung eines Konverters, sondern um ein gesamtes Projekt mit etlichen Zwischenstationen.

Ich sollte mich erst einmal in PHP 8.1 einarbeiten. Danach in Symfony 6.1. Um beides richtig zu machen, würde ich schon keine Stunden mehr veranschlagen, sondern eher einige Tage bishin zu wenigen Wochen. Eine Programmiersprache lerne ich zwar schnell, vor allem PHP, bei der ich nicht einmal von ganz vorne einsteigen müsste, dann aber noch ein komplexes Framework, das ist Aufwand.

Zusätzlich dazu sollte ein Login-Verfahren entwickelt werden und eine dazugehörige Website mit Datenbankanbindung (MySQL oder MariaDB). In der Datenbank sollten die bereits zuvor konvertierten Daten tabellarisch dargestellt werden. Jeder Aufruf sollte einen Zähler inkrementieren.

Weiterhin sollten die Daten dann als JSON exportiert werden können.

Dazu gehörte dann die Installation und Konfiguration von Debian 11, die Installation der benötigten Software (spezielle PHP-Version, nicht in APT drin, MySQL oder MariaDB, Symfony und Apache oder NGINX).

Das war der technische Part. Dazu kam dann ein Benutzerhandbuch für den Konverter über Funktionalität und Bedienung. Dann noch eine Dokumentation für das gesamte Deployment durch einen Laien (Installation und Konfiguration Betriebssystem, Installation und Konfiguration Webserver, Installation und Konfiguration Datenbankserver, Installation und Konfiguration PHP, Installation und Konfiguration von Symfony – durch einen Laien!).

Weiterhin sollte das Projekt vorher komplett durchgeplant werden, vermute mit Pflichenheft, war nicht genauer beschrieben und es sollten auch Tests (Unit-Tests, händische Tests) durchgeführt und dokumentiert werden.

Versteht mich bitte nicht falsch: Das alles kann ich, aber rein realistisch, wenn man es vernünftig macht, ist das ein Projekt, das man nicht in einigen Stunden hinbringt, selbst, wenn man PHP und Symfony auf hohem Level beherrscht. Schon alleine am Pflichtenheft schreibt man eine Weile, das Benutzerhandbuch rotzt man auch nicht einfach runter…

Ich lehnte den Job dann mit Begründung ab. Eine Antwort darauf erhielt ich nie.

Unterschiede zwischen Qt und wxWidgets

Qt und wxWidgets sind zwei beliebte Frameworks für die Entwicklung von Benutzeroberflächen, die sich in technischen und Lizenzunterschieden unterscheiden. Technisch gesehen basiert Qt auf einer objektorientierten Architektur mit einem eigenen Meta-Objekt-System (MOC), das Funktionen wie Signale und Slots für die Ereignisbehandlung bietet. Im Gegensatz dazu verwendet wxWidgets eine Wrapper-Architektur, die die nativen APIs der Zielplattform nutzt, was zu Anwendungen führt, die sich stärker an das native Look-and-Feel anpassen. In Bezug auf Programmiersprachen unterstützt Qt hauptsächlich C++, bietet aber auch Bindings für Sprachen wie Python (PyQt/PySide). wxWidgets ist primär für C++ konzipiert, hat aber ebenfalls Bindings für Python (wxPython) und Perl. Beim GUI-Design kommt Qt mit dem leistungsstarken Qt Designer zur visuellen Gestaltung von Benutzeroberflächen, während wxWidgets oft manuelles Codieren der GUI oder den Einsatz von Drittanbieter-Tools erfordert. In Bezug auf die Plattformunterstützung deckt Qt Windows, macOS, Linux sowie Embedded-Systeme und mobile Plattformen (Android, iOS) ab. wxWidgets unterstützt ebenfalls Windows, macOS und Linux, hat aber eingeschränkte Unterstützung für mobile Geräte.

In Bezug auf die Lizenzierung ist Qt unter GPL, LGPL und kommerziellen Lizenzen lizenziert. Die GPL erfordert die Offenlegung des Quellcodes von abgeleiteten Werken, während die LGPL eine flexiblere Nutzung ermöglicht, insbesondere für proprietäre Software, wenn dynamische Verlinkung verwendet wird. Unternehmen können auch eine kommerzielle Lizenz erwerben, um diese Einschränkungen zu umgehen. wxWidgets wird unter der wxWidgets-Lizenz veröffentlicht, die sehr liberal ist und ähnlich wie die LGPL funktioniert. Sie erlaubt die Entwicklung von sowohl Open-Source- als auch proprietärer Software, ohne die Notwendigkeit, den Quellcode offenzulegen.

Zusammenfassung der Unterschiede

  • Architektur:
    • Qt: Objektorientiert mit MOC, Signale und Slots.
    • wxWidgets: Wrapper-Architektur, nutzt native APIs.
  • Programmiersprachen:
    • Qt: Hauptsächlich C++, Bindings für Python und andere.
    • wxWidgets: Primär C++, Bindings für Python und Perl.
  • GUI-Design:
    • Qt: Qt Designer für visuelle Gestaltung.
    • wxWidgets: Oft manuelles Codieren oder Drittanbieter-Tools.
  • Plattformunterstützung:
    • Qt: Windows, macOS, Linux, Embedded, mobile (Android, iOS).
    • wxWidgets: Windows, macOS, Linux, eingeschränkte mobile Unterstützung.
  • Lizenz:
    • Qt: GPL, LGPL, kommerziell.
    • wxWidgets: Liberale wxWidgets-Lizenz, ähnlich LGPL.

wxWidgets-Tutorial 014: Überblick Event-Handling

In diesem Video zeige ich grob, wie das Event-Handling funktioniert. Tiefer gehen wir darauf aber dann bei den einzelnen Widgets ein.

Letztlich gibt es drei Varianten, wovon die dritte „deprecated“ ist und nicht mehr in neuen Programmen benutzt werden soll:

  1. Event-Table
  2. Bind
  3. Connect
Überblick über Event-Handling

Ein Screenshot unseres Beispielprogramms.

Screenshot unseres Beispielprogramms

Der Quelltext:

#include <wx/wx.h>

class MyApp : public wxApp {

	public:
		bool OnInit();

};

class MyFrame : public wxFrame {

	public:
		MyFrame();
	
	protected:
		void OnButtonClick(wxCommandEvent &event);

};

IMPLEMENT_APP(MyApp)

bool MyApp::OnInit() {
	auto *myFrame = new MyFrame;
	myFrame->Show();

	SetTopWindow(myFrame);

	return true;
}

MyFrame::MyFrame() : wxFrame(nullptr, wxID_ANY, _("Events")) {
	wxButton *button = new wxButton(this, wxID_ANY, _("Click me"));

	button->Bind(wxEVT_BUTTON, &MyFrame::OnButtonClick, this);
}

void MyFrame::OnButtonClick(wxCommandEvent &event) {
	std::cout << "Button clicked" << std::endl;
}

Hier geht es zum Video.

wxWidgets-Tutorial 013: wxGridBagSizer

In diesem Video zeige ich, wie ein wxGridBagSizer funktioniert.

Der wxGridBagSizer basiert auf dem wxFlexGridSizer, bietet aber die Möglichkeit, Widgets und Sizer darin explizit zu positionieren und der Sizer kann Zellen zusammenführen.

Sizer (Layoutmanager): wxGridBagSizer

Hier ein Screenshot von unserem Beispielprogramm:

Screenshot des Beispielprogramms

Der Quelltext:

#include <wx/wx.h>
#include <wx/gbsizer.h>

class MyApp : public wxApp {

	public:
		bool OnInit();

};

class MyFrame : public wxFrame {

	public:
		MyFrame();

};

IMPLEMENT_APP(MyApp)

bool MyApp::OnInit() {
	auto *myFrame = new MyFrame;
	myFrame->Show();

	SetTopWindow(myFrame);

	return true;
}

MyFrame::MyFrame() : wxFrame(nullptr, wxID_ANY, _("wxGridBagSizer")) {
	auto *mainPanel = new wxPanel(this);
	
	auto *gridBagSizer = new wxGridBagSizer(5, 5);

	wxGBSpan twoSpan(1, 2);

	gridBagSizer->Add(new wxStaticText(mainPanel, wxID_ANY, _("Firstname:")), wxGBPosition(0, 0), wxDefaultSpan, wxALIGN_RIGHT);
	gridBagSizer->Add(new wxTextCtrl(mainPanel, wxID_ANY), wxGBPosition(0, 1), twoSpan, wxEXPAND);

	gridBagSizer->Add(new wxStaticText(mainPanel, wxID_ANY, _("Lastname:")), wxGBPosition(1, 0), wxDefaultSpan, wxALIGN_RIGHT);
	gridBagSizer->Add(new wxTextCtrl(mainPanel, wxID_ANY), wxGBPosition(1, 1), twoSpan, wxEXPAND);

	gridBagSizer->Add(new wxStaticText(mainPanel, wxID_ANY, _("Age:")), wxGBPosition(2, 0), wxDefaultSpan, wxALIGN_RIGHT);
	gridBagSizer->Add(new wxTextCtrl(mainPanel, wxID_ANY), wxGBPosition(2, 1));

	gridBagSizer->Add(new wxStaticText(mainPanel, wxID_ANY, _("Zipcode/City:")), wxGBPosition(3, 0), wxDefaultSpan, wxALIGN_RIGHT);
	gridBagSizer->Add(new wxTextCtrl(mainPanel, wxID_ANY), wxGBPosition(3, 1));
	gridBagSizer->Add(new wxTextCtrl(mainPanel, wxID_ANY), wxGBPosition(3, 2), wxDefaultSpan, wxEXPAND);

	gridBagSizer->AddGrowableCol(2);

	mainPanel->SetSizer(gridBagSizer);
	gridBagSizer->SetSizeHints(this);
}

Hier geht es zum Video.

wxWidgets-Tutorial 012: wxSizer verschachteln

In diesem Video zeige ich, wie einfach es ist, wxSizer zu verschachteln.

Um komplexe GUIs zu entwickeln, ist es unabdingbar, dass man Sizer ineinander verschachteln kann. Damit lassen sich einfach und übersichtlich Formulare entwickeln, die dem Benutzer einen hohen Mehrwert bringen.

Sizer (Layoutmanager): wxSizer verschachteln

Hier ein Screenshot unseres Programms:

Screenshot von verschachtelten Sizern

Hier der Quelltext:

#include <wx/wx.h>

class MyApp : public wxApp {

	public:
		bool OnInit();

};

class MyFrame : public wxFrame {

	public:
		MyFrame();

};

IMPLEMENT_APP(MyApp)

bool MyApp::OnInit() {
	auto *myFrame = new MyFrame;
	myFrame->Show();

	SetTopWindow(myFrame);

	return true;
}

MyFrame::MyFrame() : wxFrame(nullptr, wxID_ANY, _("Sizer in Sizers")) {
	auto *mainPanel = new wxPanel(this);

	auto *mainBoxSizer = new wxBoxSizer(wxHORIZONTAL);

	auto *flexGridSizer = new wxFlexGridSizer(2, 5, 5);

	flexGridSizer->AddGrowableCol(1);
	flexGridSizer->AddGrowableRow(4);

	flexGridSizer->Add(new wxStaticText(mainPanel, wxID_ANY, _("Firstname:")), 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT);
	flexGridSizer->Add(new wxTextCtrl(mainPanel, wxID_ANY), 0, wxEXPAND);

	flexGridSizer->Add(new wxStaticText(mainPanel, wxID_ANY, _("Lastname:")), 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT);
	flexGridSizer->Add(new wxTextCtrl(mainPanel, wxID_ANY), 0, wxEXPAND);

	flexGridSizer->Add(new wxStaticText(mainPanel, wxID_ANY, _("Zipcode/City:")), 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT);

	auto *addressSizer = new wxBoxSizer(wxHORIZONTAL);
	addressSizer->Add(new wxTextCtrl(mainPanel, wxID_ANY));
	addressSizer->AddSpacer(5);
	addressSizer->Add(new wxTextCtrl(mainPanel, wxID_ANY), 1);

	flexGridSizer->Add(addressSizer, 0, wxEXPAND);

	flexGridSizer->Add(new wxStaticText(mainPanel, wxID_ANY, _("Age:")), 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT);
	flexGridSizer->Add(new wxTextCtrl(mainPanel, wxID_ANY));

	flexGridSizer->Add(new wxStaticText(mainPanel, wxID_ANY, _("Informations:")));
	flexGridSizer->Add(new wxTextCtrl(mainPanel, wxID_ANY), 0, wxEXPAND);

	mainBoxSizer->Add(flexGridSizer, 1, wxEXPAND | wxALL, 15);

	mainPanel->SetSizer(mainBoxSizer);
	mainBoxSizer->SetSizeHints(this);
}

Hier geht es zum Video.

wxWidgets-Tutorial 011 – wxFlexGridSizer

In diesem Video zeige ich die Verwendung von wxFlexGridSizer.

Im Gegensatz zum wxBoxSizer, der Elemente nur horizontal oder vertikal ausrichten kann, dient der wxFlexGridSizer wie eine Tabelle, in der bestimmt werden kann, welche Spalten und Zeilen sich ausdehen durfen und in dem man einfach Formulare zusammensetzen kann.

Sizer (Layoutmanager): wxFlexGridSizer

Hier ein Screenshot unseres Programms:

Screenshot vom wxFlexGridSizer

Hier der Quelltext:

#include <wx/wx.h>

class MyApp : public wxApp {

	public:
		bool OnInit();

};

class MyFrame : public wxFrame {

	public:
		MyFrame();

};

IMPLEMENT_APP(MyApp)

bool MyApp::OnInit() {
	auto *myFrame = new MyFrame;
	myFrame->Show();

	SetTopWindow(myFrame);

	return true;
}

MyFrame::MyFrame() : wxFrame(nullptr, wxID_ANY, _("wxFlexGridSizer")) {
	auto *mainPanel = new wxPanel(this);

	auto *flexGridSizer = new wxFlexGridSizer(2, 5, 5);

	flexGridSizer->AddGrowableCol(1);
	flexGridSizer->AddGrowableRow(3);

	flexGridSizer->Add(new wxStaticText(mainPanel, wxID_ANY, _("Firstname:")), 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT);
	flexGridSizer->Add(new wxTextCtrl(mainPanel, wxID_ANY), 0, wxEXPAND);

	flexGridSizer->Add(new wxStaticText(mainPanel, wxID_ANY, _("Lastname:")), 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT);
	flexGridSizer->Add(new wxTextCtrl(mainPanel, wxID_ANY), 0, wxEXPAND);

	flexGridSizer->Add(new wxStaticText(mainPanel, wxID_ANY, _("Age:")), 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT);
	flexGridSizer->Add(new wxTextCtrl(mainPanel, wxID_ANY));

	flexGridSizer->Add(new wxStaticText(mainPanel, wxID_ANY, _("Informations:")));
	flexGridSizer->Add(new wxTextCtrl(mainPanel, wxID_ANY), 0, wxEXPAND);

	mainPanel->SetSizer(flexGridSizer);
	flexGridSizer->SetSizeHints(this);
}

Hier geht es zum Video.

wxWidgets-Tutorial 010 – wxBoxSizer

In diesem Video beginnen wir mit Sizern, im Speziellen mit wxBoxSizer.

Layoutmanager sind essentiell bei der Programmierung von grafischen Benutzeroberflächen. Gab es zu Beginn der grafischen Benutzeroberflächen teils nur die Möglichkeit, Elemente absolut positioniert zu platzieren, bieten Layoutmanager, bei wxWidgets Sizer genannt, die Möglichkeit, die Ausrichtung und Anordnung dynamisch zu gestalten.

Der wxBoxSizer kann Elemente horizontal oder vertikal anordnen, proportional vergrößern und verkleiner, ausrichten und vieles mehr.

Sizer (Layoutmanager): wxBoxSizer

Hier ein Screenshot unseres Programms:

Screenshot mit einem wxBoxSizer

Hier der Quelltext:

#include <wx/wx.h>

class MyApp : public wxApp {

	public:
		bool OnInit();

};

class MyFrame : public wxFrame {

	public:
		MyFrame();

};

IMPLEMENT_APP(MyApp)

bool MyApp::OnInit() {
	auto *myFrame = new MyFrame;
	myFrame->Show();

	SetTopWindow(myFrame);

	return true;
}

MyFrame::MyFrame() : wxFrame(nullptr, wxID_ANY, _("wxBoxSizer")) {
	auto *mainPanel = new wxPanel(this);
	auto *mainBoxSizer = new wxBoxSizer(wxHORIZONTAL); // wxVERTICAL

	mainBoxSizer->Add(new wxTextCtrl(mainPanel, wxID_ANY, _("Eingabefeld 1")), 1, wxEXPAND | wxLEFT | wxTOP | wxBOTTOM, 5);
	mainBoxSizer->Add(new wxTextCtrl(mainPanel, wxID_ANY, _("Eingabefeld 2")), 2, wxEXPAND | wxALL, 5);
	mainBoxSizer->AddStretchSpacer();
	mainBoxSizer->Add(new wxTextCtrl(mainPanel, wxID_ANY, _("Eingabefeld 3")), 1, wxEXPAND | wxRIGHT | wxTOP | wxBOTTOM, 5);


	auto *tmp = new wxTextCtrl(mainPanel, wxID_ANY, _("Eingabefeld 0"));
	mainBoxSizer->Insert(0, tmp, 1, wxEXPAND | wxRIGHT | wxTOP | wxBOTTOM, 5);
	mainBoxSizer->InsertSpacer(0, 50);

	mainBoxSizer->Detach(1);
	tmp->Destroy();


	mainPanel->SetSizer(mainBoxSizer);
	mainBoxSizer->SetSizeHints(this);

	std::cout << "Element count: " << mainBoxSizer->GetItemCount() << std::endl;
	std::cout << "Position: " << mainBoxSizer->GetPosition().x << "x" << mainBoxSizer->GetPosition().y << std::endl;
	std::cout << "Size: " << mainBoxSizer->GetSize().GetWidth() << "x" << mainBoxSizer->GetSize().GetHeight() << std::endl;
}

Hier geht es zum Video.

Qt-Tutorial 042: QStatusBar

In diesem Video zeige ich, wie man mit der Statusbar (QStatusBar) umgehen kann.

Viele Fenster des benutzten Fenstermanagers haben am unteren Rand eine Leiste, die manchmal einfach nur da ist, manchmal Informationen anzeigt, manchmal aber auch Interaktionen anbietet. Dieses Video zeigt, wie man Informationen dauerhaft oder für eine gewisse Zeit anzeigt oder Widgets hinzufügt.

QStatusBar

Hier ein Bild unseres Programms:

Screenshot von unserem Beispielprogramm mit QStatusBar

Hier geht es zum Video.