GroupBox has always been problematic layout wise, especially when included in complex widgets with nested child layout levels, such as TabBox. All of these problems reflect bugs in their Qt counterpart classes (QGroupBox and QTabWidget, respectively), along with platform idiosyncrasies.
Try this version of your script:
// Win7 1123: Dialog.adjustToContents Edit vertical layout issue
// Win7 1171: Dialog.adjustToContents Edit vertical layout issue
// Win7 1171: GroupBox title vertical layout issue
// Win7 1171: ComboBox menu item layout issue
#include <pjsr/ColorSpace.jsh>
#include <pjsr/DataType.jsh>
#include <pjsr/FontFamily.jsh>
#include <pjsr/FrameStyle.jsh>
#include <pjsr/PenStyle.jsh>
#include <pjsr/SampleType.jsh>
#include <pjsr/Sizer.jsh>
#include <pjsr/StdButton.jsh>
#include <pjsr/StdIcon.jsh>
#include <pjsr/TextAlign.jsh>
#include <pjsr/UndoFlag.jsh>
#define TITLE "TabGroupBox3"
if (!Control.prototype.setScaledMinWidth) {
Control.prototype.setScaledMinWidth = function(w) {
this.setMinWidth(w);
};
}
if (!Control.prototype.setScaledMinSize) {
Control.prototype.setScaledMinSize = function(w, h) {
this.setMinSize(w, h);
};
}
function Tab1View(parent) {
this.__base__ = Frame;
this.__base__(parent);
this.addGroupBox = function(title) {
var groupBox = new GroupBox(this);
this.sizer.add(groupBox);
groupBox.sizer = new VerticalSizer;
groupBox.sizer.margin = 6;
groupBox.sizer.spacing = 6;
groupBox.title = title;
groupBox.styleSheet = "{}"; /* ### */
return groupBox;
};
this.addPane = function(group) {
var buttonPane = new HorizontalSizer;
buttonPane.spacing = 6;
group.sizer.add(buttonPane);
return buttonPane;
};
this.addTreeBox = function(group, rows, paths, fullPaths) {
var treeBox = new TreeBox(this);
group.sizer.add(treeBox);
treeBox.alternateRowColor = true;
treeBox.headerVisible = false;
treeBox.horizontalScrollBarVisible = true;
treeBox.indentSize = 0;
treeBox.multipleSelection = true;
treeBox.numberOfColumns = 2;
treeBox.setHeaderAlignment(0, TextAlign_Left | TextAlign_VertCenter);
//console.writeln("lineSpacing: ", treeBox.font.lineSpacing);
//console.writeln("borderWidth: ", treeBox.borderWidth);
treeBox.setFixedHeight(
this.displayPixelRatio * (rows + 1) * (treeBox.font.lineSpacing + 6) +
treeBox.borderWidth
);
treeBox.showColumn(1, false);
for (var i = 0; i < paths.length; ++i) {
var node = new TreeBoxNode(treeBox);
node.selectable = rows != 1;
node.setText(0, fullPaths ? paths[i] : File.extractNameAndExtension(paths[i]));
}
treeBox.adjustColumnWidthToContents(0);
return treeBox;
};
this.addPushButton = function(pane, text, toolTip, onClick) {
var pushButton = new PushButton(this);
pane.add(pushButton);
pushButton.text = text;
pushButton.toolTip = toolTip;
pushButton.onClick = onClick;
return pushButton;
};
this.addLabel = function(pane, text, toolTip) {
var label = new Label(this);
pane.add(label);
label.setFixedWidth(this.labelWidth);
label.text = text;
label.toolTip = toolTip;
label.textAlignment = TextAlign_Right | TextAlign_VertCenter;
return label;
};
this.addEdit = function(pane, text, toolTip, onTextUpdated, onEditCompleted) {
var edit = new Edit(this);
pane.add(edit);
edit.setFixedWidth(this.editWidth);
edit.text = text;
edit.toolTip = toolTip;
edit.onTextUpdated = onTextUpdated;
edit.onEditCompleted = onEditCompleted;
return edit;
};
this.addUnit = function(pane, text) {
var label = new Label(this);
pane.add(label);
label.setFixedWidth(this.unitWidth);
label.text = text;
label.textAlignment = TextAlign_Left | TextAlign_VertCenter;
return label;
};
this.addComboBox = function(pane, items, currentItem, toolTip, onItemSelected) {
var comboBox = new ComboBox(this);
pane.add(comboBox);
for (var i = 0; i != items.length; ++i) {
comboBox.addItem(items[i]);
}
comboBox.currentItem = currentItem;
comboBox.toolTip = toolTip;
comboBox.onItemSelected = onItemSelected;
return comboBox;
};
this.sizer = new VerticalSizer();
this.sizer.margin = 6;
this.sizer.spacing = 6;
this.labelWidth = this.parent.font.width("Label 0:");
this.editWidth = this.parent.font.width("00000000000");
this.unitWidth = this.parent.font.width("unit");
{
this.group1Box = this.addGroupBox("Group1:");
this.button1Pane = this.addPane(this.group1Box);
this.observationWavelengthTypicalValuesComboBox = this.addComboBox(
this.button1Pane,
["Alpha", "Beta", "Gamma", "Delta", "Epsilon", "Zeta", "Eta", "Theta"],
0,
"<p></p>",
function(item) {}
);
this.button1Pane.addStretch();
}
{
this.group1Box = this.addGroupBox("Group2:");
}
{
this.group1Box = this.addGroupBox("Group3:");
}
this.sizer.addStretch();
}
Tab1View.prototype = new Frame;
function Tab2View(parent) {
this.__base__ = Frame;
this.__base__(parent);
this.addGroupBox = function(title) {
var groupBox = new GroupBox(this);
this.sizer.add(groupBox);
groupBox.sizer = new VerticalSizer;
groupBox.sizer.margin = 6;
groupBox.sizer.spacing = 6;
groupBox.title = title;
groupBox.styleSheet = "{}"; /* ### */
return groupBox;
};
this.addPane = function(group) {
var buttonPane = new HorizontalSizer;
buttonPane.spacing = 6;
group.sizer.add(buttonPane);
return buttonPane;
};
this.addTreeBox = function(group, rows, paths, fullPaths) {
var treeBox = new TreeBox(this);
group.sizer.add(treeBox);
treeBox.alternateRowColor = true;
treeBox.headerVisible = false;
treeBox.horizontalScrollBarVisible = true;
treeBox.indentSize = 0;
treeBox.multipleSelection = true;
treeBox.numberOfColumns = 2;
treeBox.setHeaderAlignment(0, TextAlign_Left | TextAlign_VertCenter);
//console.writeln("lineSpacing: ", treeBox.font.lineSpacing);
//console.writeln("borderWidth: ", treeBox.borderWidth);
treeBox.setFixedHeight(
this.displayPixelRatio * (rows + 1) * (treeBox.font.lineSpacing + 6) +
treeBox.borderWidth
);
treeBox.showColumn(1, false);
for (var i = 0; i < paths.length; ++i) {
var node = new TreeBoxNode(treeBox);
node.selectable = rows != 1;
node.setText(0, fullPaths ? paths[i] : File.extractNameAndExtension(paths[i]));
}
treeBox.adjustColumnWidthToContents(0);
return treeBox;
};
this.addPushButton = function(pane, text, toolTip, onClick) {
var pushButton = new PushButton(this);
pane.add(pushButton);
pushButton.text = text;
pushButton.toolTip = toolTip;
pushButton.onClick = onClick;
return pushButton;
};
this.addLabel = function(pane, text, toolTip) {
var label = new Label(this);
pane.add(label);
label.setFixedWidth(this.labelWidth);
label.text = text;
label.toolTip = toolTip;
label.textAlignment = TextAlign_Right | TextAlign_VertCenter;
return label;
};
this.addEdit = function(pane, text, toolTip, onTextUpdated, onEditCompleted) {
var edit = new Edit(this);
pane.add(edit);
edit.setFixedWidth(this.editWidth);
edit.text = text;
edit.toolTip = toolTip;
edit.onTextUpdated = onTextUpdated;
edit.onEditCompleted = onEditCompleted;
return edit;
};
this.addUnit = function(pane, text) {
var label = new Label(this);
pane.add(label);
label.setFixedWidth(this.unitWidth);
label.text = text;
label.textAlignment = TextAlign_Left | TextAlign_VertCenter;
return label;
};
this.addComboBox = function(pane, items, currentItem, toolTip, onItemSelected) {
var comboBox = new ComboBox(this);
pane.add(comboBox);
for (var i = 0; i != items.length; ++i) {
comboBox.addItem(items[i]);
}
comboBox.currentItem = currentItem;
comboBox.toolTip = toolTip;
comboBox.onItemSelected = onItemSelected;
return comboBox;
};
this.sizer = new VerticalSizer();
this.sizer.margin = 6;
this.sizer.spacing = 6;
this.labelWidth = this.parent.font.width("Label 0:");
this.editWidth = this.parent.font.width("00000000000");
this.unitWidth = this.parent.font.width("unit");
{
this.group1Box = this.addGroupBox("Group1:");
this.button1Pane = this.addPane(this.group1Box);
this.observationWavelengthTypicalValuesComboBox = this.addComboBox(
this.button1Pane,
["Alpha", "Beta", "Gamma", "Delta", "Epsilon", "Zeta", "Eta", "Theta"],
0,
"<p></p>",
function(item) {}
);
this.button1Pane.addStretch();
}
{
this.group1Box = this.addGroupBox("Group2:");
}
this.sizer.addStretch();
}
Tab2View.prototype = new Frame;
function MainView(model, controller) {
this.__base__ = Dialog;
this.__base__();
this.addPane = function(group) {
var buttonPane = new HorizontalSizer;
buttonPane.spacing = 6;
group.sizer.add(buttonPane);
return buttonPane;
};
this.addToolButtonMousePress = function(pane, icon, toolTip, onMousePress) {
var toolButton = new ToolButton(this);
pane.add(toolButton);
toolButton.icon = this.scaledResource(icon);
toolButton.setScaledFixedSize(20, 20);
toolButton.toolTip = toolTip;
toolButton.onMousePress = onMousePress;
return toolButton;
};
this.addToolButton = function(pane, icon, toolTip, onClick) {
var toolButton = new ToolButton(this);
pane.add(toolButton);
toolButton.icon = this.scaledResource(icon);
toolButton.setScaledFixedSize(20, 20);
toolButton.toolTip = toolTip;
toolButton.onClick = onClick;
return toolButton;
};
this.addPushButton = function(pane, text, toolTip, onClick) {
var pushButton = new PushButton(this);
pane.add(pushButton);
pushButton.text = text;
pushButton.toolTip = toolTip;
pushButton.onClick = onClick;
return pushButton;
};
this.sizer = new VerticalSizer;
this.sizer.margin = 6;
this.sizer.spacing = 6;
{
this.tabBox = new TabBox(this);
this.sizer.add(this.tabBox);
this.tab1View = new Tab1View(this);
this.tabBox.addPage(this.tab1View, "Tab1");
this.tab2View = new Tab2View(this);
this.tabBox.addPage(this.tab2View, "Tab2");
}
{
this.buttonPane = this.addPane(this);
this.newInstanceButton = this.addToolButtonMousePress(
this.buttonPane,
":/process-interface/new-instance.png",
"<p>Create a new instance.</p>",
function() {
this.hasFocus = true;
this.pushed = false;
this.dialog.newInstance();
}
);
this.browseDocumentationButton = this.addToolButton(
this.buttonPane,
":/process-interface/browse-documentation.png",
"<p>Open a browser to view documentation.</p>",
function() {
if (!Dialog.browseScriptDocumentation(TITLE)) {
(new MessageBox(
"<p>Documentation has not been installed.</p>",
TITLE,
StdIcon_Warning,
StdButton_Ok
)).execute();
}
}
);
this.buttonPane.addStretch();
this.dismissButton = this.addPushButton(
this.buttonPane,
"Dismiss",
"<p>Dismiss the dialog.</p>",
function() {
this.dialog.ok();
}
);
this.dismissButton.defaultButton = true;
}
this.windowTitle = TITLE;
this.adjustToContents();
this.setScaledMinWidth(300);
//this.setScaledMinSize(300, 300);
this.setFixedHeight( this.height + this.logicalPixelsToPhysical( 4 ) ); /* ### */
}
MainView.prototype = new Dialog;
function main() {
console.hide();
var mainView = new MainView();
mainView.execute();
}
main();
where you'll find three modifications marked with /* ### */ comments. There are two modifications for GroupBox and One for the dialog.
For some reason that eludes me completely, GroupBox is not applying the application style sheet in this script (see the /rsc/qss/core-standard.qss style sheet file in your PI distribution). This is very strange and obviously a Qt bug, probably caused by the fact that these QGroupBox widgets are grandchildren of a QTabWidget. To fix this problem I have added this line to your addGroupBox functions:
groupBox.styleSheet = "{}";
This simple assignment forces a complete recalculation of internal style properties and reapplies the application style sheet. Note that an empty string does not work because it is "optimized out" by Qt, hence the pair of curly brackets are necessary to represent an empty style.
The second change is this one:
this.setFixedHeight( this.height + this.logicalPixelsToPhysical( 4 ) );
which replaces the usual call to this.setFixedHeight(). The four extra (logical) pixels are sufficient to "embed" the layout inaccuracies caused by the mix of TabBox and GroupBox controls.
On the Linux machine where I'm writing this, these changes fix all layout problems exposed by this script. Let me know if they work also on your Windows 7.
The solutions that I've found with this test script can be implemented in native code transparently. I still have to make tests on all platforms, but I am quite confident that they will work reliably. If this is the case, these improvements will be included in the next version.