mirror of
https://github.com/mappu/miqt.git
synced 2024-12-22 08:58:37 +00:00
commit
3623a3dad1
3
.gitignore
vendored
3
.gitignore
vendored
@ -9,6 +9,7 @@ cmd/handbindings/handbindings
|
|||||||
cmd/handbindings/bindings_test/direct
|
cmd/handbindings/bindings_test/direct
|
||||||
cmd/handbindings/bindings_test/testapp
|
cmd/handbindings/bindings_test/testapp
|
||||||
cmd/genbindings/genbindings
|
cmd/genbindings/genbindings
|
||||||
|
cmd/miqt-uic/miqt-uic
|
||||||
|
|
||||||
examples/helloworld/helloworld
|
examples/helloworld/helloworld
|
||||||
examples/helloworld/helloworld.exe
|
examples/helloworld/helloworld.exe
|
||||||
@ -16,6 +17,8 @@ examples/mdoutliner/mdoutliner
|
|||||||
examples/mdoutliner/mdoutliner.exe
|
examples/mdoutliner/mdoutliner.exe
|
||||||
examples/windowsmanifest/windowsmanifest
|
examples/windowsmanifest/windowsmanifest
|
||||||
examples/windowsmanifest/windowsmanifest.exe
|
examples/windowsmanifest/windowsmanifest.exe
|
||||||
|
examples/uidesigner/uidesigner
|
||||||
|
examples/uidesigner/uidesigner.exe
|
||||||
|
|
||||||
# android temporary build files
|
# android temporary build files
|
||||||
android-build
|
android-build
|
||||||
|
@ -77,6 +77,8 @@ The `connect(sourceObject, sourceSignal, targetObject, targetSlot)` is projected
|
|||||||
|
|
||||||
Qt class inherited types are projected as a Go embedded struct. For example, to pass a `var myLabel *qt.QLabel` to a function taking only the `*qt.QWidget` base class, write `myLabel.QWidget`.
|
Qt class inherited types are projected as a Go embedded struct. For example, to pass a `var myLabel *qt.QLabel` to a function taking only the `*qt.QWidget` base class, write `myLabel.QWidget`.
|
||||||
|
|
||||||
|
- When a Qt subclass adds a method overload (e.g. `QMenu::addAction(QString)` vs `QWidget::addAction(QAction*)`), the base class version is shadowed and can only be called via `myQMenu.QWidget.AddAction(QAction*)`.
|
||||||
|
|
||||||
Some C++ idioms that were difficult to project were omitted from the binding. But, this can be improved in the future.
|
Some C++ idioms that were difficult to project were omitted from the binding. But, this can be improved in the future.
|
||||||
|
|
||||||
### Q7. How can I cross-compile for Windows from a Linux host OS?
|
### Q7. How can I cross-compile for Windows from a Linux host OS?
|
||||||
|
16
cmd/miqt-uic/README.md
Normal file
16
cmd/miqt-uic/README.md
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# miqt-uic
|
||||||
|
|
||||||
|
The miqt-uic program compiles Qt Designer `.ui` files into MIQT `.go` files.
|
||||||
|
|
||||||
|
For usage information, see the `examples/uidesigner` folder.
|
||||||
|
|
||||||
|
## Architecture design
|
||||||
|
|
||||||
|
1. Parse xml type definitions
|
||||||
|
2. Recursively walk and emit Go code.
|
||||||
|
|
||||||
|
When developing `miqt-uic`, it's useful to run Qt `uic` side-by-side, and compare the output of each program for missing attributes or assignments.
|
||||||
|
|
||||||
|
There is a hardcoded list of known MIQT constructor functions taking single `parent *qt.QWidget` argument.
|
||||||
|
- A bash function to regenerate this list is included in `constructors.go`. It should be re-run if MIQT is updated.
|
||||||
|
- "Promoted Widget" will result in no matching found constructor function (current known issue).
|
166
cmd/miqt-uic/constructors.go
Normal file
166
cmd/miqt-uic/constructors.go
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
func constructorFunctionFor(className string) (string, bool) {
|
||||||
|
|
||||||
|
// Rebuild this list via:
|
||||||
|
// grep -PRoh 'func New.+\(parent \*QWidget\)' ~/dev/miqt/qt/ | sed -re 's~^func New([^0-9]+)([0-9]*)\(.*~case "\1": return "New\1\2", true~'
|
||||||
|
|
||||||
|
switch className {
|
||||||
|
|
||||||
|
// CODEGENERATED LIST START
|
||||||
|
|
||||||
|
case "QListWidget":
|
||||||
|
return "NewQListWidget2", true
|
||||||
|
case "QAbstractSpinBox":
|
||||||
|
return "NewQAbstractSpinBox2", true
|
||||||
|
case "QStackedLayout":
|
||||||
|
return "NewQStackedLayout2", true
|
||||||
|
case "QColumnView":
|
||||||
|
return "NewQColumnView2", true
|
||||||
|
case "QProgressDialog":
|
||||||
|
return "NewQProgressDialog3", true
|
||||||
|
case "QTabWidget":
|
||||||
|
return "NewQTabWidget2", true
|
||||||
|
case "QLabel":
|
||||||
|
return "NewQLabel3", true
|
||||||
|
case "QKeySequenceEdit":
|
||||||
|
return "NewQKeySequenceEdit3", true
|
||||||
|
case "QDockWidget":
|
||||||
|
return "NewQDockWidget5", true
|
||||||
|
case "QFontComboBox":
|
||||||
|
return "NewQFontComboBox2", true
|
||||||
|
case "QTreeView":
|
||||||
|
return "NewQTreeView2", true
|
||||||
|
case "QCalendarWidget":
|
||||||
|
return "NewQCalendarWidget2", true
|
||||||
|
case "QLineEdit":
|
||||||
|
return "NewQLineEdit3", true
|
||||||
|
case "QMenuBar":
|
||||||
|
return "NewQMenuBar2", true
|
||||||
|
case "QFrame":
|
||||||
|
return "NewQFrame2", true
|
||||||
|
case "QAbstractScrollArea":
|
||||||
|
return "NewQAbstractScrollArea2", true
|
||||||
|
case "QSplitter":
|
||||||
|
return "NewQSplitter3", true
|
||||||
|
case "QStackedWidget":
|
||||||
|
return "NewQStackedWidget2", true
|
||||||
|
case "QWizard":
|
||||||
|
return "NewQWizard2", true
|
||||||
|
case "QWizardPage":
|
||||||
|
return "NewQWizardPage2", true
|
||||||
|
case "QMdiSubWindow":
|
||||||
|
return "NewQMdiSubWindow2", true
|
||||||
|
case "QStatusBar":
|
||||||
|
return "NewQStatusBar2", true
|
||||||
|
case "QToolButton":
|
||||||
|
return "NewQToolButton2", true
|
||||||
|
case "QShortcut":
|
||||||
|
return "NewQShortcut", true
|
||||||
|
case "QSlider":
|
||||||
|
return "NewQSlider3", true
|
||||||
|
case "QComboBox":
|
||||||
|
return "NewQComboBox2", true
|
||||||
|
case "QScrollBar":
|
||||||
|
return "NewQScrollBar3", true
|
||||||
|
case "QTabBar":
|
||||||
|
return "NewQTabBar2", true
|
||||||
|
case "QTextBrowser":
|
||||||
|
return "NewQTextBrowser2", true
|
||||||
|
case "QTreeWidget":
|
||||||
|
return "NewQTreeWidget2", true
|
||||||
|
case "QDialog":
|
||||||
|
return "NewQDialog2", true
|
||||||
|
case "QFormLayout":
|
||||||
|
return "NewQFormLayout2", true
|
||||||
|
case "QToolBar":
|
||||||
|
return "NewQToolBar4", true
|
||||||
|
case "QWidget":
|
||||||
|
return "NewQWidget2", true
|
||||||
|
case "QRadioButton":
|
||||||
|
return "NewQRadioButton3", true
|
||||||
|
case "QCheckBox":
|
||||||
|
return "NewQCheckBox3", true
|
||||||
|
case "QSizeGrip":
|
||||||
|
return "NewQSizeGrip", true
|
||||||
|
case "QLCDNumber":
|
||||||
|
return "NewQLCDNumber3", true
|
||||||
|
case "QFileDialog":
|
||||||
|
return "NewQFileDialog3", true
|
||||||
|
case "QUndoView":
|
||||||
|
return "NewQUndoView4", true
|
||||||
|
case "QGraphicsView":
|
||||||
|
return "NewQGraphicsView3", true
|
||||||
|
case "QPushButton":
|
||||||
|
return "NewQPushButton4", true
|
||||||
|
case "QColorDialog":
|
||||||
|
return "NewQColorDialog3", true
|
||||||
|
case "QMessageBox":
|
||||||
|
return "NewQMessageBox4", true
|
||||||
|
case "QSplashScreen":
|
||||||
|
return "NewQSplashScreen3", true
|
||||||
|
case "QErrorMessage":
|
||||||
|
return "NewQErrorMessage2", true
|
||||||
|
case "QListView":
|
||||||
|
return "NewQListView2", true
|
||||||
|
case "QDateTimeEdit":
|
||||||
|
return "NewQDateTimeEdit5", true
|
||||||
|
case "QTimeEdit":
|
||||||
|
return "NewQTimeEdit3", true
|
||||||
|
case "QDateEdit":
|
||||||
|
return "NewQDateEdit3", true
|
||||||
|
case "QMenu":
|
||||||
|
return "NewQMenu3", true
|
||||||
|
case "QToolBox":
|
||||||
|
return "NewQToolBox2", true
|
||||||
|
case "QTableWidget":
|
||||||
|
return "NewQTableWidget3", true
|
||||||
|
case "QFocusFrame":
|
||||||
|
return "NewQFocusFrame2", true
|
||||||
|
case "QHBoxLayout":
|
||||||
|
return "NewQHBoxLayout2", true
|
||||||
|
case "QVBoxLayout":
|
||||||
|
return "NewQVBoxLayout2", true
|
||||||
|
case "QInputDialog":
|
||||||
|
return "NewQInputDialog2", true
|
||||||
|
case "QTableView":
|
||||||
|
return "NewQTableView2", true
|
||||||
|
case "QMdiArea":
|
||||||
|
return "NewQMdiArea2", true
|
||||||
|
case "QSpinBox":
|
||||||
|
return "NewQSpinBox2", true
|
||||||
|
case "QDoubleSpinBox":
|
||||||
|
return "NewQDoubleSpinBox2", true
|
||||||
|
case "QProgressBar":
|
||||||
|
return "NewQProgressBar2", true
|
||||||
|
case "QTextEdit":
|
||||||
|
return "NewQTextEdit3", true
|
||||||
|
case "QAbstractSlider":
|
||||||
|
return "NewQAbstractSlider2", true
|
||||||
|
case "QDialogButtonBox":
|
||||||
|
return "NewQDialogButtonBox5", true
|
||||||
|
case "QFontDialog":
|
||||||
|
return "NewQFontDialog3", true
|
||||||
|
case "QMainWindow":
|
||||||
|
return "NewQMainWindow2", true
|
||||||
|
case "QCommandLinkButton":
|
||||||
|
return "NewQCommandLinkButton4", true
|
||||||
|
case "QDial":
|
||||||
|
return "NewQDial2", true
|
||||||
|
case "QGridLayout":
|
||||||
|
return "NewQGridLayout", true
|
||||||
|
case "QPlainTextEdit":
|
||||||
|
return "NewQPlainTextEdit3", true
|
||||||
|
case "QScrollArea":
|
||||||
|
return "NewQScrollArea2", true
|
||||||
|
case "QGroupBox":
|
||||||
|
return "NewQGroupBox3", true
|
||||||
|
|
||||||
|
// CODEGENERATED LIST END
|
||||||
|
|
||||||
|
default:
|
||||||
|
return "", false
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
49
cmd/miqt-uic/main.go
Normal file
49
cmd/miqt-uic/main.go
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
inFile := flag.String("InFile", "", "Input .ui file")
|
||||||
|
outFile := flag.String("OutFile", "-", "Output .go file, or - for stdout")
|
||||||
|
packageName := flag.String("Package", "main", "Custom package name")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
if *inFile == "" {
|
||||||
|
flag.Usage()
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
inXml, err := ioutil.ReadFile(*inFile)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var parsed UiFile
|
||||||
|
err = xml.Unmarshal(inXml, &parsed)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
gosrc, err := generate(*packageName, strings.Join(os.Args[1:], " "), parsed)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if *outFile == "-" {
|
||||||
|
fmt.Println(string(gosrc))
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
err = ioutil.WriteFile(*outFile, gosrc, 0644)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
82
cmd/miqt-uic/types.go
Normal file
82
cmd/miqt-uic/types.go
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UiLayoutItem struct {
|
||||||
|
Row *int `xml:"row,attr"`
|
||||||
|
Column *int `xml:"column,attr"`
|
||||||
|
Widget UiWidget `xml:"widget"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UiLayout struct {
|
||||||
|
Class string `xml:"class,attr"`
|
||||||
|
Name string `xml:"name,attr"`
|
||||||
|
Items []UiLayoutItem `xml:"item"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UiPropertyContainer struct {
|
||||||
|
Properties []UiProperty `xml:"property"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UiWidget struct {
|
||||||
|
Class string `xml:"class,attr"`
|
||||||
|
Name string `xml:"name,attr"`
|
||||||
|
Properties []UiProperty `xml:"property"`
|
||||||
|
Attributes []UiProperty `xml:"attribute"`
|
||||||
|
|
||||||
|
Layout *UiLayout `xml:"layout,omitempty"`
|
||||||
|
Widgets []UiWidget `xml:"widget"` // If no layout
|
||||||
|
|
||||||
|
Columns []UiPropertyContainer `xml:"column"` // e.g. for QTreeWidget
|
||||||
|
Items []UiPropertyContainer `xml:"item"` // e.g. for QComboBox
|
||||||
|
|
||||||
|
AddActions []UiActionReference `xml:"addaction"`
|
||||||
|
Actions []UiAction `xml:"action"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UiRect struct {
|
||||||
|
X int `xml:"x"`
|
||||||
|
Y int `xml:"y"`
|
||||||
|
Width int `xml:"width"`
|
||||||
|
Height int `xml:"height"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UiString struct {
|
||||||
|
Value string `xml:",chardata"`
|
||||||
|
Notr bool `xml:"notr,attr,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UiProperty struct {
|
||||||
|
Name string `xml:"name,attr"`
|
||||||
|
StringVal *UiString `xml:"string,omitempty"`
|
||||||
|
NumberVal *string `xml:"number,omitempty"` // Preserve as string literal
|
||||||
|
EnumVal *string `xml:"enum,omitempty"`
|
||||||
|
RectVal *UiRect `xml:"rect,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UiActionReference struct {
|
||||||
|
Name string `xml:"name,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UiAction struct {
|
||||||
|
Name string `xml:"name,attr"`
|
||||||
|
Properties []UiProperty `xml:"property"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UiResources struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
type UiConnections struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
type UiFile struct {
|
||||||
|
XMLName xml.Name // should always be xml.Name{Local: "ui"}
|
||||||
|
|
||||||
|
Class string `xml:"class"`
|
||||||
|
Version string `xml:"version,attr"` // e.g. 4.0
|
||||||
|
Widget UiWidget `xml:"widget"` // There's only one root widget
|
||||||
|
Resources UiResources `xml:"resources"`
|
||||||
|
Connections UiConnections `xml:"connections"`
|
||||||
|
}
|
338
cmd/miqt-uic/ui2go.go
Normal file
338
cmd/miqt-uic/ui2go.go
Normal file
@ -0,0 +1,338 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"go/format"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func collectClassNames_Widget(u UiWidget) []string {
|
||||||
|
var ret []string
|
||||||
|
if u.Name != "" {
|
||||||
|
ret = append(ret, u.Name+" *qt."+u.Class)
|
||||||
|
}
|
||||||
|
for _, w := range u.Widgets {
|
||||||
|
ret = append(ret, collectClassNames_Widget(w)...)
|
||||||
|
}
|
||||||
|
if u.Layout != nil {
|
||||||
|
ret = append(ret, u.Layout.Name+" *qt."+u.Layout.Class)
|
||||||
|
for _, li := range u.Layout.Items {
|
||||||
|
ret = append(ret, collectClassNames_Widget(li.Widget)...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, a := range u.Actions {
|
||||||
|
ret = append(ret, a.Name+" *qt.QAction")
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateString(s *UiString, parentClass string) string {
|
||||||
|
if s.Notr || parentClass == "" {
|
||||||
|
return strconv.Quote(s.Value)
|
||||||
|
}
|
||||||
|
return `qt.` + parentClass + `_Tr(` + strconv.Quote(s.Value) + `)`
|
||||||
|
}
|
||||||
|
|
||||||
|
// qwidgetName creates the T.QWidget name that MIQT needs to access the base class.
|
||||||
|
func qwidgetName(name string, class string) string {
|
||||||
|
if name == "" {
|
||||||
|
return "nil"
|
||||||
|
}
|
||||||
|
if class == "QWidget" {
|
||||||
|
return name // It's already the right type
|
||||||
|
}
|
||||||
|
return name + ".QWidget"
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateWidget(w UiWidget, parentName string, parentClass string) (string, error) {
|
||||||
|
ret := strings.Builder{}
|
||||||
|
|
||||||
|
ctor, ok := constructorFunctionFor(w.Class)
|
||||||
|
if !ok {
|
||||||
|
return "", fmt.Errorf("No known constructor function for %q class %q", w.Name, w.Class)
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.WriteString(`
|
||||||
|
ui.` + w.Name + ` = qt.` + ctor + `(` + qwidgetName(parentName, parentClass) + `)
|
||||||
|
ui.` + w.Name + `.SetObjectName(` + strconv.Quote(w.Name) + `)
|
||||||
|
`)
|
||||||
|
|
||||||
|
// Properties
|
||||||
|
for _, prop := range w.Properties {
|
||||||
|
setterFunc := `.Set` + strings.ToUpper(string(prop.Name[0])) + prop.Name[1:]
|
||||||
|
|
||||||
|
if prop.Name == "geometry" {
|
||||||
|
if !(prop.RectVal.X == 0 && prop.RectVal.Y == 0) {
|
||||||
|
// Set all 4x properties
|
||||||
|
ret.WriteString(`ui.` + w.Name + `.SetGeometry(qt.NewQRect(` + fmt.Sprintf("%d, %d, %d, %d", prop.RectVal.X, prop.RectVal.Y, prop.RectVal.Width, prop.RectVal.Height) + "))\n")
|
||||||
|
|
||||||
|
} else if !(prop.RectVal.Width == 0 && prop.RectVal.Height == 0) {
|
||||||
|
// Only width/height were supplied
|
||||||
|
ret.WriteString(`ui.` + w.Name + `.Resize(` + fmt.Sprintf("%d, %d", prop.RectVal.Width, prop.RectVal.Height) + ")\n")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if prop.StringVal != nil {
|
||||||
|
// "windowTitle", "title", "text"
|
||||||
|
ret.WriteString(`ui.` + w.Name + setterFunc + `(` + generateString(prop.StringVal, parentClass) + ")\n")
|
||||||
|
|
||||||
|
} else if prop.EnumVal != nil {
|
||||||
|
// "frameShape"
|
||||||
|
ret.WriteString(`ui.` + w.Name + setterFunc + `(qt.` + strings.Replace(*prop.EnumVal, `::`, `__`, -1) + ")\n")
|
||||||
|
|
||||||
|
} else {
|
||||||
|
ret.WriteString("/* miqt-uic: no handler for " + w.Name + " property '" + prop.Name + "' */\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attributes
|
||||||
|
|
||||||
|
for _, attr := range w.Attributes {
|
||||||
|
if parentClass == "QTabWidget" && attr.Name == "title" {
|
||||||
|
ret.WriteString(parentName + `.SetTabText(` + parentName + ".IndexOf(ui." + w.Name + "), " + generateString(attr.StringVal, parentClass) + ")\n")
|
||||||
|
|
||||||
|
} else if w.Class == "QDockWidget" && parentClass == "QMainWindow" && attr.Name == "dockWidgetArea" {
|
||||||
|
ret.WriteString(parentName + `.AddDockWidget(qt.DockWidgetArea(` + *attr.NumberVal + `), ui.` + w.Name + `)` + "\n")
|
||||||
|
|
||||||
|
} else {
|
||||||
|
ret.WriteString("/* miqt-uic: no handler for " + w.Name + " attribute '" + attr.Name + "' */\n")
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO
|
||||||
|
// w.Attributes
|
||||||
|
|
||||||
|
// Layout
|
||||||
|
if w.Layout != nil {
|
||||||
|
ctor, ok := constructorFunctionFor(w.Layout.Class)
|
||||||
|
if !ok {
|
||||||
|
return "", fmt.Errorf("No known constructor function for %q class %q", w.Layout.Name, w.Layout.Class)
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.WriteString(`
|
||||||
|
ui.` + w.Layout.Name + ` = qt.` + ctor + `(` + qwidgetName("ui."+w.Name, w.Class) + `)
|
||||||
|
ui.` + w.Layout.Name + `.SetObjectName(` + strconv.Quote(w.Layout.Name) + `)
|
||||||
|
`)
|
||||||
|
|
||||||
|
for _, child := range w.Layout.Items {
|
||||||
|
|
||||||
|
// Layout items have the parent as the real QWidget parent and are
|
||||||
|
// separately assigned to the layout afterwards
|
||||||
|
|
||||||
|
nest, err := generateWidget(child.Widget, `ui.`+w.Name, w.Class)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf(w.Name+": %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.WriteString(nest)
|
||||||
|
|
||||||
|
// Assign to layout
|
||||||
|
|
||||||
|
switch w.Layout.Class {
|
||||||
|
case `QFormLayout`:
|
||||||
|
// Row and Column are always populated.
|
||||||
|
rowPos := fmt.Sprintf("%d", *child.Row)
|
||||||
|
var colPos string
|
||||||
|
if *child.Column == 0 {
|
||||||
|
colPos = `qt.QFormLayout__LabelRole`
|
||||||
|
} else if *child.Column == 1 {
|
||||||
|
colPos = `qt.QFormLayout__FieldRole`
|
||||||
|
} else {
|
||||||
|
ret.WriteString("/* miqt-uic: QFormLayout does not understand column index */\n")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// For QFormLayout it's SetWidget
|
||||||
|
ret.WriteString(`
|
||||||
|
ui.` + w.Layout.Name + `.SetWidget(` + rowPos + `, ` + colPos + `, ` + qwidgetName(`ui.`+child.Widget.Name, child.Widget.Class) + `)
|
||||||
|
`)
|
||||||
|
|
||||||
|
case `QGridLayout`:
|
||||||
|
// For QGridLayout it's AddWidget2
|
||||||
|
// FIXME in Miqt this function has optionals, needs to be called with the correct arity
|
||||||
|
// TODO support rowSpan, columnSpan
|
||||||
|
ret.WriteString(`
|
||||||
|
ui.` + w.Layout.Name + `.AddWidget2(` + qwidgetName(`ui.`+child.Widget.Name, child.Widget.Class) + `, ` + fmt.Sprintf("%d, %d", *child.Row, *child.Column) + `)
|
||||||
|
`)
|
||||||
|
|
||||||
|
case "QVBoxLayout", "QHBoxLayout":
|
||||||
|
// For box layout it's AddWidget
|
||||||
|
ret.WriteString(`
|
||||||
|
ui.` + w.Layout.Name + `.AddWidget(` + qwidgetName(`ui.`+child.Widget.Name, child.Widget.Class) + `)
|
||||||
|
`)
|
||||||
|
|
||||||
|
default:
|
||||||
|
ret.WriteString("/* miqt-uic: no handler for layout '" + w.Layout.Class + "' */\n")
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Actions
|
||||||
|
|
||||||
|
for _, a := range w.Actions {
|
||||||
|
ret.WriteString(`
|
||||||
|
ui.` + a.Name + ` = qt.NewQAction(` + parentName + `)
|
||||||
|
ui.` + a.Name + `.SetObjectName(` + strconv.Quote(a.Name) + `)
|
||||||
|
`)
|
||||||
|
|
||||||
|
// QActions are translated in the parent window's context
|
||||||
|
if prop, ok := propertyByName(a.Properties, "text"); ok {
|
||||||
|
ret.WriteString("ui." + a.Name + `.SetText(` + generateString(prop.StringVal, w.Class) + `)` + "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
if prop, ok := propertyByName(a.Properties, "shortcut"); ok {
|
||||||
|
ret.WriteString("ui." + a.Name + `.SetShortcut(qt.NewQKeySequence2(` + generateString(prop.StringVal, w.Class) + `))` + "\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Items
|
||||||
|
|
||||||
|
for itemNo, itm := range w.Items {
|
||||||
|
ret.WriteString("ui." + w.Name + `.AddItem("")` + "\n")
|
||||||
|
|
||||||
|
// Check for a "text" property and update the item's text
|
||||||
|
// Do this as a 2nd step so that the SetItemText can be trapped for retranslateUi()
|
||||||
|
// TODO Abstract for all SetItem{Foo} properties
|
||||||
|
if prop, ok := propertyByName(itm.Properties, "text"); ok {
|
||||||
|
ret.WriteString("ui." + w.Name + `.SetItemText(` + fmt.Sprintf("%d", itemNo) + `, ` + generateString(prop.StringVal, w.Class) + `)` + "\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Columns
|
||||||
|
// TODO
|
||||||
|
// w.Columns
|
||||||
|
|
||||||
|
// Recurse children
|
||||||
|
var (
|
||||||
|
setCentralWidget = false
|
||||||
|
setMenuBar = false
|
||||||
|
setStatusBar = false
|
||||||
|
)
|
||||||
|
|
||||||
|
for _, child := range w.Widgets {
|
||||||
|
nest, err := generateWidget(child, `ui.`+w.Name, w.Class)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf(w.Name+": %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.WriteString(nest)
|
||||||
|
|
||||||
|
// QMainWindow CentralWidget handling
|
||||||
|
// The first listed class can be the central widget.
|
||||||
|
// TODO should it be the first child with a layout? But need to handle windows with no layout
|
||||||
|
if w.Class == `QMainWindow` && !setCentralWidget {
|
||||||
|
ret.WriteString(`ui.` + w.Name + `.SetCentralWidget(ui.` + child.Name + ") // Set central widget \n")
|
||||||
|
setCentralWidget = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// QDockWidget also has something like a central widget
|
||||||
|
if w.Class == `QDockWidget` && !setCentralWidget {
|
||||||
|
ret.WriteString(`ui.` + w.Name + `.SetWidget(ui.` + child.Name + ") // Set central widget \n")
|
||||||
|
setCentralWidget = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if w.Class == "QMainWindow" && child.Class == "QMenuBar" && !setMenuBar {
|
||||||
|
ret.WriteString(`ui.` + w.Name + `.SetMenuBar(ui.` + child.Name + `)` + "\n")
|
||||||
|
setMenuBar = true
|
||||||
|
}
|
||||||
|
if w.Class == "QMainWindow" && child.Class == "QStatusBar" && !setStatusBar {
|
||||||
|
ret.WriteString(`ui.` + w.Name + `.SetStatusBar(ui.` + child.Name + `)` + "\n")
|
||||||
|
setStatusBar = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// QTabWidget->QTab handling
|
||||||
|
if w.Class == `QTabWidget` {
|
||||||
|
ret.WriteString(`ui.` + w.Name + `.AddTab(` + qwidgetName(`ui.`+child.Name, child.Class) + `, "")` + "\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddActions
|
||||||
|
// n.b. This must be *after* all children have been constructed, in case we
|
||||||
|
// are adding a direct child
|
||||||
|
|
||||||
|
for _, a := range w.AddActions {
|
||||||
|
if a.Name == "separator" {
|
||||||
|
// TODO how does Qt Designer disambiguate a real QAction with name="separator" ?
|
||||||
|
ret.WriteString("ui." + w.Name + ".AddSeparator()\n")
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// If we are a menubar, then <addaction> refers to top-level QMenu instead of QAction
|
||||||
|
if w.Class == "QMenuBar" {
|
||||||
|
ret.WriteString("ui." + w.Name + ".AddMenu(ui." + a.Name + ")\n")
|
||||||
|
} else if w.Class == "QMenu" {
|
||||||
|
// QMenu has its own .AddAction() implementation that takes plain string
|
||||||
|
// That's convenient, but it shadows the AddAction version that takes a QAction*
|
||||||
|
// We need to use the underlying QWidget.AddAction explicitly
|
||||||
|
ret.WriteString("ui." + w.Name + ".QWidget.AddAction(ui." + a.Name + ")\n")
|
||||||
|
} else {
|
||||||
|
ret.WriteString("ui." + w.Name + ".AddAction(ui." + a.Name + ")\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func generate(packageName string, goGenerateArgs string, u UiFile) ([]byte, error) {
|
||||||
|
ret := strings.Builder{}
|
||||||
|
ret.WriteString(`// Generated by miqt-uic. To update this file, edit the .ui file in
|
||||||
|
// Qt Designer, and then run 'go generate'.
|
||||||
|
//
|
||||||
|
//go:` + `generate miqt-uic ` + goGenerateArgs + `
|
||||||
|
|
||||||
|
package ` + packageName + `
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/mappu/miqt/qt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ` + u.Class + `Ui struct {
|
||||||
|
` + strings.Join(collectClassNames_Widget(u.Widget), "\n") + `
|
||||||
|
}
|
||||||
|
|
||||||
|
// New` + u.Class + `Ui creates all Qt widget classes for ` + u.Class + `.
|
||||||
|
func New` + u.Class + `Ui() *` + u.Class + `Ui {
|
||||||
|
ui := &` + u.Class + `Ui{}
|
||||||
|
`)
|
||||||
|
|
||||||
|
nest, err := generateWidget(u.Widget, "", "")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't emit any of the lines that included .Tr(), move them into the
|
||||||
|
// retranslateUi() function
|
||||||
|
var translateFunc []string
|
||||||
|
for _, line := range strings.Split(nest, "\n") {
|
||||||
|
if strings.Contains(line, `_Tr(`) {
|
||||||
|
translateFunc = append(translateFunc, line)
|
||||||
|
} else {
|
||||||
|
ret.WriteString(line + "\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.WriteString(`
|
||||||
|
ui.Retranslate()
|
||||||
|
|
||||||
|
return ui
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retranslate reapplies all text translations.
|
||||||
|
func (ui *` + u.Class + `Ui) Retranslate() {
|
||||||
|
` + strings.Join(translateFunc, "\n") + `
|
||||||
|
}
|
||||||
|
|
||||||
|
`)
|
||||||
|
|
||||||
|
output := ret.String()
|
||||||
|
|
||||||
|
formatted, err := format.Source([]byte(output))
|
||||||
|
if err != nil {
|
||||||
|
// Return unformatted so it can be fixed
|
||||||
|
return []byte(output), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return formatted, nil
|
||||||
|
}
|
47
cmd/miqt-uic/uic_test.go
Normal file
47
cmd/miqt-uic/uic_test.go
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/xml"
|
||||||
|
"io/ioutil"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFixtureMarshalRoundtrip(t *testing.T) {
|
||||||
|
|
||||||
|
testFixture := func(fixtureFile string) {
|
||||||
|
in, err := ioutil.ReadFile(fixtureFile)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("ReadFile: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
in = bytes.Replace(in, []byte("\r"), []byte{}, -1) // Replace CRLF to LF
|
||||||
|
|
||||||
|
var parsed UiFile
|
||||||
|
err = xml.Unmarshal(in, &parsed)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unmarshal: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ret, err := xml.MarshalIndent(parsed, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Marshal: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make some minor changes to our generated file to more closely match
|
||||||
|
// Qt Designer's generated ui file
|
||||||
|
// - Prepend XML header
|
||||||
|
// - Convert to self-closing tags
|
||||||
|
ret = []byte(xml.Header + xmlConvertToSelfClosing(string(ret)) + "\n")
|
||||||
|
|
||||||
|
// Verify that the marshalled result matches the original identically,
|
||||||
|
// i.e. we did not miss any properties in our XML type definitions
|
||||||
|
if string(in) != string(ret) {
|
||||||
|
t.Errorf("Mismatch")
|
||||||
|
t.Log(lineDiff(string(in), string(ret)))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
testFixture("../../examples/uidesigner/design.ui")
|
||||||
|
}
|
84
cmd/miqt-uic/util.go
Normal file
84
cmd/miqt-uic/util.go
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// lineDiff does some basic diagnostic printing to show where two files differ.
|
||||||
|
// It is not clever about resyncronizing runs of differences.
|
||||||
|
func lineDiff(a, b string) string {
|
||||||
|
aLines := strings.Split(a, "\n")
|
||||||
|
bLines := strings.Split(b, "\n")
|
||||||
|
|
||||||
|
var diff []string
|
||||||
|
|
||||||
|
aIdx := 0
|
||||||
|
bIdx := 0
|
||||||
|
for {
|
||||||
|
// Check if one-or both- files have reached the final line already
|
||||||
|
if aIdx == len(aLines) {
|
||||||
|
if bIdx == len(bLines) {
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
diff = append(diff, fmt.Sprintf("%d: < %q", bIdx, bLines[bIdx]))
|
||||||
|
bIdx++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
} else if bIdx == len(bLines) {
|
||||||
|
diff = append(diff, fmt.Sprintf("%d: > %q", aIdx, aLines[aIdx]))
|
||||||
|
aIdx++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Both have remaining lines
|
||||||
|
if aLines[aIdx] == bLines[bIdx] {
|
||||||
|
// Match OK
|
||||||
|
} else {
|
||||||
|
diff = append(diff, fmt.Sprintf("%d: < %q", bIdx, aLines[aIdx]))
|
||||||
|
diff = append(diff, fmt.Sprintf("%d: > %q", aIdx, bLines[bIdx]))
|
||||||
|
}
|
||||||
|
aIdx++
|
||||||
|
bIdx++
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.Join(diff, "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
// xmlConvertToSelfClosing converts a multiline XML file, where if a line
|
||||||
|
// consists of <foo ...></foo>, it is replaced with <foo />.
|
||||||
|
func xmlConvertToSelfClosing(input string) string {
|
||||||
|
|
||||||
|
lines := strings.Split(input, "\n")
|
||||||
|
|
||||||
|
for i, l := range lines {
|
||||||
|
tll := strings.TrimLeft(l, " \t")
|
||||||
|
indent := l[0 : len(l)-len(tll)]
|
||||||
|
spos := strings.IndexAny(tll, " >")
|
||||||
|
if spos == -1 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
opentag := tll[0:spos]
|
||||||
|
closetag := "</" + opentag[1:] + ">"
|
||||||
|
if !strings.HasSuffix(tll, ">"+closetag) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
tll = tll[0:len(tll)-len(closetag)-1] + "/>"
|
||||||
|
lines[i] = indent + tll
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.Join(lines, "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
// propertyByName searches a slice of UiProperty to find one with a matching name.
|
||||||
|
func propertyByName(check []UiProperty, search string) (UiProperty, bool) {
|
||||||
|
for _, p := range check {
|
||||||
|
if p.Name == search {
|
||||||
|
return p, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return UiProperty{}, false
|
||||||
|
}
|
25
examples/uidesigner/README.md
Normal file
25
examples/uidesigner/README.md
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
# miqt/examples/uidesigner
|
||||||
|
|
||||||
|
This example shows how to use [Qt Designer](https://doc.qt.io/qt-5/qtdesigner-manual.html) and miqt-uic to design a UI.
|
||||||
|
|
||||||
|
## 1. Design
|
||||||
|
|
||||||
|
Use Qt Designer to build the UI and save as a `.ui` XML file.
|
||||||
|
|
||||||
|
![](uidesigner.png)
|
||||||
|
|
||||||
|
## 2. Compile
|
||||||
|
|
||||||
|
Compile the `.ui` XML to Go code with the `miqt-uic` tool.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
miqt-uic -InFile design.ui -OutFile design.go
|
||||||
|
```
|
||||||
|
|
||||||
|
Some advanced configuration for `miqt-uic` can be done with other command-line arguments. Run `miqt-uic -Help` for more information.
|
||||||
|
|
||||||
|
## 3. Use
|
||||||
|
|
||||||
|
Use the generated types.
|
||||||
|
|
||||||
|
![](uidesigner.miqt.png)
|
152
examples/uidesigner/design.go
Normal file
152
examples/uidesigner/design.go
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
// Generated by miqt-uic. To update this file, edit the .ui file in
|
||||||
|
// Qt Designer, and then run 'go generate'.
|
||||||
|
//
|
||||||
|
//go:generate miqt-uic -InFile design.ui -OutFile design.go
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/mappu/miqt/qt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MainWindowUi struct {
|
||||||
|
MainWindow *qt.QMainWindow
|
||||||
|
centralwidget *qt.QWidget
|
||||||
|
gridLayout *qt.QGridLayout
|
||||||
|
tabWidget *qt.QTabWidget
|
||||||
|
tab *qt.QWidget
|
||||||
|
formLayout *qt.QFormLayout
|
||||||
|
label *qt.QLabel
|
||||||
|
comboBox *qt.QComboBox
|
||||||
|
label_2 *qt.QLabel
|
||||||
|
spinBox *qt.QSpinBox
|
||||||
|
tab_2 *qt.QWidget
|
||||||
|
treeWidget *qt.QTreeWidget
|
||||||
|
menubar *qt.QMenuBar
|
||||||
|
menu_File *qt.QMenu
|
||||||
|
statusbar *qt.QStatusBar
|
||||||
|
dockWidget *qt.QDockWidget
|
||||||
|
dockWidgetContents *qt.QWidget
|
||||||
|
verticalLayout *qt.QVBoxLayout
|
||||||
|
calendarWidget *qt.QCalendarWidget
|
||||||
|
action_New *qt.QAction
|
||||||
|
actionE_xit *qt.QAction
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMainWindowUi creates all Qt widget classes for MainWindow.
|
||||||
|
func NewMainWindowUi() *MainWindowUi {
|
||||||
|
ui := &MainWindowUi{}
|
||||||
|
|
||||||
|
ui.MainWindow = qt.NewQMainWindow2(nil)
|
||||||
|
ui.MainWindow.SetObjectName("MainWindow")
|
||||||
|
ui.MainWindow.Resize(800, 600)
|
||||||
|
ui.MainWindow.SetWindowTitle("MainWindow")
|
||||||
|
|
||||||
|
ui.action_New = qt.NewQAction()
|
||||||
|
ui.action_New.SetObjectName("action_New")
|
||||||
|
|
||||||
|
ui.actionE_xit = qt.NewQAction()
|
||||||
|
ui.actionE_xit.SetObjectName("actionE_xit")
|
||||||
|
|
||||||
|
ui.centralwidget = qt.NewQWidget2(ui.MainWindow.QWidget)
|
||||||
|
ui.centralwidget.SetObjectName("centralwidget")
|
||||||
|
|
||||||
|
ui.gridLayout = qt.NewQGridLayout(ui.centralwidget)
|
||||||
|
ui.gridLayout.SetObjectName("gridLayout")
|
||||||
|
|
||||||
|
ui.tabWidget = qt.NewQTabWidget2(ui.centralwidget)
|
||||||
|
ui.tabWidget.SetObjectName("tabWidget")
|
||||||
|
|
||||||
|
ui.tab = qt.NewQWidget2(ui.tabWidget.QWidget)
|
||||||
|
ui.tab.SetObjectName("tab")
|
||||||
|
|
||||||
|
ui.formLayout = qt.NewQFormLayout2(ui.tab)
|
||||||
|
ui.formLayout.SetObjectName("formLayout")
|
||||||
|
|
||||||
|
ui.label = qt.NewQLabel3(ui.tab)
|
||||||
|
ui.label.SetObjectName("label")
|
||||||
|
|
||||||
|
ui.formLayout.SetWidget(0, qt.QFormLayout__LabelRole, ui.label.QWidget)
|
||||||
|
|
||||||
|
ui.comboBox = qt.NewQComboBox2(ui.tab)
|
||||||
|
ui.comboBox.SetObjectName("comboBox")
|
||||||
|
ui.comboBox.AddItem("")
|
||||||
|
ui.comboBox.AddItem("")
|
||||||
|
|
||||||
|
ui.formLayout.SetWidget(0, qt.QFormLayout__FieldRole, ui.comboBox.QWidget)
|
||||||
|
|
||||||
|
ui.label_2 = qt.NewQLabel3(ui.tab)
|
||||||
|
ui.label_2.SetObjectName("label_2")
|
||||||
|
|
||||||
|
ui.formLayout.SetWidget(1, qt.QFormLayout__LabelRole, ui.label_2.QWidget)
|
||||||
|
|
||||||
|
ui.spinBox = qt.NewQSpinBox2(ui.tab)
|
||||||
|
ui.spinBox.SetObjectName("spinBox")
|
||||||
|
|
||||||
|
ui.formLayout.SetWidget(1, qt.QFormLayout__FieldRole, ui.spinBox.QWidget)
|
||||||
|
ui.tabWidget.AddTab(ui.tab, "")
|
||||||
|
|
||||||
|
ui.tab_2 = qt.NewQWidget2(ui.tabWidget.QWidget)
|
||||||
|
ui.tab_2.SetObjectName("tab_2")
|
||||||
|
ui.tabWidget.AddTab(ui.tab_2, "")
|
||||||
|
|
||||||
|
ui.gridLayout.AddWidget2(ui.tabWidget.QWidget, 0, 0)
|
||||||
|
|
||||||
|
ui.treeWidget = qt.NewQTreeWidget2(ui.centralwidget)
|
||||||
|
ui.treeWidget.SetObjectName("treeWidget")
|
||||||
|
ui.treeWidget.SetFrameShape(qt.QFrame__Panel)
|
||||||
|
|
||||||
|
ui.gridLayout.AddWidget2(ui.treeWidget.QWidget, 0, 1)
|
||||||
|
ui.MainWindow.SetCentralWidget(ui.centralwidget) // Set central widget
|
||||||
|
|
||||||
|
ui.menubar = qt.NewQMenuBar2(ui.MainWindow.QWidget)
|
||||||
|
ui.menubar.SetObjectName("menubar")
|
||||||
|
ui.menubar.Resize(800, 29)
|
||||||
|
|
||||||
|
ui.menu_File = qt.NewQMenu3(ui.menubar.QWidget)
|
||||||
|
ui.menu_File.SetObjectName("menu_File")
|
||||||
|
ui.menu_File.QWidget.AddAction(ui.action_New)
|
||||||
|
ui.menu_File.AddSeparator()
|
||||||
|
ui.menu_File.QWidget.AddAction(ui.actionE_xit)
|
||||||
|
ui.menubar.AddMenu(ui.menu_File)
|
||||||
|
ui.MainWindow.SetMenuBar(ui.menubar)
|
||||||
|
|
||||||
|
ui.statusbar = qt.NewQStatusBar2(ui.MainWindow.QWidget)
|
||||||
|
ui.statusbar.SetObjectName("statusbar")
|
||||||
|
ui.MainWindow.SetStatusBar(ui.statusbar)
|
||||||
|
|
||||||
|
ui.dockWidget = qt.NewQDockWidget5(ui.MainWindow.QWidget)
|
||||||
|
ui.dockWidget.SetObjectName("dockWidget")
|
||||||
|
ui.MainWindow.AddDockWidget(qt.DockWidgetArea(1), ui.dockWidget)
|
||||||
|
|
||||||
|
ui.dockWidgetContents = qt.NewQWidget2(ui.dockWidget.QWidget)
|
||||||
|
ui.dockWidgetContents.SetObjectName("dockWidgetContents")
|
||||||
|
|
||||||
|
ui.verticalLayout = qt.NewQVBoxLayout2(ui.dockWidgetContents)
|
||||||
|
ui.verticalLayout.SetObjectName("verticalLayout")
|
||||||
|
|
||||||
|
ui.calendarWidget = qt.NewQCalendarWidget2(ui.dockWidgetContents)
|
||||||
|
ui.calendarWidget.SetObjectName("calendarWidget")
|
||||||
|
|
||||||
|
ui.verticalLayout.AddWidget(ui.calendarWidget.QWidget)
|
||||||
|
ui.dockWidget.SetWidget(ui.dockWidgetContents) // Set central widget
|
||||||
|
|
||||||
|
ui.Retranslate()
|
||||||
|
|
||||||
|
return ui
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retranslate reapplies all text translations.
|
||||||
|
func (ui *MainWindowUi) Retranslate() {
|
||||||
|
ui.action_New.SetText(qt.QMainWindow_Tr("&New..."))
|
||||||
|
ui.actionE_xit.SetText(qt.QMainWindow_Tr("E&xit"))
|
||||||
|
ui.actionE_xit.SetShortcut(qt.NewQKeySequence2(qt.QMainWindow_Tr("Ctrl+Q")))
|
||||||
|
ui.tabWidget.SetTabText(ui.tabWidget.IndexOf(ui.tab), qt.QTabWidget_Tr("Tab 1"))
|
||||||
|
ui.label.SetText(qt.QWidget_Tr("Dropdown:"))
|
||||||
|
ui.comboBox.SetItemText(0, qt.QComboBox_Tr("First"))
|
||||||
|
ui.comboBox.SetItemText(1, qt.QComboBox_Tr("Second"))
|
||||||
|
ui.label_2.SetText(qt.QWidget_Tr("Number:"))
|
||||||
|
ui.tabWidget.SetTabText(ui.tabWidget.IndexOf(ui.tab_2), qt.QTabWidget_Tr("Tab 2"))
|
||||||
|
ui.menu_File.SetTitle(qt.QMenuBar_Tr("&File"))
|
||||||
|
ui.dockWidget.SetWindowTitle(qt.QMainWindow_Tr("Dock Title"))
|
||||||
|
}
|
130
examples/uidesigner/design.ui
Normal file
130
examples/uidesigner/design.ui
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>MainWindow</class>
|
||||||
|
<widget class="QMainWindow" name="MainWindow">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>800</width>
|
||||||
|
<height>600</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>MainWindow</string>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="centralwidget">
|
||||||
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QTabWidget" name="tabWidget">
|
||||||
|
<widget class="QWidget" name="tab">
|
||||||
|
<attribute name="title">
|
||||||
|
<string>Tab 1</string>
|
||||||
|
</attribute>
|
||||||
|
<layout class="QFormLayout" name="formLayout">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string>Dropdown:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QComboBox" name="comboBox">
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>First</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Second</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="label_2">
|
||||||
|
<property name="text">
|
||||||
|
<string>Number:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QSpinBox" name="spinBox"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<widget class="QWidget" name="tab_2">
|
||||||
|
<attribute name="title">
|
||||||
|
<string>Tab 2</string>
|
||||||
|
</attribute>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QTreeWidget" name="treeWidget">
|
||||||
|
<property name="frameShape">
|
||||||
|
<enum>QFrame::Panel</enum>
|
||||||
|
</property>
|
||||||
|
<column>
|
||||||
|
<property name="text">
|
||||||
|
<string notr="true">1</string>
|
||||||
|
</property>
|
||||||
|
</column>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<widget class="QMenuBar" name="menubar">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>800</width>
|
||||||
|
<height>29</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<widget class="QMenu" name="menu_File">
|
||||||
|
<property name="title">
|
||||||
|
<string>&File</string>
|
||||||
|
</property>
|
||||||
|
<addaction name="action_New"/>
|
||||||
|
<addaction name="separator"/>
|
||||||
|
<addaction name="actionE_xit"/>
|
||||||
|
</widget>
|
||||||
|
<addaction name="menu_File"/>
|
||||||
|
</widget>
|
||||||
|
<widget class="QStatusBar" name="statusbar"/>
|
||||||
|
<widget class="QDockWidget" name="dockWidget">
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Dock Title</string>
|
||||||
|
</property>
|
||||||
|
<attribute name="dockWidgetArea">
|
||||||
|
<number>1</number>
|
||||||
|
</attribute>
|
||||||
|
<widget class="QWidget" name="dockWidgetContents">
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QCalendarWidget" name="calendarWidget"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
<action name="action_New">
|
||||||
|
<property name="text">
|
||||||
|
<string>&New...</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionE_xit">
|
||||||
|
<property name="text">
|
||||||
|
<string>E&xit</string>
|
||||||
|
</property>
|
||||||
|
<property name="shortcut">
|
||||||
|
<string>Ctrl+Q</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
16
examples/uidesigner/main.go
Normal file
16
examples/uidesigner/main.go
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/mappu/miqt/qt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
qt.NewQApplication(os.Args)
|
||||||
|
|
||||||
|
ui := NewMainWindowUi()
|
||||||
|
ui.MainWindow.Show()
|
||||||
|
|
||||||
|
qt.QApplication_Exec()
|
||||||
|
}
|
BIN
examples/uidesigner/uidesigner.miqt.png
Normal file
BIN
examples/uidesigner/uidesigner.miqt.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 28 KiB |
BIN
examples/uidesigner/uidesigner.png
Normal file
BIN
examples/uidesigner/uidesigner.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 171 KiB |
Loading…
Reference in New Issue
Block a user