Help please! Can't get any value from controls

Hi there,
And thanks a million in advance for having the trouble to read my message.

I’m just starting to code with wxPython. I’ve already built a GUI, and it displays just fine. Currently, I’m in the process of building the programme logic. And here are my problems:

  • Neither Combobox.GetSelection() nor .GetCurrentSelection gets me the index of the selected choice. Only -1.
  • Same frustration with either TextCtrl.GetTextLine(0) or .GetValue().

My programme needs to collect data and then store it to a database. I only wrote here after trying to find anything useful over the Net, in vain…

Thanks again for bearing with me.

Without seeing your code, it’s difficult to say what is happening.

If no item has been selected in the ComboBox, GetSelection() will return -1. If an item has been selected, the method will return its index.

Similarly, if the TextCtrl contains no text, GetValue() will return an empty string.

The following code creates a Frame containing a ComboBox, TextCtrl and a Button. When you click on the button it will print the controls’ values to stdout. Try it first without data in the controls, and then again with data:

import wx

class MyFrame(wx.Frame):
    def __init__(self, parent):
        wx.Frame.__init__(self, parent)
        self.SetSize((400, 300))
        self.SetTitle("Test")

        self.panel_1 = wx.Panel(self, wx.ID_ANY)
        sizer_1 = wx.BoxSizer(wx.VERTICAL)
        self.combo_box_1 = wx.ComboBox(self.panel_1, wx.ID_ANY, choices=["Tom", "Dick", "Harry", ""], style=wx.CB_DROPDOWN)
        # self.combo_box_1.SetSelection(0)
        sizer_1.Add(self.combo_box_1, 0, 0, 0)
        self.text_ctrl_1 = wx.TextCtrl(self.panel_1, wx.ID_ANY, "")
        sizer_1.Add(self.text_ctrl_1, 0, wx.TOP, 4)
        self.button_1 = wx.Button(self.panel_1, wx.ID_ANY, "Get Values")
        sizer_1.Add(self.button_1, 0, wx.TOP, 8)
        self.panel_1.SetSizer(sizer_1)
        self.Layout()

        self.Bind(wx.EVT_BUTTON, self.OnGetValues, self.button_1)


    def OnGetValues(self, _event):
        index = self.combo_box_1.GetSelection()
        text = self.combo_box_1.GetValue()
        print(f"ComboBox: {index}  '{text}'")

        text = self.text_ctrl_1.GetValue()
        print(f"TextCtl: '{text}'")


if __name__ == "__main__":
    app = wx.App()
    frame = MyFrame(None)
    frame.Show()
    app.MainLoop()

Tested using wxPython 4.2.1 + Python 3.10.12 + Linux Mint 21.3

1 Like

The code supplied works like a charm.

My code isn’t a whole lot different. The difference, though, is that I opted for using a function outside the class, with as parameters each control, prefixed with “self,” of course, and then getting the needed values within that said function. Would that make a difference?! I’ll try sending the values rather than the controls.

Nope! Didn’t work.

It would be a lot easier to tell what is happening if you could post your code.

