Search code examples
user-interfacehaskelldialogwidgetgtk2hs

Multiple additions instead of single [gtk2hs]


I have another problem that I can't solve

I have a following code that adds a user (first name, last name and age) into a treeview. It all works well except for one small thing: it works well only when you add an user for the first time. When you hit add and then try to add yet another user it will attempt to add 2 users, then 3, 4 and so on, increasing with each addition. Here's the code that catches the click event:

onClicked (dodajUczBt gui) (dodajUcz gui dbh stores)

and here's the function dodajUcz that handles the click:

dodajUcz gui dbh stores =
    do  entrySetText (nImie gui) ""
        entrySetText (nNazwisko gui) ""
        entrySetText (nWiek gui) ""
        onClicked (cancelAddUczBt gui) (widgetHide (dodajUzDialog gui))
        onClicked (zapiszUczBtn gui) procADD
        windowPresent (dodajUzDialog gui)
    where procADD = do
            ucz <- getUczestnik
            let store = uczestnicy stores
            New.listStoreAppend store ucz
            dlugosc <- New.listStoreGetSize store
            labelSetText (lblLiczbaUcz gui) $ "Liczba uczestników: "++ show dlugosc 
            widgetHide (dodajUzDialog gui)
            addUser ucz dbh
          getUczestnik = do
                                imie <- entryGetText (nImie gui)
                                nazwisko <- entryGetText (nNazwisko gui)
                                wiek <- entryGetText (nWiek gui)
                                let wiek' = read wiek :: Integer
                                return $ Uczestnik 0 imie nazwisko wiek' False

Uczestnik is an algebraic data type and addUser is a function that adds an Uczestnik into database. gui is also an algebraic data type GUI,that holds all the gui elements created by castToXml

It seems to me that widgetHide is the main problem, because even if you don't do anything (just open the dialog and then close it) it'll try to add 2 users the next time you open it.

Any ideas how to solve this problem? Any help would be greatly appreciated :)


Solution

  • Well, my understanding is that:

    1. To show the dialog again, you call dodajUcz from somewhere else in you code.
    2. Every time you add a user, you hide the dialog (and you request that it's shown again)
    3. MOST IMPORTANTLY: every time you evaluate (that is, execute) dodajUcz, you install the event handler for the event 'Clicked' on zapiszUczBtn AGAIN (using onClicked). When you install an event handler for an event, it stays there forever and will get executed every time the event is triggered. if you install it twice, it'll get executed twice. In this particular case, after calling dodajUcz twice, the event handler will get executed twice when the event Clicked is triggered on zapiszUczBtn.

      Using onClicked or any other event handler installer won't remove previous handlers, they get added to the handler stack for that widget and event.

      When you initialise the gui (when you first create it), set the event handlers only once in your code and do not execute that code again. You can pass the gui and the stores as args to procADD then.

      For instance:

      startGUI :: DBH -> IO (GUI, Stores)
      startGUI dbh = do
        gui <- giveMeGUI -- or some other function that returns a gui
        stores <- giveMeStores -- or some other function that returns the stores
        onClicked (cancelAddUczBT gui) (widgetHide (dodaUzDialog gui))
        onClicked (zapiszUczBtn gui) (procADD gui dbh stores)
        return (gui, stores)
      
      dodajUcz gui dbh stores = do
         entrySetText (nImie gui) ""
         entrySetText (nNazwisko gui) ""
         entrySetText (nWiek gui) ""
         windowPresent (dodajUzDialog gui)
      
      procADD gui dbh stores = do
         ...