mirror of
https://github.com/mappu/miqt.git
synced 2025-01-08 16:38:37 +00:00
commit
12618a309c
1
.gitignore
vendored
1
.gitignore
vendored
@ -20,6 +20,7 @@ cmd/genbindings/genbindings
|
|||||||
cmd/miqt-uic/miqt-uic
|
cmd/miqt-uic/miqt-uic
|
||||||
cmd/miqt-rcc/miqt-rcc
|
cmd/miqt-rcc/miqt-rcc
|
||||||
|
|
||||||
|
examples/goroutine6/goroutine6
|
||||||
examples/helloworld/helloworld
|
examples/helloworld/helloworld
|
||||||
examples/helloworld6/helloworld6
|
examples/helloworld6/helloworld6
|
||||||
examples/mdoutliner/mdoutliner
|
examples/mdoutliner/mdoutliner
|
||||||
|
@ -94,6 +94,8 @@ Qt class inherited types are projected as a Go embedded struct. For example, to
|
|||||||
|
|
||||||
The Go runtime migrates goroutines between OS threads, but Qt expects fixed OS threads to be used for each QObject. When you first call `qt.NewQApplication` in MIQT, that will be considered the [Qt main thread](https://doc.qt.io/qt-6/thread-basics.html#gui-thread-and-worker-thread) and will automatically signal the Go runtime to bind to a fixed OS thread using `runtime.LockOSThread()`.
|
The Go runtime migrates goroutines between OS threads, but Qt expects fixed OS threads to be used for each QObject. When you first call `qt.NewQApplication` in MIQT, that will be considered the [Qt main thread](https://doc.qt.io/qt-6/thread-basics.html#gui-thread-and-worker-thread) and will automatically signal the Go runtime to bind to a fixed OS thread using `runtime.LockOSThread()`.
|
||||||
|
|
||||||
|
- When accessing Qt objects from inside another goroutine, it's safest to use `(qt6/mainthread).Wait()` to access the Qt objects from Qt's main thread.
|
||||||
|
|
||||||
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.
|
||||||
|
|
||||||
### Q6. Can I use Qt Designer and the Qt Resource system?
|
### Q6. Can I use Qt Designer and the Qt Resource system?
|
||||||
|
BIN
examples/goroutine6/goroutine6.png
Normal file
BIN
examples/goroutine6/goroutine6.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 24 KiB |
57
examples/goroutine6/main.go
Normal file
57
examples/goroutine6/main.go
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
qt "github.com/mappu/miqt/qt6"
|
||||||
|
"github.com/mappu/miqt/qt6/mainthread"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
threadcount := runtime.GOMAXPROCS(0)
|
||||||
|
|
||||||
|
qt.NewQApplication(os.Args)
|
||||||
|
|
||||||
|
window := qt.NewQMainWindow2()
|
||||||
|
window.QWidget.SetFixedSize2(250, 50*(threadcount+1))
|
||||||
|
window.QWidget.SetWindowTitle("goroutine Example")
|
||||||
|
|
||||||
|
widget := qt.NewQWidget(window.QWidget)
|
||||||
|
var layout = qt.NewQVBoxLayout2()
|
||||||
|
widget.SetLayout(layout.QLayout)
|
||||||
|
window.SetCentralWidget(widget)
|
||||||
|
|
||||||
|
labels := make([]*qt.QLabel, threadcount)
|
||||||
|
for i := range labels {
|
||||||
|
label := qt.NewQLabel(window.QWidget)
|
||||||
|
label.SetAlignment(qt.AlignCenter)
|
||||||
|
widget.Layout().AddWidget(label.QWidget)
|
||||||
|
labels[i] = label
|
||||||
|
}
|
||||||
|
|
||||||
|
button := qt.NewQPushButton5("start!", window.QWidget)
|
||||||
|
button.OnClicked1(func(bool) {
|
||||||
|
button.SetDisabled(true)
|
||||||
|
for i, label := range labels {
|
||||||
|
go func(index int, qlabel *qt.QLabel) {
|
||||||
|
var tick int
|
||||||
|
for range time.NewTicker(time.Duration((index+1)*25) * time.Millisecond).C {
|
||||||
|
tick++
|
||||||
|
// time.Sleep(50 * time.Millisecond)
|
||||||
|
|
||||||
|
mainthread.Wait(func() {
|
||||||
|
qlabel.SetText(fmt.Sprintf("%v %v", tick, time.Now().UTC().Format("15:04:05.0000")))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}(i, label)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
widget.Layout().AddWidget(button.QWidget)
|
||||||
|
|
||||||
|
window.Show()
|
||||||
|
|
||||||
|
qt.QApplication_Exec()
|
||||||
|
}
|
13
qt6/mainthread/mainthread.cpp
Normal file
13
qt6/mainthread/mainthread.cpp
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#include <QMetaObject>
|
||||||
|
#include <QCoreApplication>
|
||||||
|
|
||||||
|
#ifndef _Bool
|
||||||
|
#define _Bool bool
|
||||||
|
#endif
|
||||||
|
#include "_cgo_export.h"
|
||||||
|
|
||||||
|
void mainthread_exec(intptr_t cb) {
|
||||||
|
QMetaObject::invokeMethod(qApp, [=]{
|
||||||
|
mainthread_exec_handle(cb);
|
||||||
|
}, Qt::QueuedConnection);
|
||||||
|
}
|
69
qt6/mainthread/mainthread.go
Normal file
69
qt6/mainthread/mainthread.go
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
package mainthread
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"runtime/cgo"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
#cgo pkg-config: Qt6Core
|
||||||
|
|
||||||
|
#include "mainthread.h"
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
// Start runs the callback in the main Qt thread. You should use this whenever
|
||||||
|
// accessing the main Qt GUI from inside a goroutine.
|
||||||
|
// This function is non-blocking.
|
||||||
|
func Start(gofunc func()) {
|
||||||
|
h := cgo.NewHandle(gofunc)
|
||||||
|
|
||||||
|
C.mainthread_exec(C.intptr_t(h))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait runs the callback in the main Qt thread. You should use this whenever
|
||||||
|
// accessing the main Qt GUI from inside a goroutine.
|
||||||
|
// The call blocks until the callback is executed in the main thread's eventloop.
|
||||||
|
func Wait(gofunc func()) {
|
||||||
|
// It's possible to use Qt::BlockingQueuedConnection to implement the
|
||||||
|
// blocking, but it has a deadlock risk
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(1)
|
||||||
|
outerfunc := func() {
|
||||||
|
gofunc()
|
||||||
|
wg.Done()
|
||||||
|
}
|
||||||
|
Start(outerfunc)
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
func Wait2[T any](gofunc func() T) (ret T) {
|
||||||
|
outerfunc := func() {
|
||||||
|
ret = gofunc()
|
||||||
|
}
|
||||||
|
Wait(outerfunc)
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func Wait3[T any](gofunc func() (T, error)) (ret T, err error) {
|
||||||
|
outerfunc := func() {
|
||||||
|
ret, err = gofunc()
|
||||||
|
}
|
||||||
|
Wait(outerfunc)
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
|
||||||
|
//export mainthread_exec_handle
|
||||||
|
func mainthread_exec_handle(u uintptr) {
|
||||||
|
h := cgo.Handle(u)
|
||||||
|
|
||||||
|
gofunc, ok := h.Value().(func())
|
||||||
|
if !ok {
|
||||||
|
panic("miqt: callback of non-callback type (heap corruption?)")
|
||||||
|
}
|
||||||
|
|
||||||
|
gofunc()
|
||||||
|
|
||||||
|
// Free handle after use
|
||||||
|
h.Delete()
|
||||||
|
}
|
18
qt6/mainthread/mainthread.h
Normal file
18
qt6/mainthread/mainthread.h
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef QT_MAINTHREAD_H
|
||||||
|
#define QT_MAINTHREAD_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void mainthread_exec(intptr_t cb);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue
Block a user