therecipe / qt

Qt binding for Go (Golang) with support for Windows / macOS / Linux / FreeBSD / Android / iOS / Sailfish OS / Raspberry Pi / AsteroidOS / Ubuntu Touch / JavaScript / WebAssembly
GNU Lesser General Public License v3.0
10.39k stars 737 forks source link

Go 1.13 qt binding installation with modules on/off #1007

Open amlwwalker opened 4 years ago

amlwwalker commented 4 years ago

I've had trouble building one of my projects I haven't worked on in a while, and in the meantime had updated go to go version go1.13.3 darwin/amd64. Once doing so projects were breaking so I pulled the latest master of therecipe/qt and follow the instructions for a new install here

export GO111MODULE=off; xcode-select --install; go get -v github.com/therecipe/qt/cmd/... && $(go env GOPATH)/bin/qtsetup test && $(go env GOPATH)/bin/qtsetup -test=false

however I added the -u flag to the go get command. While its running tests, I firstly get

xcode-select: error: command line tools are already installed, use "Software Update" to install updates

and the the calc example fails with

Undefined symbols for architecture x86_64:
  "QQmlPrivate::qmlregister(QQmlPrivate::RegistrationType, void*)", referenced from:
      QGlobalStatic<(anonymous namespace)::Registry, (anonymous namespace)::(anonymous namespace)::Q_QGS_unitRegistry::innerFunction(), (anonymous namespace)::(anonymous namespace)::Q_QGS_unitRegistry::guard>::operator()() in _x004.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

Reading through some of the other issues, I can't work out whether with go 1.13 you always need go modules turned off when using qtdeploy now (as in I should add it to my make file) or its possible to use go modules for all other libraries I may be using but somehow not for qt/therecipe - or its fine to have them turned on and it will create a sum entry for this library too?

Note: I ran it again, while typing this message and I get

INFO[0000] running: 'qtsetup test darwin' [docker=false] [vagrant=false]
INFO[0000] testing widgets/textedit
ERRO[0069] failed to run command                         _func=RunCmd cmd="go build -p 4 -v -ldflags=all=\"-w\" -trimpath -o /Users/alex/go/src/github.com/therecipe/qt/internal/examples/widgets/textedit/deploy/darwin/textedit.app/Contents/MacOS/textedit -tags=minimal" dir=/Users/alex/go/src/github.com/therecipe/qt/internal/examples/widgets/textedit env="COLORTERM=truecolor PERL_MB_OPT=--install_base \"/Users/alex/perl5\" _=/Users/alex/go/bin/qtsetup PATH=/usr/local/opt/php@8.1/sbin:/usr/local/opt/php@7.1/bin:~/.composer/vendor/:~/.composer/vendor/bin:/usr/local/opt/php@7.1/sbin:/usr/local/opt/php@7.1/bin:/Users/alex/perl5/bin:/usr/local/mysql/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/go/bin:/Users/alex/go/bin:/Users/alex/Library/Android/sdk/platform-toolsATH=Qt/5.8/clang_64/libATH=Qt/5.8/clang_64/bin Apple_PubSub_Socket_Render=/private/tmp/com.apple.launchd.9vLWrIrbAB/Render QT_DIR=/usr/local/opt/qt COLORFGBG=15;0 SQLITE_EXEMPT_PATH_FROM_VNODE_GUARDS=/Users/alex/Library/WebKit/Databases XPC_FLAGS=0x0 PWD=/Users/alex/go/src/github.com/therecipe/qt/internal/examples/quick HOME=/Users/alex PERL_MM_OPT=INSTALL_BASE=/Users/alex/perl5 TERM_SESSION_ID=w0t1p2:69FBE122-40CA-46D3-8FDB-D7B927B1858F SECURITYSESSIONID=186a7 ZSH=/Users/alex/.oh-my-zsh CGO_ENABLED=1 TMPDIR=/var/folders/_t/5kvjckz11tl2hf98svc8zmrc0000gp/T/ AWS_SECRET_ACCESS_KEY=3sSixkIsLEhgoGjq/KOHFSR+lk1f8CDBrHywlLxt S3_ACCESS_KEY=AKIAJDCH3GT24CY42GIA CGO_LDFLAGS_ALLOW=-Wl,-rpath,@executable_path/Frameworks SHLVL=1 GOROOT=/usr/local/go ITERM_PROFILE=Default TERM_PROGRAM_VERSION=3.3.4 LC_TERMINAL=iTerm2 __CF_USER_TEXT_ENCODING=0x0:0:2 EDITOR=vim S3_BUCKET=encrypto LSCOLORS=Gxfxcxdxbxegedabagacad GO111MODULE=off GOOS=darwin LESS=-R PERL_LOCAL_LIB_ROOT=/Users/alex/perl5 LC_TERMINAL_VERSION=3.3.4 LANG=en_GB.UTF-8 AWS_ACCESS_KEY_ID=AKIAJL76RZ363G2P5UGQ GOPATH=/Users/alex/go TERM_PROGRAM=iTerm.app USER=alex XPC_SERVICE_NAME=0 S3_SECRET_KEY=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 GOARCH=amd64 NVM_DIR=/Users/alex/.nvm SECRET_KEY=UrF/MWv4B3MtVIt9Mpsj1+e/Mwetz4IJcDfHmys+ SSH_AUTH_SOCK=/private/tmp/com.apple.launchd.VxC9isUZXW/Listeners LOGNAME=alex PROMPT_COMMAND=hpwd=$(history 1); hpwd=\"${hpwd# *[0-9]*  }\"; if [[ ${hpwd%% *} == \"cd\" ]]; then cwd=$OLDPWD; else cwd=$PWD; fi; hpwd=\"${hpwd% ### *} ### $cwd\"; history -s \"$hpwd\" OLDPWD=/Users/alex/go/src/github.com/therecipe/qt/internal/examples/quick/calc TERM=xterm-256color LC_CTYPE=en_GB.UTF-8 PERL5LIB=/Users/alex/perl5/lib/perl5 SHELL=/bin/zsh ACCESS_KEY=AKIAIBBZ6J3X2DCVBUUA COMMAND_MODE=unix2003 ITERM_SESSION_ID=w0t1p2:69FBE122-40CA-46D3-8FDB-D7B927B1858F PAGER=less" error="exit status 2" name="build for darwin on darwin"
github.com/therecipe/qt/core
github.com/therecipe/qt/gui
github.com/therecipe/qt/widgets
# github.com/therecipe/qt/widgets
widgets-minimal.cpp:2251:64: warning: 'availableGeometry' is deprecated: Use QGuiApplication::screens() [-Wdeprecated-declarations]
/usr/local/Cellar/qt/5.13.0/lib/QtWidgets.framework/Headers/qdesktopwidget.h:88:5: note: 'availableGeometry' has been explicitly marked deprecated here
/usr/local/Cellar/qt/5.13.0/lib/QtCore.framework/Headers/qglobal.h:294:33: note: expanded from macro 'QT_DEPRECATED_X'
/usr/local/Cellar/qt/5.13.0/lib/QtCore.framework/Headers/qcompilerdetection.h:649:55: note: expanded from macro 'Q_DECL_DEPRECATED_X'
widgets-minimal.cpp:2261:44: warning: 'screen' is deprecated: Use QScreen [-Wdeprecated-declarations]
/usr/local/Cellar/qt/5.13.0/lib/QtWidgets.framework/Headers/qdesktopwidget.h:77:5: note: 'screen' has been explicitly marked deprecated here
/usr/local/Cellar/qt/5.13.0/lib/QtCore.framework/Headers/qglobal.h:294:33: note: expanded from macro 'QT_DEPRECATED_X'
/usr/local/Cellar/qt/5.13.0/lib/QtCore.framework/Headers/qcompilerdetection.h:649:55: note: expanded from macro 'Q_DECL_DEPRECATED_X'
github.com/therecipe/qt/printsupport
github.com/therecipe/qt/internal/examples/widgets/textedit
# github.com/therecipe/qt/internal/examples/widgets/textedit
Undefined symbols for architecture x86_64:
  "QQmlPrivate::qmlregister(QQmlPrivate::RegistrationType, void*)", referenced from:
      QGlobalStatic<(anonymous namespace)::Registry, (anonymous namespace)::(anonymous namespace)::Q_QGS_unitRegistry::innerFunction(), (anonymous namespace)::(anonymous namespace)::Q_QGS_unitRegistry::guard>::operator()() in _x007.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

