jerous86 / nimqt

Qt bindings for nim
GNU General Public License v2.0
93 stars 6 forks source link

How to correctly cast from 'ptr QWidget' to specific type? #12

Closed matkuki closed 1 year ago

matkuki commented 1 year ago

Hi, I have this example code:

import os
import strformat
import std/enumerate

import nimqt
import nimqt / [
    qwidget,
    qlabel,
    qpushbutton,
    qlayoutitem,
    qboxlayout,
    qtabwidget,
    qmainwindow,
    qtextdocument,
    qtextedit,
]

const headerFile0* = "QtWidgets/qlayoutitem.h"
proc widget*(this: ptr QLayoutItem): ptr QWidget {.header:headerFile0, importcpp:"#.widget(@)".}

const headerFile1* = "QtWidgets/qlayout.h"
proc setObjectName*(this: ptr QLayout, name: QString) {.header:headerFile1, importcpp:"#.setObjectName(@)".} # Public
proc objectName*(this: ptr QLayout): QString {.header:headerFile1, importcpp:"#.objectName(@)".} # Public

nimqt.init

## Custom main-window
inheritQObject(CustomMainWindow, QMainWindow):
    slot_decl write_test_message()

proc write_test_message(this: ptr CustomMainWindow) =
    let central_widget = this.centralWidget()
    echo "'", central_widget.objectName(), "'"
    echo "'", central_widget.layout().objectName(), "'"
    echo "'", central_widget.layout().count(), "'"
    for i in 0 ..< central_widget.layout().count():
        let w = central_widget.layout().itemAt(i).widget()
        echo "'", w.objectName(), "'"
        if w.objectName() == Q"MESSAGES":
            var message = Q"-- TEST --"
            let messages = cast[ptr QTextEdit](w)
            messages.append(message) # <---- HERE IS THE PROBLEM
            messages.ensureCursorVisible()

proc main() =
    let
        app = newQApplication(os.commandLineParams())
        main_window = newCustomMainWindow()
        main_widget = newQWidget()
        main_layout = newQVBoxLayout(parent=main_widget)
        tab_widget = newQTabWidget()
        messages = newQTextEdit()
        buttons = [
            newQPushButton(Q "Click me 0!!"),
            newQPushButton(Q "Click me 1!!"),
            newQPushButton(Q "Click me 2!!"),
        ]

    main_window.setObjectName(Q "Main-Window")
    main_window.setWindowTitle(Q "Nim app")

    main_layout.setObjectName(Q "Main-Layout")
    main_layout.setSpacing(0)
    main_layout.setContentsMargins(0, 0, 0, 0)
    main_widget.setObjectName(Q "Central-Widget")
    main_widget.setLayout(main_layout)

    # Tabs
    tab_widget.setObjectName(Q"TABS")
    tab_widget.setMovable(true)
    main_layout.addWidget(tab_widget)

    for i,b in enumerate(buttons):
        b.connect(SIGNAL "clicked()", main_window, SLOT "write_test_message()")
        discard tab_widget.addTab(b, (Q fmt"TAB {i}"))

    # Messages
    messages.setObjectName(Q"MESSAGES")
    messages.setReadOnly(true)
    messages.setMaximumHeight(50)
    messages.document().setMaximumBlockCount(1000)
    main_layout.addWidget(messages)

    main_window.setCentralWidget(main_widget)
    main_window.resize(640, 480)

    messages.append(Q"Initialized widgets.")
    messages.ensureCursorVisible()

    main_window.show()
    discard app.exec()

if isMainModule:
    os.setCurrentDir(os.getAppDir())
    main()

When trying to use a cast object let messages = cast[ptr QTextEdit](w), the usage messages.append(message) (line 43, marked with a comment) throws a segfault:

Traceback (most recent call last)
J:\Nim\nimqt\battle_testing\battle_testing.nim(100) battle_testing
J:\Nim\nimqt\battle_testing\battle_testing.nim(96) main
J:\Nim\nimqt\battle_testing\battle_testing.nim(47) write_test_message
SIGSEGV: Illegal storage access. (Attempt to read from nil?)

Am I doing something wrong?

jerous86 commented 1 year ago

Your code is correct, but apparently, there was no good == operator defined for QString (or it was skipped somehow). Should work now?

matkuki commented 1 year ago

Yes, works, excellent! Thanks 👍