We need to establish a time frame before I start showing any code. Yes, you can go visit Source Forge to pull down what is there if you are well and truly desperate to view it. This portion is more about the journey than the code.
xpnsqt
The xpnsqt program was originally written around the time IBM sunset OS/2 which, according to this article, spanned the time frame between March 12, 2003 and December 31, 2004. Foundations of Qt Development came out in 2007 and C++ GUI Programming with Qt 4 came out in 2006. Both of them discussed QSqlDatabase. Ordinarily I would just hit the help key in QtCreator for QSqlDatabase. It would then take me to a help page which would say something like “feature/class was added in version blah.ha. Funny thing happened when I tried that today.
Couldn’t find it on that page. Searched on-line in the 4.8 documentation and didn’t find any mention of when QSqlDatabase class was added. I don’t remember it being in Qt3 but I didn’t do that much with Qt3 because the message boards said Qt4 was going to be a night and day difference.
Point and Laugh
At any rate, now that most of you have some idea just how long ago this was and the fact it was my first “real” Qt program, it is time to let everyone point and laugh.
main.cpp
/**************************************************************************** * Originally created by Roland Hughes at Logikal Solutions. * Project is being released under GPL2 license. * * At some point in the future either Logikal Solutions or Roland Hughes * may elect to publish a book with this original source code in it. If * the book is written it will be part of "The Minimum You Need to Know" * book series. ****************************************************************************/ #include #include #include #include #include "XpnsLogonDialog.h" int main(int argc, char *argv[]) { int i_x; QString qtDbName; QTime theTime = QTime::currentTime(); QApplication app(argc, argv); { QString driverName = "QPSQL"; // // Set up information for driver and see if driver is available // qtDbName = "xpns" + theTime.toString("hhmmsszzz"); QSqlDatabase db = QSqlDatabase::addDatabase(driverName, qtDbName); if (!QSqlDatabase::isDriverAvailable( driverName)) { QMessageBox::critical( 0, "Missing PostgreSQL Driver", "The Qt driver for PostgreSQL " + driverName + " is not installed. You must install it before running this program", QMessageBox::Ok); db = QSqlDatabase(); // reset to avoid warning return 0; } XpnsLogonDialog *xpnsLogonDialog = new XpnsLogonDialog( 0, qtDbName); xpnsLogonDialog->show(); i_x = app.exec(); qDebug() << "About to delete dialog"; delete xpnsLogonDialog; qDebug() << "Dialog deleted, now clearing database"; db = QSqlDatabase::database(); // clear connection to avoid error } // end scope of logon dialog QSqlDatabase::removeDatabase(qtDbName); return i_x; }
Looking at some of the comments in here brought back memories which were a long way from good. I’m sure many of you are freaked seeing the {} block enclosing the bulk of the program and the call to removeDatabase() outside of the block. When I saw the whole “clear connection to avoid error” comment it kind of all came back to me.
One thing I really hate about writing stuff like this for my blog, besides never getting paid for it, is not being able to highlight code. There is probably some HTML markup tags which would let me set the background color, but the WordPress editor should make it a whole lot easier. Due to the way blog themes change the display of preformatted text I cannot even be sure line numbers will display.
The Logon Dialog
While I’m certainly not going to post all of the code in this series, we need to look at a couple more source files (kind of the worst) before we go too far in the discussion.
XpnsLogonDialog.h
/**************************************************************************** * Originally created by Roland Hughes at Logikal Solutions. * Project is being released under GPL2 license. * * At some point in the future either Logikal Solutions or Roland Hughes * may elect to publish a book with this original source code in it. If * the book is written it will be part of "The Minimum You Need to Know" * book series. ****************************************************************************/ #ifndef XPNSLOGONDIALOG_H #define XPNSLOGONDIALOG_H #include #include "ui_XpnsLogonDialog.h" #include "Xpnsform.h" class XpnsLogonDialog : public QDialog, public Ui::XpnsLogonDialog { Q_OBJECT public: XpnsLogonDialog( QWidget *parent, const QString &qtDbName); bool getConnectionStatus() { return m_connectionStatus;} int getTaxYear() { return m_tax_year;} bool useCalendarYear() { return m_calendarYear;} QString getActualDbName() {return m_actualDbName;} private slots: void attempt_postgresql_logon(); void on_userNameLineEdit_textChanged(); private: bool m_connectionStatus; void create_database( bool dbCreated); void ask_copy(); void prompt_prior_year(); void copy_prior_year( int year); bool test_exists( int year); int m_tax_year; QString m_qtDbName; QString m_actualDbName; bool m_calendarYear; Xpnsform *xpnsForm; }; #endif
Multiple Inheritance
Take a good look at that class definition. How many of you remember every class based on Qt and a UI file having to perform multiple inheritance? Come on, be honest! That all changed when the infamous “they” tried to make Qt usable by Java developers so they could finally abandon Swing. I and most others railed against that, but we lost. No, I won’t empty my colon on it again. You can buy a copy of this book if you want to read that rant. Let’s just say Java banned multiple inheritance and then had to bring it back with “interfaces.” They also banned the goto statement but the labeled break statement and a few other such things. The really bad parts in C and C++ existed for reasons. You can’t successfully remove the bad parts until you remove the reason for them.
Yes, if I don’t run out of time with this series I will rewrite the application from scratch using the more current approach to all things Qt. I believe you need to know where we came from because at some point you are going to encounter heritage code someone wants you to change without rewriting. If you don’t believe that you are either too young or haven’t been coding long enough. Every 1-2 years I get phone calls for a contract at a medical device company where they are looking for Qt 3 only. They have some device they want to make one small change in. If you rewrite they have to go through a very lengthy FDA approval process if the consultant just tweaks they can go through a much shorter and cheaper approval process. A rewrite means it is a “new device” while a change means it is an “updated/changed device.” No those aren’t the correct terms. I don’t remember the correct terms, but those work for the general audience.
Design due to QSqlDatabase “feature”
The convoluted design came about due to a “feature” with that version of Qt and Postgresql. In order to run like a mainframe/midrange application a database login dialog needed to pop up. Since I was going to release this as OpenSource I needed to have the information splash dialog pop up first so people too lazy to read a README.TXT would actually know something. First time users would be somewhat screwed. There was no way around having to manually enter categories. I didn’t want to ship with a predetermined set of categories. N year users would have it great because the login dialog would create the database for the tax year and offer to copy category information forward.
Our first “feature” reared its head during the first cut where I called the dialog exec() method. Even though QSqlDatabase was supposed to globally keep track of connections, when the dialog which created the database went away, so did the connection. In order to keep an entry you had to keep one reference alive. This left me with some really unenviable design choices.
- Pass a pointer to the db variable into the constructor of the dialog
- Pass string references for username and password and database year duplicating the connection code
- Derive from QApplication splitting all of the login functionality into methods within that class which would be executed when the event loop started.
- Doing something similar with a QObject class.
Our second and third “feature” reared its head when I didn’t have {} around the life of the db variable. If one didn’t clear the database connection a runtime error would spit up in the terminal/debug window about object destroyed with active connection or something like that. Simply clearing the connection wasn’t enough, or at least it wasn’t instant enough. Calling removeDatabase() immediately after the clear with the db variable still in scope would cause more errors to be thrown about removing a database with an active connection.
Growing Pains Leave Warts
Yes, they were growing pains. Yes, most of these oddities have long since been cleaned up. Yes, this code still compiles and runs, I use it every time tax season approaches. Yes, you are going to encounter decade plus old code because it serves its purpose and rewriting isn’t free.
Many years ago I worked on a trading floor system for a stock exchange. The original “temporary” system was written in BASIC on a PDP 11 computer. Years later it was “temporarily” ported to the VAX hardware, then the Alpha, all the while the “permanent” trading floor system was being designed and a platform being chosen. This temporary system got pretty much rewritten when the U.S. stock market switched from trading in fractions to dollars and cents. That stock exchange got rid of its trading floor a few years ago. I’m told that “temporary” system went away with the trading floor but I would not be surprised to learn the automated portions of that system are what is running the exchange today.
Short Term “solutions” = Long Term Problems
Keep in mind that Y2K happened because “short term” solutions which made sense in the 1970s and early 1980s which were supposed to be replaced once storage and software became cheap enough were still in use near the year 2000. Read this page to learn what other Y2K like events are in your future.
Cast no stone at decade plus old code. We can always insult the ability of the developer but sometimes it is the ability of others which forced an ugly choice.
Related posts:
Where Did My QDebug Output Go?
QtCreator – No qmlScene installed