I am also re running the build for my project but now with the -debug flag, however wont print an output here until the standard install/tests are working as that may just fix it anyway

therecipe commented 4 years ago

xcode-select: error: command line tools are already installed, use "Software Update" to install updates

This is just a "warning" and can be ignored.

Undefined symbols for architecture x86_64: "QQmlPrivate::qmlregister(QQmlPrivate::RegistrationType, void*)", referenced from:

This is probably because there were some old rcc* files somewhere in your examples/widgets/textedit folder. This is usually no problem, but I did make some changes a few weeks ago by disabling the use of the quickcompiler for these examples. And if you now happened to have some of these old files, then things might break for you.

To solve this, simply remove all leftover rcc* files inside the internal/examples/... subdirs. Or remove the whole therecipe/qt folder and run

export GO111MODULE=off; xcode-select --install; go get -v github.com/therecipe/qt/cmd/... && $(go env GOPATH)/bin/qtsetup test && $(go env GOPATH)/bin/qtsetup -test=false

again.

Reading through some of the other issues, I can't work out whether with go 1.13 you always need go modules turned off when using qtdeploy now (as in I should add it to my make file) or its possible to use go modules for all other libraries I may be using but somehow not for qt/therecipe - or its fine to have them turned on and it will create a sum entry for this library too?

Go modules complicated the whole install situation quite a bit, and Go now switching from opt-in to opt-out (with Go 1.13) for go modules didn't help either. But generally, go modules are supported by the binding and you can use them or not.

However due to the way go modules work, I would recommend to keep them disabled during development, and only enable them during the deployment.

amlwwalker commented 4 years ago

Thanks for this. I've added GO111MODULE=off to my make file for development. Will wait and see about deployment. There are still intermittent conflicts between structure names when compiling - the moc files sometimes say that I have instantiated structs that it owns - that definately don't exist - I mentioned this before, I wondered if you had a work around yet, or for now I can continue rebuilding again until the conflicts go.

/usr/local/go/pkg/tool/darwin_amd64/compile -o $WORK/b031/_pkg_.a -trimpath "$WORK/b031=>;/Users/alex/go/src/github.com/amlwwalker/project/gui/controller=>github.com/amlwwalker/project/gui/controller" -p github.com/amlwwalker/project/gui/controller -buildid Qp2qsoFk_GuSW42L4IP7/Qp2qsoFk_GuSW42L4IP7 -goversion go1.13.3 -D "" -importcfg $WORK/b031/importcfg -pack -c=4 ./controller.go ./systemtray.go $WORK/b031/_cgo_gotypes.go $WORK/b031/moc.cgo1.go $WORK/b031/moc_cgo_darwin_darwin_amd64.cgo1.go $WORK/b031/_cgo_import.go
# github.com/amlwwalker/project/gui/controller
../../project/gui/controller/controller.go:129:31: cannot use func literal (type func([]*model.CalendarEvent)) as type func(<T>) in argument to c.ConnectDisplayEventDetails
../../project/gui/controller/controller.go:170:41: cannot use model.EventModel.EventsForDate (type func(*core.QDate) []*model.CalendarEvent) as type func(*core.QDate) <T> in argument to c.ConnectEventsForDate
../../project/gui/controller/moc.go:519:68: undefined: colorstring.CalendarEvent
../../project/gui/controller/moc.go:545:54: undefined: colorstring.CalendarEvent
../../project/gui/controller/moc.go:898:75: undefined: colorstring.CalendarEvent
../../project/gui/controller/moc.go:920:64: undefined: colorstring.CalendarEvent
../../project/gui/controller/moc.go:2114:68: undefined: colorstring.CalendarEvent
../../project/gui/controller/moc.go:2125:63: undefined: colorstring.CalendarEvent_ITF
../../project/gui/controller/moc.go:2135:55: undefined: colorstring.CalendarEvent
../../project/gui/controller/moc.go:2146:50: undefined: colorstring.CalendarEvent_ITF
../../project/gui/controller/controller.go:170:41: too many errors

