package main
import (
"time"
"github.com/therecipe/qt/core"
)
func main() {
for i := 0; i < 100000; i++ {
point := core.NewQPoint()
point.DestroyQPoint()
time.Sleep(1)
}
}
Hi,
Using Win7 64-bit. This program will cause a memory leak.
I tried DestroyQPoint(), But it does not work.
Thanks.
@yizheneng
Out of pure curiosity how are you detecting a memory leak here. Looking at the binding code for NewQPoint and DestroyQPoint it is extremely straight forward. Some things to note from Go's perspective.
@rashwell
Thinks for your answer.
Here is my code. https://github.com/yizheneng/MemoryLeakingCode.git
I want to move a window without title bar.
When moved the window it cause this issues.
package widgets
import (
"github.com/therecipe/qt/core"
"github.com/therecipe/qt/gui"
"github.com/therecipe/qt/widgets"
)
type Title struct {
*widgets.QWidget
mainWindows *widgets.QWidget
mouseFlag bool
windowPos *core.QPoint
mousePos *core.QPoint
}
func NewTitle(mainWindows *widgets.QWidget) *Title {
title := &Title{}
title.QWidget = widgets.NewQWidget(nil, 0)
title.mainWindows = mainWindows
title.mouseFlag = false
title.windowPos = nil
title.mousePos = nil
title.SetObjectName("Title")
title.ConnectMousePressEvent(func(event *gui.QMouseEvent) {
title.mouseFlag = true
if title.windowPos != nil {
title.windowPos.DestroyQPoint()
}
if title.mousePos != nil {
title.mousePos.DestroyQPoint()
}
title.windowPos = mainWindows.Pos()
title.mousePos = event.GlobalPos()
})
title.ConnectMouseReleaseEvent(func(event *gui.QMouseEvent) {
title.mouseFlag = false
})
title.ConnectMouseMoveEvent(func(event *gui.QMouseEvent) {
if title.mouseFlag {
mainWindows.Move2(title.windowPos.X()+event.GlobalPos().X()-title.mousePos.X(), title.windowPos.Y()+event.GlobalPos().Y()-title.mousePos.Y())
}
})
return title
}
I did take a look at your example at your repository.
I will try and help you get this example in working shape (prob later this evening).
But I wanted to throw out a couple of notes:
First with your goal in mind check out the RasterWindow Example that is included.
Some quick notes, about how your code though currently is:
First a QWidget which you are trying to subclass and create your own Title bar widget, is just a container, in your current example it doesn't have its own Paint, so it doesn't have anything to latch onto.
Second, when you connect to mouse events, you have to let them move on as well. Otherwise you are interrupting the event stack and it is halting with your routine. So think like this, it is a stack, you want to update your stuff, but then pass the event back onto the default.
Finally, because I see in your code a lot of what I tried the first time, I tried this but you should not do structs like:
type Title struct {
*widgets.QWidget
mainWindows *widgets.QWidget
mouseFlag bool
windowPos *core.QPoint
mousePos *core.QPoint
}
Instead you do like this:
type Title struct {
widgets.QWidget
mainWindows *widgets.QWidget
mouseFlag bool
windowPos *core.QPoint
mousePos *core.QPoint
}
Not a pointer but the actual object you are sub classing. But I'll have to give you a better example this evening because then you would switch NewTitle method more like:
func initTitle(mainWindows *widgets.QWidget) *Title {
title := NewTitle(nil, core.Qt__Widget) // qmoc Automatically will create this NewTitle method
title.QWidget = widgets.NewQWidget(nil, 0)
title.mainWindows = mainWindows
title.mouseFlag = false
title.windowPos = nil
title.mousePos = nil
...
Again Sorry for the quick reply, off to work, but if I get time this evening I can prob get your concept of a frameless window with your own title bar working.
Ok to have a working example for me, I borrowed a lot from the example Advanced Clock
I did my example below with a bit more for your Title Widget, because yours wasn't actually drawing or didn't contain any widgets itself, and when I tried your example from your repository there basically wasn't anything to click on to start the mouse events. If you want to instead use style sheet stuff to style a QWidget that is subclassed you would still have to connect a paint event and gather and use QStyleOption, etc See the bottom of this page QtWidget Background
For now though since I was adding a paint event to your Title Widget, I just colored it there, and gathered the title from your main window and painted it in the Title Widget. It was fun figuring out which DrawText method in this qt library to use to get the title text to center :).
This got me to a working example that I think meets your intent of a frame-less window with your own title bar widget for dragging the window.
To try and troubleshoot your mention of memory leaks though, I tried to use your same logic for gathering the mouse and main window position, and Destroying QPoint etc. But I am afraid I can't recreate your mention of a memory leak.
My guess is that your app crashes because you are listening on mouse events for a title widget that had no drawable area, or object to click on in the first place, so when you tried to get the pos of the event, it simply failed on the Qt side of things because there isn't a position of a widget that has no area defined.
Anyway here is my version of a working example.
Or almost working, it turns out that while this works, on reviewing I shouldn't be resetting the layout of a QMainWindow. But I assume you could just use widgets instead, and create your own SetWindowTitle type of method for the title widget. But since it runs as is hopefully this will give you a start.
package main
import (
"os"
"github.com/therecipe/qt/core"
"github.com/therecipe/qt/gui"
"github.com/therecipe/qt/widgets"
)
// Title a replacement window Title bar widget
type Title struct {
widgets.QWidget
_ func() `constructor:"init"`
render func(painter *gui.QPainter)
parentw *widgets.QMainWindow
mouseFlag bool
windowPos *core.QPoint
mousePos *core.QPoint
}
func (twidget *Title) init() {
twidget.SetObjectName("Title")
twidget.Resize2(800, 50)
twidget.ConnectPaintEvent(twidget.renderNow)
twidget.mouseFlag = false
twidget.windowPos = nil
twidget.mousePos = nil
twidget.ConnectMouseMoveEvent(twidget.moveevent)
twidget.ConnectMousePressEvent(twidget.pressevent)
twidget.ConnectMouseReleaseEvent(twidget.releaseevent)
twidget.render = twidget.renderFunc
}
func (twidget *Title) moveevent(ev *gui.QMouseEvent) {
nMouseState := ev.MouseState()
bLeftButtonPressed := bool(nMouseState == core.Qt__LeftButton)
if bLeftButtonPressed {
if twidget.mouseFlag {
twidget.parentw.Move2(twidget.windowPos.X()+ev.GlobalPos().X()-twidget.mousePos.X(), twidget.windowPos.Y()+ev.GlobalPos().Y()-twidget.mousePos.Y())
}
}
}
func (twidget *Title) pressevent(ev *gui.QMouseEvent) {
nMouseState := ev.MouseState()
bLeftButtonPressed := bool(nMouseState == core.Qt__LeftButton)
if bLeftButtonPressed {
twidget.mouseFlag = true
if twidget.windowPos != nil {
twidget.windowPos.DestroyQPoint()
}
if twidget.mousePos != nil {
twidget.mousePos.DestroyQPoint()
}
twidget.windowPos = twidget.parentw.Pos()
twidget.mousePos = ev.GlobalPos()
}
}
func (twidget *Title) releaseevent(ev *gui.QMouseEvent) {
twidget.mouseFlag = false
}
func (twidget *Title) renderNow(ev *gui.QPaintEvent) {
qr3 := gui.NewQRegion2(0, 0, twidget.Width(), twidget.Height(), gui.QRegion__Rectangle)
twidget.BackingStore().BeginPaint(qr3)
device := twidget.BackingStore().PaintDevice()
painter := gui.NewQPainter2(device)
defer painter.DestroyQPainter()
twidget.render(painter)
twidget.BackingStore().EndPaint()
twidget.BackingStore().Flush(gui.NewQRegion2(0, 0, twidget.Width(), twidget.Height(), gui.QRegion__Rectangle), nil, core.NewQPoint())
}
func (twidget *Title) renderFunc(p *gui.QPainter) {
background := gui.NewQPolygon3([]*core.QPoint{
core.NewQPoint2(0, 0),
core.NewQPoint2(twidget.Width(), 0),
core.NewQPoint2(twidget.Width(), twidget.Height()),
core.NewQPoint2(0, twidget.Height()),
})
backgroundColor := gui.NewQColor3(127, 0, 127, 255)
p.SetPen3(core.Qt__NoPen)
p.SetBrush(gui.NewQBrush3(backgroundColor, 1))
p.Save()
p.DrawConvexPolygon4(background)
p.Restore()
textColor := gui.NewQColor3(230, 230, 230, 255)
windowtitle := twidget.parentw.WindowTitle()
p.SetPen2(textColor)
rect := twidget.Rect()
p.Save()
p.DrawText6(rect, int(core.Qt__AlignCenter), windowtitle, nil)
p.Restore()
}
func main() {
app := widgets.NewQApplication(len(os.Args), os.Args)
mainw := widgets.NewQMainWindow(nil, core.Qt__Widget)
mainw.SetMinimumSize2(800, 600)
mainw.SetWindowFlags(core.Qt__FramelessWindowHint)
titleWidget := NewTitle(mainw, core.Qt__FramelessWindowHint)
titleWidget.parentw = mainw
mainWidget := widgets.NewQWidget(mainw, core.Qt__Widget)
label := widgets.NewQLabel2("Main Widget", mainWidget, core.Qt__Widget)
vbox := widgets.NewQVBoxLayout()
vbox.AddWidget(label, 0, core.Qt__AlignCenter)
mainWidget.SetLayout(vbox)
mainWidget.SetGeometry2(0, titleWidget.Height(), mainw.Width(), mainw.Height()-titleWidget.Height())
gbox := widgets.NewQGridLayout(mainw)
gbox.AddWidget(titleWidget, 0, 0, core.Qt__AlignTop)
gbox.AddWidget(mainWidget, 1, 0, core.Qt__AlignBottom)
mainw.SetWindowTitle("起重机监控中心")
mainw.SetObjectName("MainWindow")
mainw.SetLayout(gbox)
mainw.Show()
app.Exec()
}
I did notice the example OpenGlWidget code leaks memory slowly as it animates.
single file example:
package main
import (
"os"
"github.com/therecipe/qt/core"
"github.com/therecipe/qt/gui"
"github.com/therecipe/qt/widgets"
)
type Window struct {
widgets.QWidget
_ func() `constructor:"init"`
helper *Helper
}
type Helper struct {
core.QObject
_ func() `constructor:"init"`
background *gui.QBrush
circleBrush *gui.QBrush
textFont *gui.QFont
circlePen *gui.QPen
textPen *gui.QPen
}
type GLWidget struct {
widgets.QOpenGLWidget
_ func() `constructor:"init"`
_ func() `slot:"animate"`
Helper *Helper
elapsed int
}
func main() {
widgets.NewQApplication(len(os.Args), os.Args)
fmt := gui.NewQSurfaceFormat()
fmt.SetSamples(4)
gui.QSurfaceFormat_SetDefaultFormat(fmt)
window := NewWindow(nil, 0)
window.Show()
widgets.QApplication_Exec()
}
func (w *Window) init() {
w.helper = NewHelper(w)
w.SetWindowTitle("2D Painting on OpenGL Widgets")
openGL := NewGLWidget(w, 0)
openGL.Helper = w.helper
openGLLabel := widgets.NewQLabel2("OpenGL", w, 0)
openGLLabel.SetAlignment(core.Qt__AlignHCenter)
layout := widgets.NewQGridLayout(nil)
layout.AddWidget(openGL, 0, 1, 0)
layout.AddWidget(openGLLabel, 1, 1, 0)
w.SetLayout(layout)
timer := core.NewQTimer(nil)
timer.ConnectTimeout(openGL.Animate)
timer.Start(50)
}
func (w *GLWidget) init() {
w.SetFixedSize2(200, 200)
w.SetAutoFillBackground(false)
w.ConnectAnimate(w.animate)
w.ConnectPaintEvent(w.paintEvent)
}
func (w *GLWidget) animate() {
w.elapsed = (w.elapsed + 50) % 1000
w.Update()
}
func (w *GLWidget) paintEvent(event *gui.QPaintEvent) {
painter := gui.NewQPainter2(w)
painter.SetRenderHint(gui.QPainter__Antialiasing, true)
w.Helper.Paint(painter, event, w.elapsed)
painter.DestroyQPainter()
}
func (h *Helper) init() {
gradient := gui.NewQLinearGradient2(core.NewQPointF3(50, -20), core.NewQPointF3(80, 20))
gradient.SetColorAt(0, gui.NewQColor2(core.Qt__white))
gradient.SetColorAt(1, gui.NewQColor3(0xa6, 0xce, 0x39, 255))
h.background = gui.NewQBrush3(gui.NewQColor3(64, 32, 64, 255), 1)
h.circleBrush = gui.NewQBrush10(gradient)
h.circlePen = gui.NewQPen3(gui.NewQColor2(core.Qt__black))
h.circlePen.SetWidth(1)
h.textPen = gui.NewQPen3(gui.NewQColor2(core.Qt__white))
h.textFont = gui.NewQFont()
h.textFont.SetPixelSize(50)
}
func (h *Helper) Paint(painter *gui.QPainter, event *gui.QPaintEvent, elapsed int) {
painter.FillRect3(event.Rect(), h.background)
painter.Translate3(100, 100)
painter.Save()
painter.SetBrush(h.circleBrush)
painter.SetPen(h.circlePen)
painter.Rotate(float64(elapsed) * float64(0.03))
r := float64(elapsed) / 1000
n := float64(30)
for i := 0; i < int(n); {
i++
painter.Rotate(30)
factor := (float64(i) + r) / n
radius := 0 + 120*factor
circleRadius := 1 + factor*20
painter.DrawEllipse(core.NewQRectF4(radius, -circleRadius, circleRadius*2, circleRadius*2))
}
painter.Restore()
painter.SetPen(h.textPen)
painter.SetFont(h.textFont)
painter.DrawText6(core.NewQRect4(-50, -50, 100, 100), int(core.Qt__AlignCenter), "Qt", core.NewQRect())
}
I found even window focus cause memory increase, this is a very serious problem.
@yizheneng @rashwell @5k3105
I just fixed some memory leaks with: https://github.com/therecipe/qt/commit/299f21022aa217faace4fae811ddd2ede4e35c9d
Please let me know if this fixes the issues you were experiencing.
I tested the examples posted in here, and they shouldn't leak memory anymore.
Hi @therecipe
I've just started looking into Qt over the past couple of days, and am trying to write a modular "bar" application for Linux, similar to things like Polybar, using this library. I've only just really started working on it, but I noticed some interesting behaviour around memory usage.
I've made other persistent Go applications that make use of native UI frameworks and not really noticed any memory "issues" with them.
All I've made so far is a bar with 2 things on it - a clock, and a button that opens up a menu to trigger shutdown, restart, and log off (for i3 at least). But even with such a small amount of functionality, I'm seeing memory usage start at around 32MiB. I left it running overnight last night as I noticed the memory gradually increasing over time, and it got to about 56MiB overnight.
My question really is, is this normal? I understand that Go and your OS don't necessarily actually free up memory when it's done with it unless there's a more urgent need to do so. But I have observed other Go applications' memory increase, and then decrease again and stay roughly the same. Perhaps the behaviour is normal for Qt and it does something internally that means it's memory increases up to a point and then is either cleared, or just stays the same?
Feel free to take a look at the source code if it is helps. I'm going to try investigate more myself, but I don't know C or C++ to look into that side of it in much depth.
@seeruk
Hey
Yeah, for widget applications the memory usage starts at around 30mb and then usually grows to something between 50-75mb and stays there, this is normal. And your observations match what I observed, the memory is not really freed unless there is an urgent need for it. (If you want to test it, you can force this with stress)
Btw, maybe take a look at this example to see how you can test the function that you suspect is leaking memory.