Thoughts on porting to Qt4/KDE4

Work so far

Structuring the work

Four sorts of work here:

  1. Preliminary or refactoring work that does not depend on any major Qt4 conversion slog (but that some Qt4 conversion slog may depend on). For example, making the sequencer a thread rather than a process (if we did that).
  2. Major Qt4 conversion slog that can be scripted. See A Programme For Tedium.
  3. Major Qt4 conversion slog that can't be scripted (dull manual tasks).
  4. The “rewrite bits” – work that takes serious thought and depends on already having a Qt4 backbone, such as the notation editor canvas.

We can also reasonably expect to have some developers (particularly occasional contributors or newer arrivals) wanting to commit fixes and small features while the Qt4 work is going on.

Possible structure:

  • Keep the trunk as a relatively stable KDE3 version, and pledge that any work committed to trunk during the upheaval will be ported over to the Qt4 version at some point afterwards and will not be lost. Trunk may or may not be released as 1.8.
  • Create one branch for each chunk of “preliminary or refactoring work” we attempt. (An existing, rather limited example is the kiftsgate branch, which aims to simplify the notation view by removing most of the action-related code from it, localising it in actions instead – though we probably should merge that particular branch back to trunk anyway as it's more or less done.) One developer for each branch, ripping the code apart with gusto.
  • Create one branch for Qt4 conversion slog. In it:
    • First, concentrate on automatable conversion. I initially thought it might be best not to commit any changes to actual code, instead just building up a set of transformations in a script that we can then run once at the end and commit the results of. I've changed my mind about this; I think instead we should commit as we go, and also keep the script that made the changes up to date so that the current changes could always be reproduced. See A Programme For Tedium. The sole aim here is to get the code as close as possible to compiling. This can take place concurrently with the “preliminary” work that is happening in other branches.
    • Merge preliminary work – a bottleneck.
    • Then do manual conversion of the remaining dull-o slog changes. For this we probably ought to assign developers chunks of code to handle each (assuming we can find any developers).
  • Finally, there's no alternative but to apply lots of actual developer thought to the “rewrite bits” individually.

Major subjects to consider

Build system

Our CMake build system should continue to work, but will need a lot of updating to handle different header locations and to avoid getting mixed up between Qt3/KDE3 and Qt4/KDE4 headers. I personally have never had KDE4 headers installed on any of my machines at all yet, so I've no idea where they go or what they look like.

It would be great if someone could take on the task of updating the CMake files so as to build with entirely “v4” instead of “v3” headers, libraries, moc etc – perhaps in the qt4-mechanised branch so we can test it as we test any mechanical updates.

Translation

One of our goals should be that no translator should have to rewrite a single word as a result of this port.

Qt4 has a different translation file format from the one we use (it's a .ts file, and it's XML) with different tools; KDE4 uses an enhanced version of the same format, with updated tools.

KDE3 to KDE4

Porting KDE3 → 4 there are some big changes in i18n. I don't see any indication that this would break existing .po files, but it's a lot of work for us. i18n is replaced with i18n, i18nc, i18np, i18ncp, and ki18n, and the .arg() methods are no longer used. That looks interesting.

http://websvn.kde.org/*checkout*/trunk/KDE/kdelibs/KDE4PORTING.html#i18n

Plural call is renamed to i18np and does away with %n placeholder, all being numbered instead:

     i18np("One image in album %2", "%1 images in album %2", n, albumName);
KDE3 to Qt4

As far as switching to QT4, if ts2po and po2ts actually work reliably, they could serve to convert our existing files to the new format, and convert them back, in order to allow translators to continue to use their familiar tools in the familiar way.

(Alexandre: [I did] a po2ts migration back in Scribus 1.0.x days. It was fairly easy to do this, but in the end the hierarchy that TS files usually have (a branch for each dialog) was not recreated, so I had to run lupdate and then use the Ctrl+1 key combination to manually put everything in place. I fail to find package that has po2ts for qt4 in Ubuntu, so I cannot test it again.)

The process of swapping i18n() for tr() looks like a lot of work in itself though. It doesn't look like a simple s/i18n/tr/g job at all. We may have difficulties bringing all the i18n stuff up to KDE4 too (although there is a Perl script to do a partial conversion, which ought to help considerably,) but the QT4 route definitely seems to involve even more difficulties. As near as I can tell, the i18n stuff just works thanks to KDE, and we don't have to find and load the translation files manually in main.cpp or elsewhere, or futz with any of the rest of this hands-on stuff QT requires to make translations work.

Plural handling for Qt4 is described at http://doc.trolltech.com/qq/qq19-plurals.html – again, a change from what we've been using, but capable enough.

At a glance, it looks like KBabel can handle the QT stuff, but not vice versa. The most recent linguist-qt4 I have doesn't know what to do with .po files, but the most recent KBabel I have does apparently know how to handle .ts files.

COMMENTS: Either option looks evil, but if we're definitely allowing KDE classes in, I'm (dmm) just more comfortable with the i18n option for no really specific and highly justifiable reason. tr() just seems comparatively lame.

DCOP

KDE4 moves to D-BUS, which is conceptually similar. But my radical suggestion here is to drop the sequencer process altogether. Bring it into a single process, as a separate thread.

Lose the proxying/forwarding classes from src/sound etc, including the whole of MappedStudio.cpp (an overengineered set of classes never really used for their intended purpose). Replace with a simple set of container classes.

Keep MappedEvent, and keep the SegmentMmapper etc on both sides, writing into memory buffers of fixed-layout objects – the difference is simply that the memory buffers are not shared or file-backed any more.

Rename MappedComposition to something less suggestive of an actual composition, but keep it.

Lose MappedInstrument and MappedDevice. Make the sequencer use the main studio classes directly (with mutex as appropriate).

Calling directly from one thread's domain to another is fine for simple accessors etc, but of course we would still need to schedule requests for play, stop etc to ensure they're carried out in the correct thread.

Advantages: Code that stands more chance of being understood by other programmers. Opens more possibilities for tighter integration, e.g. reading and writing tempo maps from the sequencer, or fixing the problems we have with adding and deleting devices, without ludicrous amounts of pain.

Disadvantages: More possibilities for threading-related bugs. Crashing sequencer crashes the GUI too.

Widget and dialog layouts

The Qt widget hierarchy and layout classes have changed somewhat for Qt4. The standard form has become a bit longer in terms of lines of code, though each line is simpler.

e.g. we do quite a bit of things like

   QGroupBox *groupBox = new QGroupBox(1, Horizontal, i18n("Group"), parent);
   QHBox *hbox = new QHBox(groupBox);
   QLabel *label = new QLabel(i18n("Thing:"), hbox);

The more usual way to write this now would be:

   QGroupBox *groupBox = new QGroupBox(i18n("Group")); // or tr() or whatever
   QHBoxLayout *layout = new QHBoxLayout;
   groupBox->setLayout(layout);
   QLabel *label = new QLabel(i18n("Thing:"));
   layout->addWidget(label);   

Also, we use

  QGridLayout::addMultiCellWidget(w, a, b, c, d, align);

an awful lot. This has changed to effectively

  QGridLayout::addWidget(w, a, c, b - a + 1, d - c + 1, align);

The old and new APIs are (respectively)

  QGridLayout::addMultiCellWidget(w, fromrow, torow, fromcol, tocol, align);
  QGridLayout::addWidget(w, fromrow, fromcol, rowspan, colspan, align);

The new API is better, but it's going to be a tricky one. It might just about be scriptable.

Qt4 object constructors no longer have a “name” argument (which used to appear optionally at the end of the constructor arguments in Qt3). It wasn't very useful even in Qt3 and we probably haven't used it all that much overall, but I bet there are some classes that will be using it disproportionately heavily.

Canvas areas

The main segment canvas is a widget, which should be straightforward to port.

The matrix currently uses QCanvas. Guillaume embarked on an experimental revision of this for the Qt4 QGraphicsView (in SVN at trunk/experiments/matrix4). This builds into a separate application with just a matrix in it. In my opinion it's not all that promising – it displays, but operations like rubberbanding are slow and zooming is buggy. I personally think it would be better to convert the matrix to a widget like the segment canvas (only much, much simpler), because I think there would be payoffs in terms of performance and control over display that justified the change.

The notation view also uses QCanvas, and it may be better as a QGraphicsView because the layout model is much more complex than the matrix. I think we should embark on the most literal possible translation from QCanvas and items to QGraphicsView/QGraphicsScene and see where it gets us.

Printing

It might not be unreasonable to remove Rosegarden's native printing altogether – perhaps this is the moment at which we decide that Lilypond-only is better.

Arguments for:

1) Native printing is a mirror of what's on the screen, and there are dozens of bitchy layout problems on-screen that go right to the page. Like the horrible slurs, top of the list.