I also get

import cycle not allowed
package github.com/amlwwalker/pickleIt/gui
    imports github.com/amlwwalker/pickleit/gui/controller
    imports github.com/amlwwalker/project/gui/controller

Occuring intermittently, and possibly occurs when I have -debug not set, which would seem very odd, but can't quite tell if that's the case. It seems like its not a true cyclic import because it suggests controller imports controller, which... I think is fine... Thanks otherwise! All good, going to go and try all my other questions out!

therecipe commented 4 years ago

I made some changes regarding the name issues with https://github.com/therecipe/qt/commit/3904ec6ceb7b0f2e042c85450c8ca123a1396fb9 and https://github.com/therecipe/qt/commit/1097424d656c23c9c8372222a6d8f2a7bcef0fa2

Do you get these with GO111MODULE on or off? Also can you maybe test the latest version, if you haven't already? And if it still persists, can you maybe cut it down to a small example so I can reproduce it?

import cycle not allowed

I think it's importing gui -> controller -> controller -> gui

Occuring intermittently, and possibly occurs when I have -debug not set

Can you maybe try to run qtmoc -slow && go run . and check if you can still reproduce the issue?

amlwwalker commented 4 years ago

Hey, So when I run qtmoc -slow && go run . it seems to build, but as I said both issues above are intermittent. I have always now been using GO111MODULE off - this is my build command in my make file

export GO111MODULE=off; QT_DIR=$(QT_DIR) CGO_LDFLAGS_ALLOW=$(CGO_LDFLAGS_ALLOW) qtdeploy -debug -tags "qml" -ldflags "$(LDFLAGS)" test desktop

