I frequently write computer programs with graphical user interfaces ("GUI"s). I insist that the interfaces look good on Windows, Mac, and Linux computers. By "good", I mean that the widgets (the buttons, sliders, and what-not), look exactly like those found on most other applications developed specifically for that particular platform. For example, buttons and progress bars on Mac must have that clear blue "Aqua" look.
There are several programming tool kits which help to create native-looking user interfaces on multiple platforms. The three platforms I pay particular attention to are Windows, Mac, and Linux. Cross-platform GUI tool kits include wxWidgets, Tk, and Java Swing. This post documents the failure of wxWidgets and Java Swing to respect Windows font sizes.
Look at the following picture to see the failure of wx and Java to respect the Windows font sizes. From left to right, the test programs are in Visual Basic, python/Tk, python/wx, and Java Swing.
wxWidgets looks nice in some cases, but it has some ways to go to support native look and feel on Windows. I am working on several Windows XP systems, on which I routinely select "Large Fonts" in my desktop preferences. wxWidgets does not respect those preferences.
To see the difference, first set extra large fonts on your desktop:
Far click desktop -> Properties -> Appearance -> Font Size -> Extra Large Fonts
Next, write an application using wxWidgets and test whether it respects your font choice. I didn't think so.
If it's any consolation, Java doesn't respect the Windows font size either.
If you want to use a cross-platform widget tool kit, and your definition of "cross-platform" includes Windows, my recommendation is to use Tk 8.5.
The table below summarizes the results for the four test programs I wrote:
Tool Kit | Native look-and-feel? | Respects font size? |
---|---|---|
Visual basic | No(!) | Yes |
Tk 8.5 | Yes | Yes |
wx 2.8.10 | Yes | No |
Java 1.6.0 | Yes | No |
Below are the test programs I wrote to create the windows shown at the beginning of this post.
- Visual Basic
' "Hello, World!" program in Visual Basic.
Module Hello
Sub Main()
MsgBox("Hello, World! (VB)") ' Display message on computer screen.
End Sub
End Module - Tk 8.5 (tkinter in python 3.1)
# Note - requires python 3.1 for ttk 8.5 support
import tkinter as tk
import tkinter.ttk as ttk
root = tk.Tk()
padding = 10
panel = ttk.Frame(root, padding=padding).pack()
label = ttk.Label(panel, text="Hello, World! (Tk)")
label.pack(padx=padding, pady=padding)
button = ttk.Button(panel, text="Hello", default="active")
button.pack(padx=padding, pady=padding)
root.mainloop() - wx 2.8.10 (in python 2.6 with wxpython)
import wx
padding = 10
app = wx.App(0)
frame = wx.Frame(None, -1, "Hello")
panel = wx.Panel(frame)
sizer = wx.BoxSizer(wx.VERTICAL)
panel.SetSizer(sizer)
text = wx.StaticText(panel, -1, "Hello, World! (wx)")
sizer.Add(text, 0, wx.ALL, padding)
button = wx.Button(panel, -1, "Hello")
sizer.Add(button, 0, wx.ALL, padding)
frame.Centre()
frame.Show(True)
app.MainLoop() - Java swing 1.6.0
import javax.swing.*;
import java.awt.Dimension;
public class HelloWorldFrame extends JFrame
{
public static void main(String args[])
{
new HelloWorldFrame();
}
HelloWorldFrame()
{
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch(Exception e) {}
JPanel panel = new JPanel();
add(panel);
panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS));
panel.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
JLabel label = new JLabel("Hello, World! (java)");
panel.add(label);
panel.add(Box.createRigidArea(new Dimension(0, 10)));
JButton button = new JButton("Hello");
panel.add(button);
pack();
setVisible(true);
}
}
The Tk source code that sets the windows correctly appears to be near line 418 of file win/tkWinFont.c in the Tk source code:
if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS,
sizeof(ncMetrics), &ncMetrics, 0)) {
CreateNamedSystemLogFont(interp, tkwin, "TkDefaultFont",
&ncMetrics.lfMessageFont);
CreateNamedSystemLogFont(interp, tkwin, "TkHeadingFont",
&ncMetrics.lfMessageFont);
CreateNamedSystemLogFont(interp, tkwin, "TkTextFont",
&ncMetrics.lfMessageFont);
CreateNamedSystemLogFont(interp, tkwin, "TkMenuFont",
&ncMetrics.lfMenuFont);
CreateNamedSystemLogFont(interp, tkwin, "TkTooltipFont",
&ncMetrics.lfStatusFont);
CreateNamedSystemLogFont(interp, tkwin, "TkCaptionFont",
&ncMetrics.lfCaptionFont);
CreateNamedSystemLogFont(interp, tkwin, "TkSmallCaptionFont",
&ncMetrics.lfSmCaptionFont);
}
The wx source code has similar code in a few locations. But it appears that this technique may be only used for menu fonts and message dialog fonts.
The main problem might be that the method wxGetCCDefaultFont() in the wx source code uses SPI_GETINCONTITLELOGFONT instead of SPI_GETNONCLIENTMETRICS.
Microsoft has documentation for the NONCLIENTMETRICS data structure.
Even if the wx authors fix this today, I fear it will be a long time before the change trickles down into a wxPython release.