2) LilyPond export is damn reliable thanks to Heikki.

3) Native printing makes gigantic graphics, and my printer takes hours to digest just one page of this stuff. Native printing is totally useless with my printer unless I use a different, lower-resolution print driver that also reduces the quality to crap. I realize this is just me, not the rest of the world, but native printing is pure evil to me, and I wouldn't miss it a bit.

Arguments against:

1) There are still a few things LilyPond does not export at all. Pedal marks (FIXED), ottavas (FIXED), C vs. 4/4 per user preference (FIXED),

2) The whole rosegarden-lilypondview thing is still rather fragile, and we never have done an adequate job of coming up with some way to make sure users have the right helper applications in place.

3) One of the reasons I work here is because it used to be (and maybe still is) the case that NoteEdit (now Canorus) could only print with external help, whereas Rosegarden could just print and be done with it. (Similar feelings about their old dependency on the TSE3 library.) That impression was formed long ago in a different age when everything worked more poorly than it does now, but I still have a little nostalgia for the idea that we can do our own printing in house.

It's probably a lot more sensible just to ditch it though, and focus attention on the remaining LilyPond edge cases that still allow a user to draw X on the screen but force him to print Y. (One such that springs to mind is http://sourceforge.net/tracker/index.php?func=detail&aid=1560372&group_id=4932&atid=104932)