I can probably have a look at stripping it down but it will be a big task to do so, so that I can be sure its still causing the error and you can get a smaller app. In the mean time, (I'll look tomorrow probably at stripping it down) but the logs are attached from the above command, perhaps they give you a clue - and you can see its got a moc.go issue. I have also put the folder structure of gui below debug-output.txt .

✘  ~/go/src/github.com/amlwwalker/pickleit/gui   gui ●  tree . ├── README.md ├── assets │   ├── icons │   │   ├── generate-iconset.py │   │   ├── icon.png │   │   ├── icon_16x16.png │   │   ├── pickleIt-Logo-432x512.iconset │   │   │   ├── icon_1024x1024.png │   │   │   ├── icon_128x128.png │   │   │   ├── icon_128x128@2x.png │   │   │   ├── icon_16x16.png │   │   │   ├── icon_16x16@2x.png │   │   │   ├── icon_256x256.png │   │   │   ├── icon_256x256@2x.png │   │   │   ├── icon_32x32.png │   │   │   ├── icon_32x32@2x.png │   │   │   ├── icon_512x512.png │   │   │   ├── icon_512x512@2x.png │   │   │   ├── icon_64x64.png │   │   │   ├── icon_64x64@2x.png │   │   │   └── icon_8x8@2x.png │   │   ├── pickleIt-Logo-432x512.png │   │   └── systray.png │   ├── qml │   │   └── assets │   │   ├── add-file.png │   │   ├── band-aid.png │   │   ├── credits.txt │   │   ├── electric-current-symbol.png │   │   ├── eventindicator.png │   │   ├── events.png │   │   ├── eye.png │   │   ├── ic_account_balance_wallet_black_24px.svg │   │   ├── ic_cloud_download_black_24px.svg │   │   ├── ic_cloud_upload_black_24px.svg │   │   ├── ic_code_black_24px.svg │   │   ├── ic_create_black_24px.svg │   │   ├── ic_create_new_folder_black_24px.svg │   │   ├── ic_dashboard_black_24px.svg │   │   ├── ic_delete_forever_black_24px.svg │   │   ├── ic_file_download_black_24px.svg │   │   ├── ic_file_upload_black_24px.svg │   │   ├── ic_folder_black_24px.svg │   │   ├── ic_insert_drive_file_black_24px.svg │   │   ├── ic_invert_colors_black_24px.svg │   │   ├── ic_lock_open_black_24px.svg │   │   ├── ic_lock_outline_black_24px.svg │   │   ├── ic_search_black_24px.svg │   │   ├── ic_share_black_24px.svg │   │   ├── ic_vpn_key_black_24px.svg │   │   ├── icon.png │   │   ├── list.png │   │   ├── patch.png │   │   └── star.png │   ├── rcc.cpp │   ├── rcc.qrc │   └── rcc_cgo_darwin_darwin_amd64.go ├── calendarView │   ├── calendarView.go │   ├── moc.cpp │   ├── moc.go │   ├── moc.h │   ├── moc_cgo_darwin_darwin_amd64.go │   ├── moc_moc.h │   ├── qml │   │   └── CalendarView │   │   ├── CalendarView.qml │   │   ├── GraphView.qml │   │   ├── assets │   │   │   ├── eventindicator.png │   │   │   ├── leftanglearrow.png │   │   │   └── rightanglearrow.png │   │   └── qmldir │   ├── rcc.cpp │   ├── rcc.qrc │   └── rcc_cgo_darwin_darwin_amd64.go ├── controller │   ├── controller.go │   ├── moc.cpp │   ├── moc.go │   ├── moc.h │   ├── moc_cgo_darwin_darwin_amd64.go │   ├── moc_moc.h │   └── systemtray.go ├── darwin │   └── Contents │   ├── Info.plist │   └── Resources │   └── pickle.icns ├── diffdetails.xml ├── gui.qrc ├── main.go ├── makefile ├── model │   ├── database.go │   ├── database_boltdb.go │   ├── database_nodb.go │   ├── eventmodel.go │   ├── listmodel.go │   ├── moc.cpp │   ├── moc.go │   ├── moc.h │   ├── moc_cgo_darwin_darwin_amd64.go │   ├── moc_moc.h │   ├── objects.go │   ├── sortmodel.go │   └── viewmodel.go ├── rcc.cpp ├── rcc_cgo_darwin_darwin_amd64.go └── view ├── detail │   ├── detail_qml.go │   ├── moc.cpp │   ├── moc.go │   ├── moc.h │   ├── moc_cgo_darwin_darwin_amd64.go │   ├── moc_moc.h │   ├── qml │   │   └── Detail │   │   ├── Button.qml │   │   ├── Card │   │   │   └── qml │   │   │   └── Card │   │   │   ├── Card.qml │   │   │   ├── CardDelegate.qml │   │   │   ├── CardStyle.qml │   │   │   ├── ListDividerStyle.qml │   │   │   ├── qmldir │   │   │   └── styles.js │   │   ├── Detail.qml │   │   ├── Stack.qml │   │   ├── moment.js │   │   └── qmldir │   ├── rcc.cpp │   ├── rcc.qrc │   └── rcc_cgo_darwin_darwin_amd64.go ├── dialog │   ├── add_qml.go │   ├── delete_qml.go │   ├── moc.cpp │   ├── moc.go │   ├── moc.h │   ├── moc_cgo_darwin_darwin_amd64.go │   ├── moc_moc.h │   ├── qml │   │   └── Dialog │   │   ├── AddDialog.qml │   │   ├── DeleteDialog.qml │   │   └── qmldir │   ├── rcc.cpp │   ├── rcc.qrc │   └── rcc_cgo_darwin_darwin_amd64.go ├── diff │   ├── diff_qml.go │   ├── moc.cpp │   ├── moc.go │   ├── moc.h │   ├── moc_cgo_darwin_darwin_amd64.go │   ├── moc_moc.h │   ├── qml │   │   └── Diff │   │   ├── Diff.qml │   │   ├── HeaderDelegate.qml │   │   ├── RowDelegate.qml │   │   ├── TableColumnActionDelegate.qml │   │   ├── TableColumnDelegate.qml │   │   ├── TableColumnDelegateButton.qml │   │   └── qmldir │   ├── rcc.cpp │   ├── rcc.qrc │   └── rcc_cgo_darwin_darwin_amd64.go ├── file │   ├── file_qml.go │   ├── moc.cpp │   ├── moc.go │   ├── moc.h │   ├── moc_cgo_darwin_darwin_amd64.go │   ├── moc_moc.h │   ├── qml │   │   └── File │   │   ├── File.qml │   │   └── qmldir │   ├── rcc.cpp │   ├── rcc.qrc │   └── rcc_cgo_darwin_darwin_amd64.go ├── imageScroller │   ├── imageScroller.go │   ├── moc.cpp │   ├── moc.go │   ├── moc.h │   ├── moc_cgo_darwin_darwin_amd64.go │   ├── moc_moc.h │   ├── qml │   │   └── ImageScroller │   │   ├── ImageScroller.qml │   │   └── qmldir │   ├── rcc.cpp │   ├── rcc.qrc │   └── rcc_cgo_darwin_darwin_amd64.go ├── menubar.go ├── moc.cpp ├── moc.go ├── moc.h ├── moc_cgo_darwin_darwin_amd64.go ├── moc_moc.h ├── notifications │   ├── moc.cpp │   ├── moc.go │   ├── moc.h │   ├── moc_cgo_darwin_darwin_amd64.go │   ├── moc_moc.h │   ├── notifications.go │   ├── qml │   │   └── Notifications │   │   ├── Notifications.qml │   │   ├── Toast.qml │   │   ├── ToastManager.qml │   │   └── qmldir │   ├── rcc.cpp │   ├── rcc.qrc │   └── rcc_cgo_darwin_darwin_amd64.go ├── plugins │   ├── moc.cpp │   ├── moc.go │   ├── moc.h │   ├── moc_cgo_darwin_darwin_amd64.go │   ├── moc_moc.h │   ├── plugins.go │   ├── plugins_qml.go │   ├── qml │   │   └── Plugins │   │   ├── Button.qml │   │   ├── Plugins.qml │   │   └── qmldir │   ├── rcc.cpp │   ├── rcc.qrc │   └── rcc_cgo_darwin_darwin_amd64.go ├── qml │   ├── Button.qml │   ├── SplashIndicator.qml │   ├── Splashscreen.qml │   ├── Switch.qml │   ├── View.qml │   └── main.qml ├── rcc.cpp ├── rcc.qrc ├── rcc_cgo_darwin_darwin_amd64.go ├── settings │   └── qml │   └── Settings │   ├── Settings.qml │   └── qmldir ├── stack │   ├── moc.cpp │   ├── moc.go │   ├── moc.h │   ├── moc_cgo_darwin_darwin_amd64.go │   ├── moc_moc.h │   ├── qml │   │   └── Stack │   │   ├── Stack.qml │   │   └── qmldir │   ├── rcc.cpp │   ├── rcc.qrc │   ├── rcc_cgo_darwin_darwin_amd64.go │   └── stack_qml.go ├── status │   ├── moc.cpp │   ├── moc.go │   ├── moc.h │   ├── moc_cgo_darwin_darwin_amd64.go │   ├── moc_moc.h │   ├── qml │   │   └── Status │   │   ├── Button.qml │   │   ├── DropZone.qml │   │   ├── ProgressBar.qml │   │   ├── Slider.qml │   │   ├── Status.qml │   │   ├── Switch.qml │   │   └── qmldir │   ├── rcc.cpp │   ├── rcc.qrc │   ├── rcc_cgo_darwin_darwin_amd64.go │   └── status_qml.go ├── theme │   └── qml │   └── Theme │   ├── Theme.qml │   └── qmldir └── view_qml.go

52 directories, 239 files ~/go/src/github.com/amlwwalker/pickleit/gui   gui ● 

therecipe commented 4 years ago

@amlwwalker Thanks, I will take a look at it tomorrow.

But FYI, the log you uploaded seemed to have contained some AWS+S3 credentials. I edited your comment and re-uploaded the log and also removed the original link from your comment history, but you should probably invalidate the keys anyway.

I will also blacklist the aws env variables so that they won't appear when using -debug in the future.

amlwwalker commented 4 years ago

Ahh damn, thanks for pointing that out, I have them in my zshrc. Must remove! Thanks - There are a load of others in there that I happen to have commented out - could it perhaps not print them unless requested?

Anyway let me know if there is anything in there that helps, otherwise I'll begin making a stripped down version.

Quick question - while here....

When I started out using these bindings, I was using this approach to pass data between Go and Qml:

view.RootContext().SetContextProperty("QmlBridge", qmlBridge)
view.RootContext().SetContextProperty("QmlUser", qmlBridge.User)

Since discovering Qml modules I have been doing everything using controllers and using signals and slots:

_ func(profileLabelText string)                                `signal:"showFileProfile"`
_ func(id, name, description string, selectedDate *core.QDateTime) `signal:"displayDiffDetails"`

Generally this feels nicer, and besides, the first way doesn't seem to work now that I am using an ApplicationWindow. However what was nice about the first way was being able to pass entire objects back and forth. For instance my app has a settings page, and when any field on the settings page is changed I want to update the settings. It seems like a nice way would be each possible setting is a field on an object. When the object changes I can do an onSettingsObjectChanged and deal with the update. However with a signal I can only pass simple data types to/from Qml which would mean the signal to update settings would either have a huge prototype, or a different signal for each setting that can be changed. Is there a way to do either the first approach, or to pass entire Go objects back and forward using slots

Finally, again I'm not sure if its useful - I have uploaded the debug logs from when it intermittently says there is a circular reference. Its strange that its intermittent. I suspect something else because as you mentioned before,

I think it's importing gui -> controller -> controller -> gui

however gui is the top level folder as you can see in the tree in my previous post - I don't think there is anything in there to import really (except main). The fact that its intermittent confuses me. (I removed the env vars this time, but they are now invalidated anyway :)

intermiitent-circular.txt

therecipe commented 4 years ago

could it perhaps not print them unless requested?

Yeah, I will try to hide env variables that are known to contain credentials in the future.

the first way doesn't seem to work now that I am using an ApplicationWindow.

It should work though.

However what was nice about the first way ... pass entire Go objects back and forward using slots

You can actually do both, you can use a property with something like

type CtxObject struct {
    core.QObject

    _ func() `constructor:"init"`

    _ string `property:"someString"`
}

func (t *CtxObject) init() {
    t.SetSomeString("someString")
}

And then listen on the onSomeStringChanged signal in Qml (or bind to it like a usual property), and/or use the ConnectSomeStringChanged in Go. Then to access the property you can use the SomeString and SetSomeString getter/setter in Go. And if you want you can also overwrite/connect the getter/setter functions as well. Also maybe take a look a this example

Properties should work with most primitive types, as well as QObject derived types (such as moc structs). Also to pass more complex types you can use an QVariant and then either manually encapsulate multiple dimensions of maps/slices or if you are on the recent version of the binding you can also directly pass complex go structs/maps/slices to NewQVariant1 as well and then use the variant for the property or the signals/slots. (The values will be copied though, so you won't be acutally able to pass a reference to Qml for pure Go stuff) And if you want to get an Go Type out of an variant you can use, (*QVariant).ToGoType() as well.

Also, I'm currently working on greatly extending the Go/Qml interoperability and once the changes are out, you can directly pass QObject derived types to Qml and then call Go methods on them without the need to declare them first inside the struct. The ultimate goal is to make it possible to pass the Go/(Qml/JS) border, which is currently crossed by these "bridge" functions and moc structs, without really realizing it. You will then be also able to use the whole binding from Qml/JS and create QPushButtons, QMainWindows and all of this stuff, basically your whole application in Qml/JS as well.

I don't think there is anything in there to import really (except main). The fact that its intermittent confuses me

Okay, then it's a qtmoc issue. I think I know where the problem probably occures, but I will need to do some testing. Thanks for the logs btw, I think they are all that I need so far. I will keep you updated.

amlwwalker commented 4 years ago

Hey, thanks, I guess I hadn't made my object a correct instance of the interface QObject. I have been getting confused between using core. QObject and *core. QObject when interfacing in a few situations.

That sounds awesome - the future plans I mean! I'll carry on as I am, my project is going well. I'm tidying up some last bits and then will make a [probably private] repo and I may have some questions as to the best way you would approach some of the things I have done - hope thats ok :) ?

Just FYI the project is getting larger now, and I am getting about 3 intermittently failed bills per successful build due to the issues above now. (i.e I have to build 2 or 3 times to get a successful one) so as projects get larger this seems to cause more issues.

I've been thinking about breaking the project up into plugins using the Qt and Go plugin approaches but I wondered if you had a small example you could share - I thought this may be a solution to larger builds and a) the time it takes to build them and b) the issues I have mentions about intermiitent build fails. This as we have discussed before, seems slightly different when dealing with Qml. My thinking was that if I took this approach, the different views of the app etc (I'm using a tab view) could each be a plugin, that if existed in a certain directory would be loaded in as a tab. This felt like a way to minimise the size of the core build and speed up building and at the same time make the build smaller, resulting in less chance of intermittent collisions that the moc.go files seem to experience. It also seems like a nice touch anyway :) What I haven't been able to work out is where the Qt plugin approach and the Go plugin approach would meet. For instance if I take an example from one of your projects: image

