forked from caiorss/Functional-Programming
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathGUI_Graphical_User_Interface_GTK.org
More file actions
858 lines (629 loc) · 34.8 KB
/
Copy pathGUI_Graphical_User_Interface_GTK.org
File metadata and controls
858 lines (629 loc) · 34.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
#+INCLUDE: ../theme/style.org
#+TITLE: Graphical User Interface with Gtk
#+DESCRIPTION: Graphical User Interface with GTK
#+KEYWORDS: gui, haskell, gtk, fp, gtk
#+STARTUP: contents
* Graphical User Interface with GTK
** Overview
GTK is cross-platform GUI toolkit written in C available for Linux,
Mac Osx and Windows. The Haskell package Gtk2HS provides bindings to
it through the foreign function interface FFI.
*Installation*
- 1. Install GTK libraries:
Linux: (Manjaro / Arch Linux)
#+BEGIN_SRC sh
$ sudo pacman -S gobject-introspection gobject-introspection-runtime gtksourceview3 webkitgtk webkit2gtk
#+END_SRC
Install gtk2hs (gtk 2.0) package and Haskell 7.10.2
#+BEGIN_SRC sh
$ stack --resolver lts-3.22 --install-ghc install gtk datetime # Install gtk 2.0 bindings
$ stack --resolver lts-3.22 --install-ghc install gtk3 datetime # Install gtk 3.0 bindings
#+END_SRC
This section will use gtk3 (gtk 3.0) package due to it support glade3 generated
layout files unlike the gtk (gtk 2.0), despite most of documentation and examples be available
for gtk 2.0.
** GUI Terminology
- _Widget_ - GUI component or item such as button, text box, entry,
menu and etc.
- _Container_ - Widget that can allocate space for other widgets and
contain them such as Window, hbox, vbox and so on.
- hbox - aligns widgets in a horizontal way.
- vbox - aligns widgets in a vertical way
- _Event loop_ - Infinite loop generally running in a single thread
which scans for inputs and calls the registered callbacks (aka
event handling) when some event happens. The event loop is
generally not exposed to the application developer.
- _Event_ - Notification provided by the GUI event-loop which can call
an callback function (aka event-handling) registered by the
application programmer.
- _Callback or event handler_ - Function provided by the application
programmer that will be called by the GUI event loop when some
event happens. Example: function called when the user clicks in a
button.
- _Event driven or asyncrhonous_ - GUI Programming is _event-driven_
or _asyncrhonous_. The program doesn't wait for some input to
arrive or poll some input. Instead of doing that the program only
needs to _register the event handlers_ to the events that it will
react. The event loop calls the event handlers associated to an
event when it happens. The drawback of this approach is the loss of
control and the possibility of callback hell.
GUI Challenges:
- GUI design, look and feel are hard to specify programatically
because the code must be recompiled if the GUI is modified, changes
in the GUI may break the GUI code and end-users may want to change
it to fit better their needs. So it is hard to develop a complex GUI
without a GUI builder such as Glade for Gtk, QT Designer for QT and
so on.
- The event handlers or callbacks execution must finish as soon as
possible and avoid _blocking IO_ or perform any task that operation
that blocks GUI event loop thread.
- Any long running computation like run Haskell getLine that waits
the user enter an input or an http request or anything that does
not finish soon or blocks the GUI event loop thread must run in
another thread in order to avoid the GUI crashing or freezing and
it becoming unresponsive.
- Callbacks are written in continuation passing style what makes it
hard to reason about and to compose and it can also lead to
callback-hell, when there is multiple nested callback function.
- Many GUI toolkits like GTK are not thread-safe. The GUI functions
can only be called from the GUI event loop thread.
- The communication between callbacks requires global variables or
global mutable references what can lead to race conditions and
make the code hard to understand and debug.
See:
- [[http://stackoverflow.com/questions/2548200/how-do-i-access-gui-gtk-from-multi-threads][c# - How do I access GUI (GTK) from multi threads? - Stack Overflow]]
- [[http://trampster.blogspot.com.br/2012/10/invoking-on-ui-thread-in-gtk.html][Before Humanity: Invoking on the UI thread in GTK#]]
- [[http://softwareengineering.stackexchange.com/questions/165663/thread-safe-gui-programming][multithreading - Thread safe GUI programming - Software Engineering Stack Exchange]]
- [[https://blogs.oracle.com/swinger/entry/on_gui_multithreading][On GUI Multithreading (Let's Swing!)]]
- [[http://wiki.c2.com/?TheGuiThreadIsTheMainThread][The Gui Thread Is The Main Thread]]
** Gtk3 functions and types
*Package Documentation*
Package Gtk2
- [[https://hackage.haskell.org/package/gtk][gtk Binding to the Gtk+ graphical user interface library.]]
Package Gtk3
- [[https://hackage.haskell.org/package/gtk3][gtk3 Binding to the Gtk+ 3 graphical user interface library]]
*Useful Functions for Gtk*
| Function / type | | Signature | Description |
|-----------------------------+----+-------------------------------------+--------------------------------------------------------------------|
| *Module* Data.IORef | | | |
|-----------------------------+----+-------------------------------------+--------------------------------------------------------------------|
| newIORef | :: | a -> IO (IORef a) | Create new IO Reference initialized with some value. |
| readIORef | :: | IORef a -> IO a | Read IO reference. |
| writeIORef | :: | writeIORef :: IORef a -> a -> IO () | Update IO reference |
| modifyIORef | :: | IORef a -> (a -> a) -> IO () | Apply function to IO reference value and update it. |
| | | | |
| *Module* Control.Concurrent | | | |
|-----------------------------+----+-------------------------------------+--------------------------------------------------------------------|
| forkIO | :: | IO () -> IO ThreadId | Run IO action in a new lightweight thread |
| forkIOS | :: | IO () -> IO ThreadId | Run IO action in a new os (aka native) thread |
| killThread | :: | ThreadId -> IO () | Stop a thread given its ID |
| threadDelay | :: | threadDelay :: Int -> IO () | Pause current thread for N micro seconds (us) or N x 1e-6 seconds. |
| | | | |
*Gtk Functions*
| Function / type | | Singature | Description |
|-----------------------+----+-----------------------------------------------------------------------+-------------------------------------------------|
| initGUI | :: | IO () | Initialize Gtk |
| mainGUI | :: | IO () | Run Gtk event loop. |
| | | | |
| widgetShowAll | :: | WidgetClass self => self -> IO () | |
| | | | |
| *Widget Layout* | | | |
|-----------------------+----+-----------------------------------------------------------------------+-------------------------------------------------|
| vBoxNew | :: | Bool -> Int IO -> VBox | Create new VBox container |
| hBoxNew | :: | Bool -> Int IO -> HBox | Create new HBox container |
| containerAdd | :: | (WidgetClass widget, ContainerClass self) => self -> widget -> IO () | Add widget to a container widget like Window. |
| | | | |
| | | | |
| *Widget Constructors* | | | |
|-----------------------+----+-----------------------------------------------------------------------+-------------------------------------------------|
| windowNew | :: | IO Window | Create new window |
| | | | |
| buttonNew | :: | IO Button | Create new button |
| buttonNewWithLabel | :: | string -> IO Button | Create button with label |
| | | | |
| drawingAreaNew | :: | IO DrawingArea | Create new drawing area (canvas) |
| labelNew | :: | Maybe string -> IO Label | Create new label |
| entryNew | :: | IO Entry | Create new entry (single-line text input) |
| | | | |
| *Window Functions* | | | |
|-----------------------+----+-----------------------------------------------------------------------+-------------------------------------------------|
| windowSetDefaultSize | :: | WindowClass self => self -> Int -> Int -> IO () | Set default window size - window, width, height |
| windowGetDefaultSize | :: | WindowClass self => self -> IO (Int, Int) | Get window default size - (width, height) |
| | | | |
| *Event Handling* | | | |
|-----------------------+----+-----------------------------------------------------------------------+-------------------------------------------------|
| on | :: | object -> Signal object callback -> callback -> IO (ConnectId object) | Register callback |
| |
Note: The type _string_ is:
#+BEGIN_SRC sh
> :t Gtk.labelNew
Gtk.labelNew
:: System.Glib.UTFString.GlibString string =>
Maybe string -> IO Label
>
#+END_SRC
** Examples
*** Simple GUI
:PROPERTIES:
:ID: cbd3ba2f-35c2-492e-88aa-98e6bae4571e
:END:
Screenshot:
[[file:images/haskell_gtk_gui1.png][file:images/haskell_gtk_gui1.png]]
File: gui1.hs
#+BEGIN_SRC haskell
import Graphics.UI.Gtk
import Graphics.UI.Gtk.Gdk.EventM
import qualified Graphics.UI.Gtk.Gdk.Events as E
import Control.Monad.Trans
main :: IO ()
main = do
initGUI -- Start Gtk. Must be called before creating the widgets
-- Create Widgets
window <- windowNew
btn <- buttonNew
set btn [ buttonLabel := "Click me!" ]
containerAdd window btn
widgetShowAll window
-- Set up events and register callbacks (aka event handlers)
--
on btn buttonPressEvent $ tryEvent $ liftIO $ putStrLn "I was clicked"
-- Exit application if the user click on exit button
on window deleteEvent $ liftIO mainQuit >> return False
mainGUI -- Start GTK event loop and and react to events.
#+END_SRC
*Running:*
#+BEGIN_SRC sh
$ stack --resolver lts-3.22 runhaskell /tmp/gui1.hs # Now it works
#+END_SRC
*** GUI with long running task - Clock display
Screenshot:
[[file:images/haskell_gtk_clock_display.png][file:images/haskell_gtk_clock_display.png]]
File: clockDisplay.hs
#+BEGIN_SRC haskell
import Control.Concurrent (forkIO, forkOS, threadDelay)
import Data.IORef
import Control.Monad (forever)
import Control.Monad.Trans (liftIO)
import qualified Data.DateTime as Date
import Graphics.UI.Gtk
-- 1 second dealy = 1 million us = 1 sec
delay1sec = 1000000
ignore :: IO a -> IO ()
ignore ioAction = do
_ <- ioAction
return ()
{-| Run IO action in infinite loop in a new thread,
without block Gtk main thread.
threadLopp delayMilliSeconds GtkIOAction
-}
threadLoop :: Int -> IO () -> IO ()
threadLoop delay ioAction = do
forkIO $ forever $ do
threadsEnter -- Acquire the global Gtk lock
ioAction -- Perform Gtk interaction like update widget
threadsLeave -- Release the global Gtk lock
threadDelay delay -- Delay in us
return ()
driveDisplay :: Entry -> IO ()
driveDisplay entry = do
acc <- newIORef 0
ignore $ forkIO $ forever $ do
counter <- readIORef acc
entrySetText entry $ show counter
writeIORef acc (counter + 1)
threadDelay delay1sec
driveDisplayClock :: Entry -> IO ()
driveDisplayClock entry =
threadLoop delay1sec $ do
localTime <- show . Date.toClockTime <$> Date.getCurrentTime
entrySetText entry $ localTime
main :: IO ()
main = do
initGUI -- Init Gtk
-- Create main window
window <- windowNew
-- Create new user input or entry
display <- entryNew
set window [ windowTitle := "Clock time App"
,windowDefaultWidth := 300
,windowDefaultHeight := 100
,windowResizable := True
,containerBorderWidth := 2
]
-- Add entry to window
containerAdd window display
-- entrySetText display "Display Message"
-- driveDisplay display
driveDisplayClock display
---------------------------------------------
-- Set Signals or Events callbacks --
---------------------------------------------
-- Exit app when the window is deleted (User clicks exit button).
on window deleteEvent $ liftIO mainQuit >> return False
-- Display Widget
widgetShowAll window
-- Start GUI Event loop
mainGUI
#+END_SRC
Running as script:
#+BEGIN_SRC sh
$ stack --resolver lts-3.22 --install-ghc runhaskell clockDisplay.hs
#+END_SRC
Compiling:
#+BEGIN_SRC sh
$ stack --resolver lts-3.22 --install-ghc exec -- ghc -threaded clockDisplay.hs
Linking clockDisplay ...
$ ./clockDisplay
#+END_SRC
*** Build Interface with Glade
Specifying a GUI pragmatically is not viable for a complex GUI
application because each layout change will require the code to be
changed and recompiled. The Glade 3 (glade for gtk3) GUI builder
generate a XML layout containing the GUI specifications. The GUI
library can read it, build the widgets automatically and display the
user interface.
*Screenshot:*
Glade Gtk Builder
[[file:images/glade_builder_screenshot.png][file:images/glade_builder_screenshot.png]]
*GUI application*
[[file:images/haskell_gtk_glade1.png][file:images/haskell_gtk_glade1.png]]
*How it works ?*
The displayed exchange rate BRL/USD (USD to BRL - Brazilian Reals) or
Brazilian Reals per USD is a random number updated every 2 seconds
within the range 3.0 +- 2.0 or 1.0 (3.0 - 2.0) to 5.0 (3.0 + 2.0).
The user enters the amount of USD and the equivalent BRL amount is
displayed every time the user changes in input or the exchange rate is
updated. Example: If the current exchange rate is BRL/USD is 2.0 and
the "amount in USD" is 100.0 the 'Total in BRL' will be 200.0.
When the user press the button 'Sum' current amount in BRL 'Total in BRL' is
add to the reference totalSumRef and the current sum is displayed in
the field 'Total Sum'.
File: Converter.hs
#+BEGIN_SRC haskell
{-# language PackageImports #-}
import Control.Concurrent (forkIO, forkOS, threadDelay)
import Data.IORef
import Control.Monad (forever)
import Control.Monad.Trans
import qualified Data.DateTime as Date
import Graphics.UI.Gtk
import Graphics.UI.Gtk.Builder
import System.Random (randomRIO)
import Text.Read (readMaybe)
import Text.Printf (printf)
parseDouble :: String -> Maybe Double
parseDouble text = readMaybe text
formatDouble :: Double -> String
formatDouble = printf "%.3f"
-- 1 second dealy = 1 million us = 1 sec
delay1sec = 1000000
-- Execute an IO action in a infinite loop and
-- in a regular time interval given in us.
--
clockUpdate :: Int -> IO () -> IO ()
clockUpdate delay ioAction = do
forkIO $ forever $ do
threadsEnter -- Acquire the global Gtk lock
ioAction -- Perform Gtk interaction like update widget
threadsLeave -- Release the global Gtk lock
threadDelay delay -- Delay in us
return ()
driveClockDisplays :: Entry -> Entry -> Entry -> IO ()
driveClockDisplays amountEntry rateDisplay totalSumDisplay = undefined
getAmount :: Entry -> IORef Double -> IO (Maybe Double)
getAmount amountEntry rateRef = do
rate <- readIORef rateRef
amount <- parseDouble <$> entryGetText amountEntry
return $ fmap (\ x -> x * rate) amount
clockUpdateGUI :: Int -> Entry -> Entry -> (IORef Double) -> IO () -> IO ()
clockUpdateGUI delay timeDisplay rateDisplay rateRef action =
clockUpdate delay $ do
rnd <- randomRIO (-2.0, 2.0)
let nextRate = rnd + 3.0
writeIORef rateRef nextRate
action
updateAmount :: Entry -> Entry -> IORef Double -> IO ()
updateAmount amountEntry totalDisplay rateRef = do
rate <- readIORef rateRef
amount <- getAmount amountEntry rateRef
case amount of
Nothing -> entrySetText totalDisplay "Error: Invalid input"
Just amount' -> do let total = rate * amount'
entrySetText totalDisplay (formatDouble total)
updateRate :: Int -> Entry -> Entry -> Entry -> IORef Double -> IO ()
updateRate delay rateDisplay amountEntry totalDisplay rateRef = do
clockUpdate delay $ do
rnd <- randomRIO (-2.0, 2.0)
let nextRate = rnd + 3.0
writeIORef rateRef nextRate
entrySetText rateDisplay $ formatDouble nextRate
updateAmount amountEntry totalDisplay rateRef
updateTimeDisplay :: Entry -> IO ()
updateTimeDisplay entry = do
clockUpdate delay1sec $ do
localTime <- show . Date.toClockTime <$> Date.getCurrentTime
entrySetText entry $ localTime
clearTotalSum :: Entry -> IORef Double -> IO ()
clearTotalSum totalSumDisplay totalSumRef = do
writeIORef totalSumRef 0.0
entrySetText totalSumDisplay "0.0"
putStrLn "Total sum set to 0.0"
updateTotalSum :: Entry -> Entry -> IORef Double -> IORef Double -> IO ()
updateTotalSum amountEntry totalSumDisplay rateRef totalSumRef = do
amount <- getAmount amountEntry rateRef
totalSum <- readIORef totalSumRef
case amount of
Nothing -> return ()
Just amount' -> do let nextTotal = amount' + totalSum
writeIORef totalSumRef nextTotal
entrySetText totalSumDisplay (formatDouble nextTotal)
main :: IO ()
main = do
initGUI -- Start Gtk
-- Application State - (Mutable references )
rateRef <- newIORef 2.5
totalSumRef <- newIORef 0.0
{- ========= Set up widgets and GUI layout ========= -}
-- Load GUI layout from XML file built by glade2 GUI builder.
--
builder <- builderNew
builderAddFromFile builder "converter2.glade"
let getEntry objID = builderGetObject builder castToEntry objID
let getButton objID = builderGetObject builder castToButton objID
window <- builderGetObject builder castToWindow "mainWindow"
buttonSum <- getButton "buttonSum" -- Sum total amount in BRL to total sum
buttonClear <- getButton "buttonClear" -- Clear total sum
amountEntry <- getEntry "amountEntry" -- USD amount entry
rateDisplay <- getEntry "rateDisplay" -- Display exchange rate in USD/BRL
totalDisplay <- getEntry "totalDisplay" -- Display total amount in BRL
totalSumDisplay <- getEntry "totalSumDisplay" -- Display total sum in BRL
timeDisplay <- getEntry "timeDisplay" -- Display user local time
widgetShowAll window
{- ======= Initialize Widget ====== -}
entrySetText amountEntry "3.0"
clearTotalSum totalSumDisplay totalSumRef
{- ======= Set up events ========== -}
on window deleteEvent $ liftIO mainQuit >> return False
-- let updateGUIHandler = updateGUI amountEntry rateDisplay totalDisplay totalSumDisplay rateRef
let updateTotalSumHandler = updateTotalSum amountEntry totalSumDisplay rateRef totalSumRef
let updateAmountHandler = updateAmount amountEntry totalDisplay rateRef
on buttonSum buttonReleaseEvent $ tryEvent $ liftIO $ updateTotalSumHandler
on buttonClear buttonReleaseEvent $ tryEvent $ liftIO $ clearTotalSum totalSumDisplay totalSumRef
on amountEntry keyReleaseEvent $ tryEvent $ liftIO $ updateAmountHandler
{- ===== Run long-running background tasks ====== -}
-- Update time display every 1 second
updateTimeDisplay timeDisplay
-- Update exchange rate each 2 seconds
updateRate (2 * delay1sec) rateDisplay amountEntry totalDisplay rateRef
-- on window deleteEvent $ liftIO mainQuit >> return False
putStrLn "GUI application started. Up and running. OK."
mainGUI -- Start Gtk Event loop
#+END_SRC
File: converter2.glade - Generated by glade3 GUI builder. This file
can be edited in Glade.
#+BEGIN_SRC xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.20.0 -->
<interface>
<requires lib="gtk+" version="3.20"/>
<object class="GtkWindow" id="mainWindow">
<property name="can_focus">False</property>
<property name="default_width">500</property>
<child>
<object class="GtkGrid">
<property name="width_request">400</property>
<property name="height_request">300</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_top">1</property>
<property name="margin_bottom">1</property>
<property name="hexpand">False</property>
<property name="vexpand">False</property>
<property name="row_spacing">5</property>
<property name="column_homogeneous">True</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_right">30</property>
<property name="label" translatable="yes">Exchange Rate BRL/USD</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_right">77</property>
<property name="label" translatable="yes">Amount in USD</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_right">80</property>
<property name="label" translatable="yes">Total in BRL</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">2</property>
</packing>
</child>
<child>
<object class="GtkButton" id="buttonSum">
<property name="label" translatable="yes">Sum</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">3</property>
</packing>
</child>
<child>
<object class="GtkButton" id="buttonClear">
<property name="label" translatable="yes">Clear</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">3</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_right">80</property>
<property name="label" translatable="yes">Total Sum</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">4</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="rateDisplay">
<property name="visible">True</property>
<property name="can_focus">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="amountEntry">
<property name="visible">True</property>
<property name="can_focus">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="totalDisplay">
<property name="visible">True</property>
<property name="can_focus">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">2</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="totalSumDisplay">
<property name="visible">True</property>
<property name="can_focus">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">4</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="timeDisplay">
<property name="visible">True</property>
<property name="can_focus">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">5</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_right">80</property>
<property name="label" translatable="yes">Time</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">5</property>
</packing>
</child>
</object>
</child>
<child>
<placeholder/>
</child>
</object>
</interface>
#+END_SRC
Run as script:
#+BEGIN_SRC sh
$ stack --resolver lts-3.22 --install-ghc runhaskell Converter.hs
Total sum set to 0.0
GUI application started. Up and running. OK.
#+END_SRC
Compile:
#+BEGIN_SRC sh
$ stack --resolver lts-3.22 --install-ghc exec -- ghc -threaded Converter.hs
[1 of 1] Compiling Main ( Converter.hs, Converter.o )
Linking Converter ...
$ ./Converter
Total sum set to 0.0
GUI application started. Up and running. OK.
#+END_SRC
** References and Bookmarks
*Package Documentations*
- [[https://hackage.haskell.org/package/gtk][gtk: Binding to the Gtk+ graphical user interface library.]]
- [[https://hackage.haskell.org/package/gtk3][gtk3: Binding to the Gtk+ 3 graphical user interface library]]
- [[https://github.com/gtk2hs/gtk2hs][GitHub - gtk2hs/gtk2hs: GUI library for Haskell based on GTK+]] (Github)
*GTK Libraries for Windows*
- [[https://sourceforge.net/projects/gtk-win/][GTK+ for Windows Runtime Environment download | SourceForge.net]]
- [[https://sourceforge.net/projects/gtk-mingw/][GTK+ for Windows (MinGW) download | SourceForge.net]]
- [[https://tschoonj.github.io/blog/2014/09/29/gtk2-64-bit-windows-runtime-environment-installer-now-on-github/][Gtk2 64-bit Windows Runtime Environment Installer: now on GitHub! - The Code Dungeon]]
*Misc*
- [[http://book.realworldhaskell.org/read/gui-programming-with-gtk-hs.html][Chapter 23. GUI Programming with gtk2hs]]
- Kenneth Hoste. *An Introduction to Gtk2Hs, a Haskell GUI
Library*. Available at
<https://pdfs.semanticscholar.org/dd9a/8d3986630da6dea10c504c907681fdb3c322.pdf>
- [[https://wiki.haskell.org/Gtk2Hs/Tutorials][Gtk2Hs/Tutorials - HaskellWiki]]
- Krasimir Angelov. *Gtk2Hs Tutorial*
<http://www.cse.chalmers.se/edu/year/2010/course/TDA451_Functional_Programming/FPLectures/06A-gtk2hs.pdf>
- [[http://tab.snarc.org/posts/haskell/2010-11-13-webkit_part1.html][Vincent Hanquez 2010-11-13-webkit_part1]]
- [[http://rickardlindberg.me/writing/reflections-on-programming/2012-02-11-hello-world-haskell-gtk/][Hello world in Haskell and GTK | Rickard's personal homepage]]
- [[http://berlinbrownresearch.blogspot.com.br/2011/03/first-example-ever-on-gtk-and-haskell.html][Berlin Research Code Posts: First example EVER on GTK and Haskell, Render Primitives]]
*Papers*
- Thomas Van Noort. *Building GUIs in Haskell - Comparing Gtk2Hs and
wxHaskell*. Available at <http://foswiki.cs.uu.nl/foswiki/pub/Stc/BuildingGUIsInHaskell/GUI-paper.pdf>
or <https://archive.is/8VRgN>
- John Peterson, Antony Courtney and Bart Robinson. *Can GUI
Programming Be Liberated From The IO Monad*. Available at
<http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.112.2186&rep=rep1&type=pdf>
- Beatriz Alarcón, Salvador Lucas *Building .NET GUIs for Haskell
applications*. Available at
<http://s3.amazonaws.com/academia.edu.documents/41680191/NET06.pdf?AWSAccessKeyId=AKIAIWOWYYGZ2Y53UL3A&Expires=1489302403&Signature=W5tajDKres%2Fv1dgCxuuyyLyK6as%3D&response-content-disposition=inline%3B%20filename%3DBuilding_.NET_GUIs_for_Haskell_applicati.pdf>
- Daan Leijen. *wxHaskell - A Portable and Concise GUI Library for
Haskell*. Available at
<http://www.inf.ufpr.br/andrey/ci062/wxhaskell.pdf>
- Sebastiaan la Fleur. *Outsourcing the Graphical Input/Output using
Streams and Haskell* <http://referaat.cs.utwente.nl/conference/20/paper/7418/outsourcing-the-graphical-input-output-using-streams-and-haskell.pdf>
*Wikipedia*
- [[https://en.wikipedia.org/wiki/GTK%2B][GTK+ - Wikipedia]]
- [[https://en.wikipedia.org/wiki/Continuation-passing_style][Continuation-passing style - Wikipedia]]
- [[https://en.wikipedia.org/wiki/Event_loop][Event loop - Wikipedia]]
- [[https://en.wikipedia.org/wiki/Event_(computing)#Event_handler][Event (computing) - Wikipedia]]
- [[https://en.wikipedia.org/wiki/Asynchronous_I/O][Asynchronous I/O - Wikipedia]]
- [[https://en.wikipedia.org/wiki/Event-driven_programming][Event-driven programming - Wikipedia]]
- [[https://en.wikipedia.org/wiki/Polling_(computer_science)][Polling (computer science) - Wikipedia]]
- [[https://en.wikipedia.org/wiki/Interrupt_handler][Interrupt handler - Wikipedia]]
- [[https://en.wikipedia.org/wiki/Callback_(computer_programming)][Callback (computer programming) - Wikipedia]]
- [[https://en.wikipedia.org/wiki/Windowing_system][Windowing system - Wikipedia]]
- [[https://en.wikipedia.org/wiki/Observer_pattern][Observer pattern - Wikipedia]]
- [[https://en.wikipedia.org/wiki/Event-driven_architecture][Event-driven architecture - Wikipedia]]
- [[https://en.wikipedia.org/wiki/Inversion_of_control][Inversion of control - Wikipedia]]