Help

Qt4 includes a help module starting in Qt4.4 (http://doc.trolltech.com/4.4/qthelp.html). I haven't tried to use it. I think the help is expected to be represented as a set of HTML files. I'm a bit suspicious about how far the Qt classes actually get you – I don't really want to be firing up “Qt Assistant” as a help browser – we want to either have desktop integration (i.e. use the same help browser as KDE, or even a standard web browser) or have our own built-in help window (which it seems can be done without too much trouble using QTextBrowser).

An unknown: What does KDE4 do with help? Research needed!

I'm no great fan of the KDE3 help window; half the time it doesn't even show up for users. A less well-specified, but more tightly integrated with the application, help system might be no bad thing. It does beg more questions about translation support though.

To which Michael replies… Translation of the manual isn't much of an issue, because the only language that's up to date in any meaningful way, AFAIK, is English anyway. It would be interesting to see if we could use this as an opportunity to make it easier for the manual to get translated along the way. It's currently a gigantic pain in the ass. As far as that goes, I wouldn't much object to converting the whole thing to HTML or something, and doing away with the Docbook. Docbook can offer lots of cool things, but we don't really care, and it's off-putting. We've recently had two different people run away screaming after looking at the source for the manual.

I totally agree about losing Docbook. (Chris)

And then (Michael) we have the complication of someone having taken it under her wing to face lift the thing, and who has agreed to take it upon herself (Shelagh) to wrestle with the evil Docbook syntax for the furtherment of all humankind and stuff. If KDE4 uses Docbook still, we might ought to just leave well enough alone. (Shelagh) Hey I don't care what you use for the manual, I'd still be happy to do the documentation whatever evil markup you decide to use.

I should check into this. I currently have KDE4 installed and runnable, but I'm only looking at the -4 apps from my -3 desktop, rather than actually suffering with the whole experience. (And many of the apps, if not most, are totally broken in this environment, incidentally.) (I haven't tried the 4.1 beta yet either. I think it's 4.1 isn't it? I'm waiting for it to get out of beta before I mess with it at all.)

I have to say this entire overriding issue is almost enough to send me to Windows or, to Guillaume's great delight, OS-X at this point. Of course I'm still here because I took a 66% pay cut and I'm having to liquidate my life to try to keep out of bankruptcy court, so on the other hand, beer free software is good software.

Breakdown of individual classes

The notes here contain discussion about how our uses of these classes might be ported to “pure” Qt4. Generally I'm not mentioning porting to KDE4 except in cases where “pure” Qt4 looks hard to do.

It may be worth aiming at “mostly Qt4 and cutting back our dependency on KDE a bit”, rather than going either “pure Qt4” or “the whole hog with KDE4”.

Note that Trolltech took the very sensible decision to change the function name or make an incompatible prototype for each function whose behaviour had significantly changed between Qt3 and 4. This means you can easily identify the code you need to work on because it doesn't compile; there's very little risk of having it compile and then later discovering that doesn't work.

ScriptableClassNotes
YesKAboutDataThe About box is arguably too KDE-specific for us anyway. We could easily generate a formatted string from the same data.
KAction, KActionCollection, KActionMenuQt4's QAction is comparably powerful, but lacks the XML GUI setup logic that we use, and is not readily converted to from KDE3 KAction code. This may be a point in favour of KDE4.
KApplicationGenerally, use QApplication
KCmdLineArgs
KColorDialogUse QColorDialog – but it isn't great and the KDE4 equivalent is likely to be better
YesKComboBoxQComboBox
YesKCommand, KMacroCommand, KNamedCommandUse our own classes. These are trivial classes and I have suitable replacements already in SV.
KConfigQSettings does everything KConfig does, but the API is different, and our use of KConfig is quite tricksy in various places – this is not likely to be completely scriptable. If KDE4 has a KConfig that is exactly compatible with KDE3's, that might be the answer; but if it's a bit different, it could even be more difficult to move to than the totally different QSettings (because of the risk of getting it compiling and then finding the subtle bugs later). Update: KConfig does have structural changes, to remove the state from the KConfig object. It's probably six and two threes which is simpler, then.
KDialog, KDialogBaseQDialog provides much of the same stuff, including intelligent allocation of OK/Cancel etc buttons. The API is significantly different though
KDockMainWindow, KDockWidgetI don't know. This could be tricky. I fear porting the main window.
KFileDialogQFileDialog. The KDE version is likely to be much nicer (at least for many users) than the plain Qt4 one though. Even if we go plain Qt4, it would be nice to have KDE enhancements as an option…
KFontRequesterQFontDialog
KGlobal, KGlobalSettings
KIconQIcon
KLineEdit
KLineEditDlg
KListView
KListViewItem
KMainWindow
KMessageBox
PartiallyKProcess Changed from KDE3 and QProcess has changed from QT3. Port to QT4, but this looks like it can only be partially mechanized.
KProgress
KProgressDialog
KRadioAction
KRecentFilesAction
KSqueezedTextLabel
KStartupLogo
KStatusBar
KStdAccel
KStdAction
KStyle
KTabWidget
KTempFile
KTipDialogMichael and I agree that this doesn't really add anything. Perhaps we should just remove it. (Michael: Less for the translators to deal with that way too. Though an argument the other way is that the tips are actually the only documentation that exists in several target languages.)
KTmpStatusMsg
KToggleAction
KToolBar
KToolBarPopupAction
KToolBarSeparator
KUniqueApplication
KURL
QAccel
QApplication
QBitmap
QBoxLayout
QBrush
QButton
QButtonGroup
QByteArray
QCheckBox
QCheckListItem
QCheckTableItem
QCloseEvent
QColor
QColorGroup
QComboBox
QComboTableItem
QContextMenuEvent
QCursor
QCustomEvent
QDataStream
QDate
QDateTime
QDeferScrollView
QDialog
QDir
QDragEnterEvent
QDropEvent
QEvent
QFile
QFileInfo
QFont
QFontMetrics
QFrame
QFrames
QGrid
QGridLayout
QGroupBox
QHeader
QHideEvent
QHttp
QHttpResponseHeader
QIconSet
QImage
QLabel
QLine
QLineEdit
QList
QListBox
QListBoxItem
QListView
QListViewItem
QMap
QMenuItem
QMouseEvent
QMutex
QMutexLocker
QName
QObject
QObjectList
QPaintDevice
QPainter
QPaintEvent
QPalette
QPen
QPixmap
QPoint
QPointArray
QPopupMenu
QPtrList
QPtrListIterator
QPushButton
QRadioButton
QRect
NoQRegExpWe need to replace search() with indexIn(), which is a simple swap in four files, but this conflicts with QString::search() among possible others, and it would take more effort to try to script this than it would to just go make the four changes by hand.
QRegion
QResizeEvent
QRgb
QScrollBar
QScrollView
QSessionManager
QSettings
QShowEvent
QSignalMapper
QSize
QSizePolicy
QSlider
QSocketNotifier
QSpacerItem
QSpinBox
QSpinWidget
QString
QStringList
QStrList
QStyle
QStyleOption
QTabBar
QTable
QTableItem
QTabWidget
QTextCodec
QTextDrag
QTextEdit
QTextIStream
QTextOStream
QTextStream
QThread
QTime
QTimer
QToolBar
QToolBarExtensionWidget
QToolButton
QToolTip
QUriDrag
QValueList
YesQValueVectorChanges to QVector in QT4, with syntactical changes that don't seem to affect us
QWheelEvent
QWidget
QWidgetItem
QWidgetStack
QXmlAttributes
QXmlDefaultHandler
QXmlInputSource
QXmlParseException
QXmlSimpleReader
 
 
dev/notes_on_porting_to_qt4.txt · Last modified: 2022/05/06 16:07 (external edit)
Recent changes RSS feed Creative Commons License Valid XHTML 1.0 Valid CSS Driven by DokuWiki