This is the structure of the detail view from your internal/examples/sql/masterdetail_qml/ example. This is the structure all my tabs are taking. If this was to be wrapped up in a plugin then the core of the app doesn't have to compile this every time and at run time would just grab the shared object at runtime and load it in as a tab.

So I guess, if this is a valid approach (and it seems like it could be) then what is the best way to wrap that up in a plugin - in your other examples you load qml code in as a string which seems slightly 'hacked' if you don't mind me saying - when the plugin could be compiled qml (i.e qrc:\\) and then the main app could just have an interface that it expects all plugins to follow to be tabs (for instance).

Would be cool as builds are now taking some time and with the 2/3 fails it can be hard to manage. What do you think?

(Finally, while we are chatting, I have slightly edited the code on the calendar example here. I am trying to get the actual calendar to update when the underlying data changes (in this case add the eventIndicator to the date). This suggests I need to attach to a new object that is then update, however I can't get that to work. Seems also a bit of a hack... This person manually goes forward and back a month but thats a hack too... any thoughts?

therecipe commented 4 years ago

I may have some questions as to the best way you would approach some of the things I have done - hope thats ok :) ?

Sure, no problem

Just FYI the project is getting larger now, and I am getting about 3 intermittently failed bills per successful build

I pushed some changes yesterday, which might solve the issue. The problem is that you probably import the same package under multiple names from different files inside the same package? I think one of them was the bolt package, the other one I can't recall. There is a chance, that the changes I made fix this for you, but the issue still isn't fully solved yet. So maybe, try to update and if you are lucky then it may work for you.

I've been thinking about breaking the project up into plugins using the Qt and Go plugin approaches but I wondered if you had a small example you could share

Here is an example that uses the Go plugin approach https://github.com/therecipe/qt/issues/759#issuecomment-453235535. For a Qt plugin approach, I currently have no example at hand, sorry. But I think you can probably get most stuff working with go plugins as well.

I am trying to get the actual calendar to update when the underlying data changes ...

I will try to take a look tomorrow.

Also btw, I pushed the Qml related changes I wrote about a few day ago as well. You can find some examples here https://github.com/therecipe/examples/tree/master/js

amlwwalker commented 4 years ago

Awesome, I'm gonna look through those examples later, thanks. I prefer to use go plugins anyway! Re the plugin one thats great. Thanks. I got that running, I'm now trying to be a bit more ambitious, few questions... :\

If I create a plugin that has a Qt module (i.e the qmldir etc in the above image) - i like this approach as it wraps all of the qml/qt etc into one package, then can I then somehow pass the resulting object back to the main application which places the module into the main application?

That may sound weird... basically I'm trying to combine your therecipe/qt/internal/examples/quick/dynamic example which imports qml as a string and make an element from the qml, and the plugin example which uses a qt instance of a button etc to make a plugin.

I guess I can create an interface that the plugin adheres to which passes the object that is created the module back to the main application. The main application then somehow can pass this to it's qml (I don't know how I would do this step though).

My main application is just a tab view, so I want to add a tab for each plugin and put the module's content in the tab. It all makes sense except how I add the module programmatically and not in the main apps' Qml to the tabs (I can't do it as Qml because it doesn't exist until well into runtime). I haven't really got my head around using Qt instances and Qml modules together yet (or creating one from the other which is what I guess I have to do here to use a module in a plugin and absorb it into the main app. Does that make sense?

Also, the plugin uses go generate to create both the plugin and the main application. How does this tie into using qtdeploy? Can it be made with Qtdeploy instead (I guess it needs some QtX if there is a module in there). - I'm just wondering if go generate is what is needed when making plugins with Qt/Qml as part of it, or should I/could I/can I use qtdeploy (which seems possibly not as it won't know what a plugin is?) Thanks - I'll post an example of where I'm at later if this is confusing....

amlwwalker commented 4 years ago

As mentioned, I have put together an example of what I am trying to do. It's not working but it should demo what I mean.

The idea is plugin-example-main is a Qt app. It loads a plugin which is a Qt module.

Currently I am just trying to load the plugin from main, however I get

panic: plugin.Open("plugin-example-addon/plugin"): plugin was built with a different version of package github.com/therecipe/qt/core

goroutine 1 [running, locked to thread]:
main.main()
    /Users/alex/go/src/github.com/amlwwalker/example-qml/plugin-example-main/main.go:34 +0x2e

I want to be able to build the main app with qtdeploy test desktop but I got the eror as above, however the library internal/cpu was different for each apparently, so I tried with go generate - which produced the above at runtime. I'm happy building the plugin itself with go generate as we need the .so file. That seems a nice approach actually.

I am hoping that the main application opens a qml app (inside qml/main.qml). Then it loads in the plugin found in plugin-example-addon and this in turn is a module (or provides a module) that the main app can then load into the qml app.

I've sort of started with your examples/quick/dynamic however haven't got far enough to test loading a module into the qml at runtime so don't know if this is the approach to take. Its a bit of a copy paste at the moment, however eventually im hoping that all qml is in qml files, but that is trickier to debug while I can't load the plugin.