> # -*- coding: UTF-8 -*-
> #
> # generated by wxGlade 1.1.0a2 on Fri Apr 12 20:31:59 2024
> #
> 
> import wx
> 
> # begin wxGlade: dependencies
> import wx.grid
> # end wxGlade
> 
> # begin wxGlade: extracode
> # end wxGlade
> 
> import sqlite3
> 
> def storeData(thatCategory, thatPhrase, thoseKeywords, thatContext, thatTranslation):
>     theCategory = thatCategory.GetCurrentSelection()
>     thePhrase = thatPhrase.GetValue()
>     theKeywords = thoseKeywords.GetLineText(0)
>     theContext = thatContext.GetLineText(0)
>     theTranslation = thatTranslation.GetLineText(0)
>     
>     conn = sqlite3.connect("Cairo_Qlq_Idioms.db")
>     cur = conn.cursor()
>     cur.execute('''INSERT INTO phrases (Phrase, Categ_ID) VALUES (?, ?)''', (thePhrase, theCategory))
>     conn.commit()
>     thisPhraseID = cur.lastrowid
>     
>     conn.close()
> 
> 
> class MyFrame(wx.Frame):
>     def __init__(self, *args, **kwds):
>         # begin wxGlade: MyFrame.__init__
>         kwds["style"] = kwds.get("style", 0) | wx.DEFAULT_FRAME_STYLE
>         wx.Frame.__init__(self, *args, **kwds)
>         self.SetSize((640, 576))
>         self.SetTitle(u"التعبيرات القاهرية الدارجة في العامية المصرية")
> 
>         self.window_1 = wx.SplitterWindow(self, wx.ID_ANY)
>         self.window_1.SetMinimumPaneSize(20)
> 
>         self.InputPanel = wx.Panel(self.window_1, wx.ID_ANY)
> 
>         sizer_1 = wx.BoxSizer(wx.VERTICAL)
> 
>         label_1 = wx.StaticText(self.InputPanel, wx.ID_ANY, u"إدخال البيانات")
>         label_1.SetFont(wx.Font(18, wx.FONTFAMILY_ROMAN, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD, 0, ""))
>         sizer_1.Add(label_1, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALL, 2)
> 
>         sizer_2 = wx.BoxSizer(wx.HORIZONTAL)
>         sizer_1.Add(sizer_2, 1, wx.EXPAND, 0)
> 
>         sizer_4 = wx.BoxSizer(wx.VERTICAL)
>         sizer_2.Add(sizer_4, 1, wx.EXPAND, 0)
> 
>         self.theClassesSet = wx.ComboBox(self.InputPanel, wx.ID_ANY, choices=[u"تعبير دارج", u"تعبير خاصّ", u"غريبة نحوية", u"صعوبة ترجمة"], 
>                                          style=wx.CB_READONLY)
>         sizer_4.Add(self.theClassesSet, 0, wx.ALIGN_RIGHT | wx.ALL, 1)
> 
>         self.thePhrase = wx.TextCtrl(self.InputPanel, wx.ID_ANY, "")
>         sizer_4.Add(self.thePhrase, 0, wx.ALL | wx.EXPAND, 1)
> 
>         self.theKeywordsSet = wx.TextCtrl(self.InputPanel, wx.ID_ANY, "")
>         sizer_4.Add(self.theKeywordsSet, 0, wx.ALL | wx.EXPAND, 1)
> 
>         self.theContext = wx.TextCtrl(self.InputPanel, wx.ID_ANY, "")
>         sizer_4.Add(self.theContext, 0, wx.ALL | wx.EXPAND, 1)
> 
>         self.theTranslation = wx.TextCtrl(self.InputPanel, wx.ID_ANY, "")
>         sizer_4.Add(self.theTranslation, 0, wx.ALL | wx.EXPAND, 1)
> 
>         sizer_3 = wx.BoxSizer(wx.VERTICAL)
>         sizer_2.Add(sizer_3, 0, wx.ALL, 1)
> 
>         label_2 = wx.StaticText(self.InputPanel, wx.ID_ANY, u"الفئة: ")
>         label_2.SetFont(wx.Font(10, wx.FONTFAMILY_ROMAN, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, 0, ""))
>         sizer_3.Add(label_2, 0, wx.ALL, 4)
> 
>         label_3 = wx.StaticText(self.InputPanel, wx.ID_ANY, u"العبارة: ")
>         label_3.SetFont(wx.Font(10, wx.FONTFAMILY_ROMAN, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, 0, ""))
>         sizer_3.Add(label_3, 0, wx.ALL, 4)
> 
>         label_4 = wx.StaticText(self.InputPanel, wx.ID_ANY, u"الكلمات الرئيسية: ")
>         label_4.SetFont(wx.Font(10, wx.FONTFAMILY_ROMAN, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, 0, ""))
>         sizer_3.Add(label_4, 0, wx.ALL, 5)
> 
>         label_5 = wx.StaticText(self.InputPanel, wx.ID_ANY, u"سياق / شرح: ")
>         label_5.SetFont(wx.Font(10, wx.FONTFAMILY_ROMAN, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, 0, ""))
>         sizer_3.Add(label_5, 0, wx.ALL, 4)
> 
>         label_6 = wx.StaticText(self.InputPanel, wx.ID_ANY, u"ترجمة: ")
>         label_6.SetFont(wx.Font(10, wx.FONTFAMILY_ROMAN, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, 0, ""))
>         sizer_3.Add(label_6, 0, wx.ALL, 5)
> 
>         self.theCommit = wx.Button(self.InputPanel, wx.ID_ANY, u"حفظ البيانات")
>         self.theCommit.SetFont(wx.Font(10, wx.FONTFAMILY_ROMAN, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, 0, ""))
>         sizer_1.Add(self.theCommit, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALL, 1)
>         self.theCommit.Bind(wx.EVT_BUTTON, storeData(self.theClassesSet, self.thePhrase, self.theKeywordsSet, 
>                                                      self.theContext, self.theTranslation))
> 
>         self.DisplayPanel = wx.Panel(self.window_1, wx.ID_ANY)
> 
>         sizer_5 = wx.BoxSizer(wx.VERTICAL)
> 
>         label_7 = wx.StaticText(self.DisplayPanel, wx.ID_ANY, u"إظهار البيانات")
>         label_7.SetFont(wx.Font(18, wx.FONTFAMILY_ROMAN, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD, 0, ""))
>         sizer_5.Add(label_7, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALL, 2)
> 
>         sizer_6 = wx.BoxSizer(wx.HORIZONTAL)
>         sizer_5.Add(sizer_6, 1, wx.EXPAND, 0)
> 
>         sizer_8 = wx.BoxSizer(wx.VERTICAL)
>         sizer_6.Add(sizer_8, 1, wx.EXPAND, 0)
> 
>         self.theClassesGet = wx.ComboBox(self.DisplayPanel, wx.ID_ANY, choices=[u"تعبير دارج", u"تعبير خاصّ", u"غريبة نحوية", u"صعوبة ترجمة"], 
>                                          style=wx.CB_READONLY)
>         sizer_8.Add(self.theClassesGet, 0, wx.ALIGN_RIGHT | wx.ALL, 1)
> 
>         self.theKeywordsGet = wx.TextCtrl(self.DisplayPanel, wx.ID_ANY, "")
>         sizer_8.Add(self.theKeywordsGet, 0, wx.ALL | wx.EXPAND, 1)
> 
>         sizer_7 = wx.BoxSizer(wx.VERTICAL)
>         sizer_6.Add(sizer_7, 0, wx.ALL, 1)
> 
>         label_8 = wx.StaticText(self.DisplayPanel, wx.ID_ANY, u"الفئة: ")
>         label_8.SetFont(wx.Font(10, wx.FONTFAMILY_ROMAN, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, 0, ""))
>         sizer_7.Add(label_8, 0, wx.ALL, 4)
> 
>         label_9 = wx.StaticText(self.DisplayPanel, wx.ID_ANY, u"الكلمات الرئيسية: ")
>         label_9.SetFont(wx.Font(10, wx.FONTFAMILY_ROMAN, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, 0, ""))
>         sizer_7.Add(label_9, 0, wx.ALL, 5)
> 
>         self.theRetrieve = wx.Button(self.DisplayPanel, wx.ID_ANY, u"استرجاع البيانات")
>         self.theRetrieve.SetFont(wx.Font(10, wx.FONTFAMILY_ROMAN, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, 0, ""))
>         sizer_5.Add(self.theRetrieve, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALL, 1)
> 
>         self.theData = wx.grid.Grid(self.DisplayPanel, wx.ID_ANY)
>         self.theData.SetLayoutDirection(wx.Layout_RightToLeft)
>         self.theData.CreateGrid(10, 0)
>         sizer_5.Add(self.theData, 1, wx.EXPAND, 0)
> 
>         self.DisplayPanel.SetSizer(sizer_5)
> 
>         self.InputPanel.SetSizer(sizer_1)
> 
>         self.window_1.SplitHorizontally(self.InputPanel, self.DisplayPanel)
> 
>         self.Layout()
>         self.Centre()
>         # end wxGlade
> 
> # end of class MyFrame
> 
> class MyApp(wx.App):
>     def OnInit(self):
>         self.frame = MyFrame(None, wx.ID_ANY, "")
>         self.SetTopWindow(self.frame)
>         self.frame.Show()
>         return True
> 
> # end of class MyApp
> 
> if __name__ == "__main__":
>     app = MyApp(0)
>     app.MainLoop()
'''

If you edit the post, put three backtick characters ` before the first line and three backtick characters after the last line and then Save the post, it will stop the forum software from trying to style the code.

Done! :+1:t2: Thanks a lot!

The problem with your code is this line:

self.theCommit.Bind(wx.EVT_BUTTON, storeData(self.theClassesSet, self.thePhrase, self.theKeywordsSet, 
                                                 self.theContext, self.theTranslation))

The Bind() method expects to be passed the name of the function or method that is to be called when the event happens.

In your code, the storeData() function will be triggered immediately the Bind() method is called and not when the event happens.

The usual technique is to pass the Bind() method the name of a method:

self.theCommit.Bind(wx.EVT_BUTTON, self.OnCommit)

then that method would call the storeData() function:

def OnCommit(self, event):
    storeData(self.theClassesSet, self.thePhrase, self.theKeywordsSet, 
                     self.theContext, self.theTranslation)

I can’t thank you enough! Will do that now. :pray:t2:

I think one thing you should consider is how best to divide the functionality between the GUI and the database code.

In your current design you have a separate function which is extracting values from GUI components and then passing those values to the database.

It would be better to have a cleaner division between the two parts. For example, the OnCommit() method could extract the values from the controls and then pass the values to the storeData() function. That function would then only be responsible for doing the database side of the operation. This would also make it easier to test the storeData() function independently of the HCI.

I was in fact contemplating this way of doing things. To be honest, I thought I’d better leave the generated code as is, and do my own stuff outside of it. On the other hand, a better organisation of the code is more important on the long run.

Didn’t you work through the wxGlade tutorial?
Binding and handling events is covered:

Create and Use Source Code — wxGlade 1.0.0 documentation

Create and Use Source Code — wxGlade 1.0.0 documentation

whatever your Gui looks like (or not) never forget handling exceptions to keep the data base somewhat consistent :smirk: