PMunch / wxnim

Nim wrapper for wxWidgets.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Unable to compile controlgallery, when adding even handling to button

teras opened this issue · comments

I am trying to add event handling to button in the controlgallery demo app.

So, I first add this proc:

proc buttonClicked(e: WxCommandEvent) {.cdecl.} =
  echo "Clicked"

and then I change button to

Button -> (wxEVT_BUTTON, buttonClicked): "Click me!"

But when I compile I get this error, any idea?

# nim cpp -r -f controlgallery
Hint: used config file '/usr/local/Cellar/nim/0.19.4/nim/config/nim.cfg' [Conf]
Hint: used config file '/Users/teras/Sources/wxnim/examples/genuimacro/nim.cfg' [Conf]
Hint: system [Processing]
Hint: controlgallery [Processing]
Hint: wx [Processing]
Hint: ospaths [Processing]
Hint: strutils [Processing]
Hint: parseutils [Processing]
Hint: math [Processing]
Hint: bitops [Processing]
Hint: algorithm [Processing]
Hint: unicode [Processing]
../../wxCompile.nim(14, 14) Warning: Compiling without wxWidgetsPath assumes wx-config is in your path [User]
../../wx.nim(15, 12) Warning: use {.experimental: "codeReordering.".} instead; {.noForward.} is deprecated [Deprecated]
Hint: genui [Processing]
Hint: macros [Processing]
CC: wxnim_controlgallery
CC: stdlib_system
CC: wxnim_wx
CC: stdlib_ospaths
CC: stdlib_strutils
CC: stdlib_parseutils
CC: stdlib_math
CC: stdlib_bitops
CC: stdlib_algorithm
CC: stdlib_unicode
CC: wxnim_genui
CC: stdlib_macros
Error: execution of an external compiler program 'clang++ -c  -w `wx-config --cppflags`  -I/usr/local/Cellar/nim/0.19.4/nim/lib -o /Users/teras/.cache/nim/controlgallery_d/wxnim_controlgallery.cpp.o /Users/teras/.cache/nim/controlgallery_d/wxnim_controlgallery.cpp' failed with exit code: 1

In file included from /Users/teras/.cache/nim/controlgallery_d/wxnim_controlgallery.cpp:13:
In file included from /usr/local/include/wx-3.1/wx/wx.h:24:
/usr/local/include/wx-3.1/wx/event.h:522:13: error: data member instantiated with function type 'void (wxCommandEvent *)'
    Functor m_handler;
            ^
/usr/local/include/wx-3.1/wx/event.h:3773:19: note: in instantiation of template class 'wxEventFunctorFunctor<wxEventTypeTag<wxCommandEvent>, void (wxCommandEvent *)>' requested here
                  wxNewEventFunctor(eventType, functor),
                  ^
/Users/teras/.cache/nim/controlgallery_d/wxnim_controlgallery.cpp:377:40: note: in instantiation of function template specialization 'wxEvtHandler::Bind<wxEventTypeTag<wxCommandEvent>, void (wxCommandEvent *)>' requested here
        (*colontmp__1UwySShPsyIc7ISBrwqt0w_5).Bind(wxEVT_BUTTON, buttonClicked_5nwOWdidlrM07GF8178VzQ, ((wxStandardID) -1), ((wxStandardID) -1), NIM_NIL);
                                              ^
In file included from /Users/teras/.cache/nim/controlgallery_d/wxnim_controlgallery.cpp:13:
In file included from /usr/local/include/wx-3.1/wx/wx.h:24:
/usr/local/include/wx-3.1/wx/event.h:490:31: error: cannot initialize a member subobject of type 'const void *' with an rvalue of type 'void (*)(wxCommandEvent *)'
        : m_handler(handler), m_handlerAddr(&handler)
                              ^             ~~~~~~~~
/usr/local/include/wx-3.1/wx/event.h:560:16: note: in instantiation of member function 'wxEventFunctorFunctor<wxEventTypeTag<wxCommandEvent>, void (wxCommandEvent *)>::wxEventFunctorFunctor' requested here
    return new wxEventFunctorFunctor<EventTag, Functor>(func);
               ^
/usr/local/include/wx-3.1/wx/event.h:3773:19: note: in instantiation of function template specialization 'wxNewEventFunctor<wxEventTypeTag<wxCommandEvent>, void (wxCommandEvent *)>' requested here
                  wxNewEventFunctor(eventType, functor),
                  ^
/Users/teras/.cache/nim/controlgallery_d/wxnim_controlgallery.cpp:377:40: note: in instantiation of function template specialization 'wxEvtHandler::Bind<wxEventTypeTag<wxCommandEvent>, void (wxCommandEvent *)>' requested here
        (*colontmp__1UwySShPsyIc7ISBrwqt0w_5).Bind(wxEVT_BUTTON, buttonClicked_5nwOWdidlrM07GF8178VzQ, ((wxStandardID) -1), ((wxStandardID) -1), NIM_NIL);
                                              ^
2 errors generated.

Hmm, strange, it works just fine with exactly the same modifications on my end:

# NOTE: these imports are relative to this folder, this would not be required if wxnim was installed with Nimble
import "../../wx", "../../genui"

# Experimental is required for now, this will become default later
{.experimental.}

# This is required to use spinctrls as wx includes the Nim files but not the C headers
{.emit: "#include <wx/spinctrl.h>" .}

var cbChoices = @["Combobox 1", "Combobox 2", "Combobox 3"]

# This allows us to use these variables before they are declared to create our callbacks
var
  spinner: ptr WxSpinCtrl
  slider: ptr WxSlider
  gauge: ptr WxGauge

# Callbacks needs to be {.cdecl.} to be passed to the bind function in wxWidgets
proc spinnerCallback(e: var WxSpinEvent) {.cdecl.} =
  let val = e.getPosition()
  slider.setValue(val)
  gauge.setValue(val)

proc sliderCallback(e: var WxCommandEvent) {.cdecl.} =
  let val = e.getInt()
  spinner.setValue(val)
  gauge.setValue(val)

proc buttonClicked(e: var WxCommandEvent) {.cdecl.} =
  echo "Button clicked!"

# Generate the GUI
genui:
  mainFrame % Frame(title = "Hello World"):
    Panel | Boxsizer(orient = wxHorizontal):
      StaticBox(label = "Basic controls")[proportion = 1] | StaticBoxSizer(orient = wxVertical):
        Button -> (wxEVT_BUTTON, buttonClicked): "Button"
        CheckBox: "Checkbox"
        TextCtrl(value = "Entry")
        StaticText: "Label"
      Panel[proportion = 2] | Boxsizer(orient = wxVertical):
        StaticBox(label = "Numbers") | StaticBoxSizer(orient = wxVertical):
          spinner % SpinCtrl(min = 0, max = 100) -> (wxEVT_SPINCTRL, spinnerCallback)
          slider % Slider(value = 0, minValue = 0, maxValue = 100) -> (wxEVT_SLIDER, sliderCallback)
          gauge % Gauge(range = 100)
        StaticBox(label = "Lists") | StaticBoxSizer(orient = wxVertical):
          Choice(choices = cbChoices, pos = wxDefaultPosition, size = wxDefaultSize)
          ComboBox(choices = cbChoices)
          RadioButton(style = wxRB_GROUP): "RadioButton 1"
          RadioButton: "RadioButton 2"
          RadioButton: "RadioButton 3"

# Show the main frame and run the main loop
mainFrame.show()
runMainLoop()

If un-comment line 240 in genui.nim it will output what it rewrites it to do you get anything different from this:

when not declared(mainFrame):
  var mainFrame = cnew constructWxFrame(title = "Hello World", parent = nil, id = wxID_ANY)
else:
  mainFrame = cnew constructWxFrame(title = "Hello World", parent = nil, id = wxID_ANY)
var :tmp274109 = cnew constructWxPanel(parent = mainFrame, id = wxID_ANY)
var :tmp274110 = cnew constructWxBoxsizer(orient = wxHorizontal)
setSizer(:tmp274109, :tmp274110)
var :tmp274111 = cnew constructWxStaticBox(label = "Basic controls", parent = :tmp274109,
                                       id = wxID_ANY)
var :tmp274112 = cnew constructWxStaticBoxSizer(orient = wxVertical, box = :tmp274111)
var :tmp274113 = cnew constructWxButton(parent = :tmp274111, id = wxID_ANY,
                                    label = "Button")
bind(:tmp274113, wxEVT_BUTTON, buttonClicked)
add(:tmp274112, :tmp274113, border = 5, flag = wxExpand or wxAll)
var :tmp274114 = cnew constructWxCheckBox(parent = :tmp274111, id = wxID_ANY,
                                      label = "Checkbox")
add(:tmp274112, :tmp274114, border = 5, flag = wxExpand or wxAll)
var :tmp274115 = cnew constructWxTextCtrl(value = "Entry", parent = :tmp274111,
                                      id = wxID_ANY)
add(:tmp274112, :tmp274115, border = 5, flag = wxExpand or wxAll)
var :tmp274116 = cnew constructWxStaticText(parent = :tmp274111, id = wxID_ANY,
                                        label = "Label")
add(:tmp274112, :tmp274116, border = 5, flag = wxExpand or wxAll)
add(:tmp274110, :tmp274112, proportion = 1, border = 5, flag = wxExpand or wxAll)
var :tmp274117 = cnew constructWxPanel(parent = :tmp274109, id = wxID_ANY)
var :tmp274118 = cnew constructWxBoxsizer(orient = wxVertical)
setSizer(:tmp274117, :tmp274118)
var :tmp274119 = cnew constructWxStaticBox(label = "Numbers", parent = :tmp274117,
                                       id = wxID_ANY)
var :tmp274120 = cnew constructWxStaticBoxSizer(orient = wxVertical, box = :tmp274119)
when not declared(spinner):
  var spinner = cnew constructWxSpinCtrl(min = 0, max = 100, parent = :tmp274119,
                                     id = wxID_ANY)
else:
  spinner = cnew constructWxSpinCtrl(min = 0, max = 100, parent = :tmp274119, id = wxID_ANY)
bind(spinner, wxEVT_SPINCTRL, spinnerCallback)
add(:tmp274120, spinner, border = 5, flag = wxExpand or wxAll)
when not declared(slider):
  var slider = cnew constructWxSlider(value = 0, minValue = 0, maxValue = 100, parent = :tmp274119,
                                  id = wxID_ANY)
else:
  slider = cnew constructWxSlider(value = 0, minValue = 0, maxValue = 100, parent = :tmp274119,
                               id = wxID_ANY)
bind(slider, wxEVT_SLIDER, sliderCallback)
add(:tmp274120, slider, border = 5, flag = wxExpand or wxAll)
when not declared(gauge):
  var gauge = cnew constructWxGauge(range = 100, parent = :tmp274119, id = wxID_ANY)
else:
  gauge = cnew constructWxGauge(range = 100, parent = :tmp274119, id = wxID_ANY)
add(:tmp274120, gauge, border = 5, flag = wxExpand or wxAll)
add(:tmp274118, :tmp274120, border = 5, flag = wxExpand or wxAll)
var :tmp274121 = cnew constructWxStaticBox(label = "Lists", parent = :tmp274117,
                                       id = wxID_ANY)
var :tmp274122 = cnew constructWxStaticBoxSizer(orient = wxVertical, box = :tmp274121)
var :tmp274123 = cnew constructWxChoice(choices = cbChoices, pos = wxDefaultPosition,
                                    size = wxDefaultSize, parent = :tmp274121,
                                    id = wxID_ANY)
add(:tmp274122, :tmp274123, border = 5, flag = wxExpand or wxAll)
var :tmp274124 = cnew constructWxComboBox(choices = cbChoices, parent = :tmp274121,
                                      id = wxID_ANY)
add(:tmp274122, :tmp274124, border = 5, flag = wxExpand or wxAll)
var :tmp274125 = cnew constructWxRadioButton(style = wxRB_GROUP, parent = :tmp274121,
    id = wxID_ANY, label = "RadioButton 1")
add(:tmp274122, :tmp274125, border = 5, flag = wxExpand or wxAll)
var :tmp274126 = cnew constructWxRadioButton(parent = :tmp274121, id = wxID_ANY,
    label = "RadioButton 2")
add(:tmp274122, :tmp274126, border = 5, flag = wxExpand or wxAll)
var :tmp274127 = cnew constructWxRadioButton(parent = :tmp274121, id = wxID_ANY,
    label = "RadioButton 3")
add(:tmp274122, :tmp274127, border = 5, flag = wxExpand or wxAll)
add(:tmp274118, :tmp274122, border = 5, flag = wxExpand or wxAll)
add(:tmp274110, :tmp274117, proportion = 2, border = 5, flag = wxExpand or wxAll)

If the output is different then it's genui's fault, if not then it's either wxmac or the wxnim bindings which causes it.

Aha, you didn't have the callback with a var WxCommandEvent argument. If I remove var then I get a similar error.

Did you ever try my fix? And got it to work?

If I remove the "var", indeed compilation works.
But why should we be forced to use var in the first place?

Even if there's some hard requirement to use var, due to library restrictions, maybe this should be documented?

Pretty sure this is because it requires the given type to be a pointer, which var makes it. I guess creating a macro/pragma {.wxCallback.} that checks the argument and adds the cdecl pragma would be possible to avoid similar issues in the future. Either way I agree that this should be documented (which is why I haven't closed this issue yet).