Thinking ahead, the thing I'm not sure of the best approach is the import MyModule 1.0 in the qml will need to happen at runtime. I wonder if I need a 'plugin manager' that is a module in its own right that somehow dynamically imports the plugin modules once it loads them, but still I can't see how the best approach is to have a generic plugin/module loader from qml side. This would be a cool thing. I added you as a contributor once again if you had a chance to take a look and advise? Thankss

therecipe commented 4 years ago

I pushed some changes with https://github.com/amlwwalker/qml-plugin-example/commit/df64a1a300ebd56f1ca702617222e752dbb185ba, which should make your example work. And I also added some new examples here https://github.com/therecipe/qt/tree/master/internal/examples/plugins

To make qml modules work, you will just need to load the plugin. The initialization is then done automatically, or with these *_QmlRegisterType2 functions inside the module. I'm not sure if it's possible to load a plugin while the qml engine is running already though, it could work but I haven't tested it.

About these plugin was built with a different version of errors, yeah I will look into some way to work around this, but for now you will need to use something like this https://github.com/amlwwalker/qml-plugin-example/blob/master/main.go#L13-L21 to make it work. Then it will probably be also possible to build your application with qtdeploy, but for now the only way is to manually build it with qtminimal, qtrcc, ... and go build.

amlwwalker commented 4 years ago

Hey, this is fantastic thanks. Your examples all work nicely. Im going to read through the code now on the train a few times. With regard to my example, it seems you are copying main.go from the main application into the plugin to build it, and then removing it at the end, is that correct?

Im going to have a look around and studying the neatest way to import the plugins module dynamically. I think i should be able to create a qt object from it and pass that (with a slot/signal?) to the frontend, say to a stack or tabview etc. Im going to study your mixed example, as I feel it must be possible to load it from the Go side and then use it without having to have an import in qml directly.... Perhaps I'm dreaming! The goal is for every plug in add it to a tabview as a new tab (which means I need to work out how to add a tab for a plugin from the Go side. A challenge in itself)

This is really exciting.

P.S Did you get a chance to think about the calendar refresh by any chance? Otherwise I'll do the next/last month hack

EDIT:

I'm almost there loading the content in a similar fashion to how you have however into an existing element. I think I know exactly where it's going wrong:

if I replace (most) of the content of main.go with:

    core.QCoreApplication_SetAttribute(core.Qt__AA_EnableHighDpiScaling, true)
    widgets.NewQApplication(len(os.Args), os.Args)
    app := qml.NewQQmlApplicationEngine(nil)

    app.AddImportPath("qrc:/qml/")
    app.Load(core.NewQUrl3("./qml/main.qml", 0))

    //now prep to load the plugin.
    view := quick.NewQQuickViewFromPointer(app.RootObjects()[0].Pointer())

    stackLayout := view.RootObject().FindChild("stackLayout", core.Qt__FindChildrenRecursively)

    mainComponent := qml.NewQQmlComponent2(app, nil)
    mainComponent.ConnectStatusChanged(func(status qml.QQmlComponent__Status) {
        if status == qml.QQmlComponent__Ready {

            item := quick.NewQQuickItemFromPointer(mainComponent.Create(app.RootContext()).Pointer()) //create item and "cast" it to QQuickItem
            app.SetObjectOwnership(item, qml.QQmlEngine__JavaScriptOwnership)
            item.SetParent(stackLayout) //add invisible item to invisible parent (for auto-deletion ...)
            item.SetParentItem(view.ContentItem())

        } else {
            fmt.Println("failed with status:", status)
            for _, e := range mainComponent.Errors() {
                fmt.Println("error:", e.ToString())
            }
        }
    })

    qmlString := `
        import QtQuick 2.0
        Item {
            id: rootItem
            anchors.fill: parent
            Component.onCompleted: {
                var subComponent = Qt.createQmlObject(' \
                import Settings 1.0; \
                Settings { \
                    id: settings; \
                    width: parent.width; \
                    height: parent.height;}', rootItem);
            }
        }
    `
    mainComponent.SetData(core.NewQByteArray2(qmlString, -1), core.NewQUrl())
    widgets.QApplication_Exec()

I then get settings loaded into the main.qml

This loads the settings.qml over the top of the whole application and I think thats because of this line: item.SetParentItem(view.ContentItem()). If that were item.SetParentItem(stackLayout) it would work I think. My inspiration came from here.

However, stackLayout doesn't have a contentItem() so that won't work and I can't seem to get a handle on the correct _PTR() or _ITF interface, which the error message suggests is the problem. I think then the above would load the plugin into the stackLayout as a child which looks like

    StackLayout {
      id: stackLayout
      width: parent.width
      implicitHeight: parent.height - bar.height
      anchors.top: bar.bottom

      currentIndex: bar.currentIndex
      Item {
          id: calendarTab
          Rectangle { 
            id: calendar 
            width: parent.width
            height: parent.height
            color: "red"
          }
      }
      Item {
        id: listingTab
          Rectangle {
            id: listing
            width: parent.width
            height: parent.height
            color: "green"
          }
      }
    }

and that would be pretty sweet. I can then use a similar method i guess to add a tab for it as a

      TabButton {
          text: qsTr("Settings")
      }

and hey presto - dynamic adding of tabs!

EDIT 2: Just re pushed, I swear I'm damned close! I got a handle on the pointer but now I see nothing. Based on the inspiration mentioned above, I get a compilation but I can't add to an existing element - or atleast I see nothing. The problem I think is as a result of having an engine and a view and trying to create the Application from the Qml (which I prefer), but it makes it slightly harder to follow the examples that way.

amlwwalker commented 4 years ago

Scratch that I got it working! Very pleased. Depending on when you read this, I will have pushed a working version but I'm in the process of adding a 'dynamic plugin loader' so that the main application doesn't need to know anything about the plugin whatsoever (hard coded qml etc) to load it. Weirdly my code means that the tabs lose their background colour. Not a big deal, but an odd one. Are you copying the files to the plugin so that it can all be generated from one go? I.e if you didn't do that, then it would just be a case of running go generate in both directories?

therecipe commented 4 years ago

Sorry for the delay.

P.S Did you get a chance to think about the calendar refresh by any chance? Otherwise I'll do the next/last month hack

Sorry, I haven't looked into that yet. I will see if I can find some time tomorrow.

Weirdly my code means that the tabs lose their background colour. Not a big deal, but an odd one.

Yeah, I noticed that as well when looking into this last week. What I figured was, that the StackLayout isn't really meant to be used to dynamically add items to it. You can probably use the Qml Loader or Repeater to make it fully work, but I'm not really sure. One workaround I found was to use an SwipeView instead, which has an addItem function to dynamically at items to it.

diff --git a/pluginLoader.go b/pluginLoader.go
index 6e083f2..ade964b 100644
--- a/pluginLoader.go
+++ b/pluginLoader.go
@@ -46,7 +46,7 @@ func (p *PluginManager) Init(engine *qml.QQmlApplicationEngine) {
 const viewTemplate = `
 import {{.Name}} 1.0;
 {{.Name}} {
-   anchors.fill: parent
+   //the item is automatically resized by the SwipeView
 }
 `
 const tabTemplate = `
@@ -163,7 +163,11 @@ func (p *PluginManager) addToGui(childElement, qmlString string) error {
    item := quick.NewQQuickItemFromPointer(mainComponent.Create(p.engine.RootContext()).Pointer())
    p.engine.SetObjectOwnership(item, qml.QQmlEngine__JavaScriptOwnership)
    // //specify the parent
-   item.SetParent(stackLayout)
-   item.SetParentItem(stackLayoutPointer)
+   if childElement == "stackLayout" {
+       stackLayout.InvokeMethod("addToStackLayout", core.NewQVariant1(item))
+   } else {
+       item.SetParent(stackLayout)
+       item.SetParentItem(stackLayoutPointer)
+   }
    return nil
 }
diff --git a/qml/main.qml b/qml/main.qml
index b746f85..e8e8ece 100644
--- a/qml/main.qml
+++ b/qml/main.qml
@@ -30,7 +30,7 @@ ApplicationWindow {
           text: qsTr("Listing")
       }
     }
-    StackLayout {
+    SwipeView {
       id: stackLayout
       objectName: "stackLayout"
       width: parent.width
@@ -62,6 +62,7 @@ ApplicationWindow {
             }
           }
       }
+      function addToStackLayout(item){stackLayout.addItem(item);}
     }
   }
 }

Are you copying the files to the plugin so that it can all be generated from one go? I.e if you didn't do that, then it would just be a case of running go generate in both directories?

Yeah, this is because of the plugin was built with a different version of package issue and because the main.go file contains a moc struct. If there would be no moc struct, then the main.go file could be removed after running qtminimal and qtmoc would only be needed to be run inside the root dir of the project.

therecipe commented 4 years ago

P.S Did you get a chance to think about the calendar refresh by any chance? Otherwise I'll do the next/last month hack

I looked into this, but the solutions I came up with aren't really beautifully either.

diff --git a/events/Events.go b/events/Events.go
index e85e757..f2becf0 100644
--- a/events/Events.go
+++ b/events/Events.go
@@ -136,7 +136,7 @@ func (e *eventController) init() {
        goEve.SetEndDate(et)
        goEve.MoveToThread(e.Thread())
        e.events = append(e.events, goEve)
-       e.Update()
+       e.CalendarUpdate()
        fmt.Println("updating")
    }()
 }
diff --git a/events/qml/Events/Calendar.qml b/events/qml/Events/Calendar.qml
index ea721d6..e9d9e6d 100644
--- a/events/qml/Events/Calendar.qml
+++ b/events/qml/Events/Calendar.qml
@@ -84,13 +84,20 @@ Old.Calendar {
       }

       Image {
-          visible: calendar.parent.eventsForDate(styleData.date).length > 0
+          id: image
+          property int vHelper: 1
+          visible: calendar.parent.eventsForDate(styleData.date).length > 0 || vHelper == 0
           anchors.top: parent.top
           anchors.left: parent.left
           anchors.margins: -1
           width: 12
           height: width
           source: "./assets/eventindicator.png"
+
+          Connections {
+            target: calendar.parent
+            onCalendarUpdate: image.vHelper++;
+          }
       }

       Label {

and

diff --git a/events/Events.go b/events/Events.go
index e85e757..f2becf0 100644
--- a/events/Events.go
+++ b/events/Events.go
@@ -136,7 +136,7 @@ func (e *eventController) init() {
        goEve.SetEndDate(et)
        goEve.MoveToThread(e.Thread())
        e.events = append(e.events, goEve)
-       e.Update()
+       e.CalendarUpdate()
        fmt.Println("updating")
    }()
 }
diff --git a/events/qml/Events/Calendar.qml b/events/qml/Events/Calendar.qml
index ea721d6..685c88c 100644
--- a/events/qml/Events/Calendar.qml
+++ b/events/qml/Events/Calendar.qml
@@ -84,6 +84,7 @@ Old.Calendar {
       }

       Image {
+          id: image
           visible: calendar.parent.eventsForDate(styleData.date).length > 0
           anchors.top: parent.top
           anchors.left: parent.left
@@ -91,6 +92,11 @@ Old.Calendar {
           width: 12
           height: width
           source: "./assets/eventindicator.png"
+
+          Connections {
+            target: calendar.parent
+            onCalendarUpdate: image.visible = Qt.binding(function(){ return calendar.parent.eventsForDate(styleData.date).length > 0 })
+          }
       }

       Label {