SlideShare a Scribd company logo
1 of 53
© Serokell OÜ, 2020
Introduction to
GUI programming in Haskell
GTK+ Nix Haskell
© Serokell OÜ, 2020
Case Study: Temperature Converter
2
© Serokell OÜ, 2020
The Setup
Part I
© Serokell OÜ, 20204
Installing Dependencies
$ brew install gtk+3
© Serokell OÜ, 20205
© Serokell OÜ, 20206
© Serokell OÜ, 20207
© Serokell OÜ, 20208
$ pkg-config --modversion gtk+-3.0
3.24.23
Too new, and brew does not let us downgrade.
© Serokell OÜ, 20209
Nix to the Rescue
1. Declarative configuration
2. Reliable and reproducible
3. System and Haskell libraries
© Serokell OÜ, 202010
Nix configuration: nixpkgs
let
nixpkgsPin = {
url = https://github.com/nixos/nixpkgs/archive/....tar.gz;
sha256 = "...";
};
pkgs = import (builtins.fetchTarball nixpkgsPin) {};
in
...
default.nix
© Serokell OÜ, 202011
Nix configuration: mkDerivation
pkgs.stdenv.mkDerivation rec {
name = "gui-haskell-app";
src = ./.;
buildInputs = [ ... ];
}
default.nix
© Serokell OÜ, 202012
Nix configuration: build-inputs
buildInputs = [
(pkgs.haskell.packages.ghc865.ghcWithPackages (p: [
p.gi-gtk
]))
pkgs.gtk3
pkgs.pkgconfig
pkgs.gobject-introspection
];
default.nix
© Serokell OÜ, 202013
Nix configuration: miscellaneous
libPath = pkgs.lib.makeLibraryPath buildInputs;
shellHook = ''
export LD_LIBRARY_PATH=${libPath}:$LD_LIBRARY_PATH
export LANG=en_US.UTF-8
'';
LOCALE_ARCHIVE =
if pkgs.stdenv.isLinux
then "${pkgs.glibcLocales}/lib/locale/locale-archive"
else "";
default.nix
© Serokell OÜ, 202014
30 lines of config.
Enter nix-shell
and you're set.
© Serokell OÜ, 2020
ghc ./Main.hs
-outputdir _build
-o gui-haskell-app
-threaded
-Wall
-O2
15
ghcid ./Main.hs
Full Build Development
© Serokell OÜ, 2020
The User Interface
Part II
© Serokell OÜ, 202017
import qualified GI.Gtk as Gtk
main = do
window <- Gtk.windowNew Gtk.WindowTypeToplevel
Gtk.widgetShow window
$ ./gui-haskell-app
(process:23372): Gtk-CRITICAL **: 14:37:37.314:
_gtk_style_provider_private_get_settings: assertion
'GTK_IS_STYLE_PROVIDER_PRIVATE (provider)' failed
Segmentation fault: 11
© Serokell OÜ, 202018
main = do
Just app <- Gtk.applicationNew (Just appId) []
_ <- Gio.onApplicationActivate app (appActivate app)
_ <- Gio.applicationRun app Nothing
return ()
appId = Text.pack "io.serokell.gui-haskell-app"
appActivate :: Gtk.Application -> IO ()
appActivate app = do
window <- Gtk.applicationWindowNew app
Gtk.widgetShowAll window
© Serokell OÜ, 202019
main = do
Just app <- Gtk.applicationNew (Just appId) []
_ <- Gio.onApplicationActivate app (appActivate app)
_ <- Gio.applicationRun app Nothing
return ()
appId = Text.pack "io.serokell.gui-haskell-app"
appActivate :: Gtk.Application -> IO ()
appActivate app = do
window <- Gtk.applicationWindowNew app
Gtk.widgetShowAll window
© Serokell OÜ, 202020
main = do
Just app <- Gtk.applicationNew (Just appId) []
_ <- Gio.onApplicationActivate app (appActivate app)
_ <- Gio.applicationRun app Nothing
return ()
appId = Text.pack "io.serokell.gui-haskell-app"
appActivate :: Gtk.Application -> IO ()
appActivate app = do
window <- Gtk.applicationWindowNew app
Gtk.widgetShow window
© Serokell OÜ, 2020
Checkpoint: Window Created
21
© Serokell OÜ, 202022
appActivate app = do
window <- Gtk.applicationWindowNew app
Gtk.setWindowTitle window (Text.pack "GUI Haskell App")
Gtk.widgetShow window
© Serokell OÜ, 202023
appActivate app = do
window <- Gtk.applicationWindowNew app
Gtk.setWindowTitle window (Text.pack "GUI Haskell App")
entry <- Gtk.entryNew
Gtk.containerAdd window entry
Gtk.widgetShow entry
Gtk.widgetShow window
© Serokell OÜ, 202024
appActivate app = do
window <- Gtk.applicationWindowNew app
Gtk.setWindowTitle window (Text.pack "GUI Haskell App")
_entryC <- addEntry window
_entryF <- addEntry window
Gtk.widgetShow window
addEntry :: Gtk.IsContainer a => a -> IO Gtk.Entry
addEntry container = do
entry <- Gtk.entryNew
Gtk.containerAdd container entry
Gtk.widgetShow entry
return entry
© Serokell OÜ, 202025
$ ./gui-haskell-app
(<unknown>:27651): Gtk-WARNING **: 15:36:57.816:
Attempting to add a widget with type GtkEntry to a
GtkApplicationWindow, but as a GtkBin subclass a
GtkApplicationWindow can only contain one widget at a
time; it already contains a widget of type GtkEntry
© Serokell OÜ, 202026
appActivate app = do
window <- Gtk.applicationWindowNew app
Gtk.setWindowTitle window (Text.pack "GUI Haskell App")
vbox <- Gtk.boxNew Gtk.OrientationVertical 10
Gtk.setWidgetMargin vbox 10
Gtk.containerAdd window vbox
Gtk.widgetShow vbox
_entryC <- addEntry vbox
_entryF <- addEntry vbox
Gtk.widgetShow window
© Serokell OÜ, 202027
© Serokell OÜ, 202028
appActivate app = do
window <- Gtk.applicationWindowNew app
Gtk.setWindowTitle window (Text.pack "GUI Haskell App")
Gtk.setWindowResizable window False
Gtk.setWindowDefaultWidth window 300
...
© Serokell OÜ, 202029
appActivate app = do
...
_entryC <- addEntry (Text.pack "°C") vbox
_entryF <- addEntry (Text.pack "°F") vbox
Gtk.widgetShow window
addEntry :: Gtk.IsContainer a => Text -> a -> IO Gtk.Entry
addEntry labelStr container = ...
© Serokell OÜ, 202030
addEntry labelStr container = do
hbox <- Gtk.boxNew Gtk.OrientationHorizontal 5
entry <- Gtk.entryNew
label <- Gtk.labelNew (Just labelStr)
Gtk.containerAdd hbox entry
Gtk.containerAdd hbox label
Gtk.containerAdd container hbox
Gtk.widgetShow entry
Gtk.widgetShow label
Gtk.widgetShow hbox
return entry
© Serokell OÜ, 202031
addEntry labelStr container = do
hbox <- Gtk.boxNew Gtk.OrientationHorizontal 5
entry <- Gtk.entryNew
label <- Gtk.labelNew (Just labelStr)
Gtk.containerAdd hbox entry
Gtk.containerAdd hbox label
Gtk.containerAdd container hbox
Gtk.widgetShow entry
Gtk.widgetShow label
Gtk.widgetShow hbox
return entry
© Serokell OÜ, 202032
Gtk.setWidgetExpand entry True
Gtk.setEntryXalign entry 1No alignment
© Serokell OÜ, 202033
appActivate app = do
window <- Gtk.applicationWindowNew app
...
vbox <- Gtk.boxNew Gtk.OrientationVertical 10
...
_entryC <- addEntry (Text.pack "°C") vbox
_entryF <- addEntry (Text.pack "°F") vbox
button <- Gtk.buttonNew
Gtk.setButtonLabel button (Text.pack "Get Weather")
Gtk.setWidgetHalign button Gtk.AlignCenter
Gtk.containerAdd vbox button
Gtk.widgetShow button
Gtk.widgetShow window
© Serokell OÜ, 202034
Checkpoint: Widget Layout
© Serokell OÜ, 2020
Application Logic and Behavior
Part III
© Serokell OÜ, 2020
entryC <- addEntry (Text.pack "°C") vbox
entryF <- addEntry (Text.pack "°F") vbox
_ <- Gtk.onEditableChanged entryC $
do s <- Gtk.entryGetText entryC
Gtk.entrySetText entryF (Text.reverse s)
36
© Serokell OÜ, 202037
c_to_f, f_to_c :: Double -> Double
c_to_f = (+32) . (*1.8)
f_to_c = (/1.8) . subtract 32
parseDouble :: Text -> Maybe Double
parseDouble = readMaybe . Text.unpack
renderDouble :: Double -> Text
renderDouble = Text.pack . show @Centi . realToFrac
getWeather :: IO Double
getWeather = do
threadDelay (3 * 1000000)
randomRIO (-30, 30)
© Serokell OÜ, 202038
do s <- Gtk.entryGetText entryC
case parseDouble s of
Nothing -> return ()
Just v ->
let s' = renderDouble (c_to_f v)
in Gtk.entrySetText entryF s'
© Serokell OÜ, 202039
setEntryRelation ::
Gtk.Entry -> (Double -> Double) -> Gtk.Entry -> IO ()
setEntryRelation entrySource conv entryTarget = do
_ <- Gtk.onEditableChanged entrySource $
do s <- Gtk.entryGetText entrySource
case parseDouble s of
Nothing -> return ()
Just v ->
let s' = renderDouble (conv v)
in Gtk.entrySetText entryTarget s'
return ()
entryC <- addEntry (Text.pack "°C") vbox
entryF <- addEntry (Text.pack "°F") vbox
setEntryRelation entryC c_to_f entryF
setEntryRelation entryF f_to_c entryC
© Serokell OÜ, 202040
A Feedback Loop?
© Serokell OÜ, 202041
do target_focused <- Gtk.widgetHasFocus entryTarget
unless target_focused $ do
s <- Gtk.entryGetText entrySource
case parseDouble s of
Nothing -> return ()
Just v ->
let s' = renderDouble (conv v)
in Gtk.entrySetText entryTarget s'
© Serokell OÜ, 202042
Checkpoint: Bidirectional Conversion
© Serokell OÜ, 202043
_ <- Gtk.onButtonClicked button $
do c <- getWeather
Gtk.entrySetText entryC (renderDouble c)
© Serokell OÜ, 202044
_ <- Gtk.onButtonClicked button $
do _ <- forkIO $ do
c <- getWeather
Gtk.entrySetText entryC (renderDouble c)
return ()
© Serokell OÜ, 202045
Gtk.onButtonClicked button $
do _ <- forkIO $ do
c <- getWeather
_ <- GLib.idleAdd GLib.PRIORITY_HIGH_IDLE $ do
_ <- Gtk.entrySetText entryC (renderDouble c)
return False
return ()
return ()
© Serokell OÜ, 202046
Gtk.onButtonClicked button $
do Gtk.widgetSetSensitive button False
_ <- forkIO $ do
c <- getWeather
_ <- GLib.idleAdd GLib.PRIORITY_HIGH_IDLE $ do
_ <- Gtk.entrySetText entryC (renderDouble c)
Gtk.widgetSetSensitive button True
return False
return ()
return ()
© Serokell OÜ, 202047
© Serokell OÜ, 2020
Documentation and Resources
Part IV
© Serokell OÜ, 202049
https://github.com/serokell/gui-haskell-app
dd96394 Unclickable button
fcb6778 Safe multi-threading
703f605 Bad forkIO
def6ebc Button click
e2f212b Break the feedback loop
9264545 Mutual updates
5771689 Convert C to F
ae3d8b7 Application logic functions
b3a3f5b Connect to the editable-changed signal
e60afad Add a button
e182920 Expand/align entries
84ce7ee Entry labels
2c88179 Disable resizing
cc8f4d8 Vertical box
34c037c Create second entry (not visible)
f6f5dbe Add an entry (input field)
f8206a8 Set window title
3657b3a Checkpoint: window created
4ab77ef Initial commit
© Serokell OÜ, 202050
https://developer.gnome.org/gtk3/stable/
© Serokell OÜ, 202051
https://github.com/haskell-gi/haskell-
gi/tree/master/examples/
© Serokell OÜ, 202052
https://hackage.haskell.org/package/gi-gtk
https://hackage.haskell.org/package/gi-gdk
https://hackage.haskell.org/package/gi-glib
https://hackage.haskell.org/package/haskell-gi-base
© Serokell OÜ, 2020
Questions?
GTK+ Nix Haskell

More Related Content

What's hot

Lập trình Java GUI
Lập trình Java GUILập trình Java GUI
Lập trình Java GUI
Ha Bogay
 
bài giảng phân tích thiết kệ thống thông tin (hutech)
bài giảng phân tích thiết kệ thống thông tin (hutech)bài giảng phân tích thiết kệ thống thông tin (hutech)
bài giảng phân tích thiết kệ thống thông tin (hutech)
truong le hung
 

What's hot (20)

Quản lý quy trình phần mềm KHTN
Quản lý quy trình phần mềm KHTNQuản lý quy trình phần mềm KHTN
Quản lý quy trình phần mềm KHTN
 
Lập trình Java GUI
Lập trình Java GUILập trình Java GUI
Lập trình Java GUI
 
Bài giảng kiểm thử xâm nhập PTIT
Bài giảng kiểm thử xâm nhập PTITBài giảng kiểm thử xâm nhập PTIT
Bài giảng kiểm thử xâm nhập PTIT
 
Janus workshop @ RTC2019 Beijing
Janus workshop @ RTC2019 BeijingJanus workshop @ RTC2019 Beijing
Janus workshop @ RTC2019 Beijing
 
BGP on RouterOS7 -Part 1
BGP on RouterOS7 -Part 1BGP on RouterOS7 -Part 1
BGP on RouterOS7 -Part 1
 
Thủy vân số
Thủy vân số Thủy vân số
Thủy vân số
 
Building your own CGN boxes with Linux
Building your own CGN boxes with LinuxBuilding your own CGN boxes with Linux
Building your own CGN boxes with Linux
 
Architecting your WebRTC application for scalability, Arin Sime
Architecting your WebRTC application for scalability, Arin SimeArchitecting your WebRTC application for scalability, Arin Sime
Architecting your WebRTC application for scalability, Arin Sime
 
Thuật toán Slope One (final)
Thuật toán Slope One (final)Thuật toán Slope One (final)
Thuật toán Slope One (final)
 
SIP transfer with Janus/WebRTC @ OpenSIPS 2022
SIP transfer with Janus/WebRTC @ OpenSIPS 2022SIP transfer with Janus/WebRTC @ OpenSIPS 2022
SIP transfer with Janus/WebRTC @ OpenSIPS 2022
 
Cấu trúc android
Cấu trúc androidCấu trúc android
Cấu trúc android
 
Mikrotik pcq
Mikrotik   pcqMikrotik   pcq
Mikrotik pcq
 
bài giảng phân tích thiết kệ thống thông tin (hutech)
bài giảng phân tích thiết kệ thống thông tin (hutech)bài giảng phân tích thiết kệ thống thông tin (hutech)
bài giảng phân tích thiết kệ thống thông tin (hutech)
 
Zabbix Agent - Protocolo SNMP
Zabbix Agent - Protocolo SNMPZabbix Agent - Protocolo SNMP
Zabbix Agent - Protocolo SNMP
 
Slide An toàn mạng nâng cao PTIT
Slide An toàn mạng nâng cao PTITSlide An toàn mạng nâng cao PTIT
Slide An toàn mạng nâng cao PTIT
 
Interactive Voice Con
Interactive Voice ConInteractive Voice Con
Interactive Voice Con
 
Bảo mật ứng dụng web
Bảo mật ứng dụng webBảo mật ứng dụng web
Bảo mật ứng dụng web
 
React native felicaの紹介
React native felicaの紹介React native felicaの紹介
React native felicaの紹介
 
BGP.HE.NET by Walt Wollny
BGP.HE.NET by Walt WollnyBGP.HE.NET by Walt Wollny
BGP.HE.NET by Walt Wollny
 
First and Third-Party Cookies
First and Third-Party CookiesFirst and Third-Party Cookies
First and Third-Party Cookies
 

Similar to Vladislav Zavialov – Introduction to GUI programming in Haskell

Google Developer Fest 2010
Google Developer Fest 2010Google Developer Fest 2010
Google Developer Fest 2010
Chris Ramsdale
 
Zero-Copy Compositing in WebKitGTK+ for GUADEC 2015 (GUADEC 2015)
Zero-Copy Compositing in WebKitGTK+ for GUADEC 2015 (GUADEC 2015)Zero-Copy Compositing in WebKitGTK+ for GUADEC 2015 (GUADEC 2015)
Zero-Copy Compositing in WebKitGTK+ for GUADEC 2015 (GUADEC 2015)
Igalia
 

Similar to Vladislav Zavialov – Introduction to GUI programming in Haskell (20)

Full Stack Visualization: Build A React App With A Sankey Diagram
Full Stack Visualization: Build A React App With A Sankey DiagramFull Stack Visualization: Build A React App With A Sankey Diagram
Full Stack Visualization: Build A React App With A Sankey Diagram
 
Odoo development workflow with pip and virtualenv
Odoo development workflow with pip and virtualenvOdoo development workflow with pip and virtualenv
Odoo development workflow with pip and virtualenv
 
Make Testing Groovy
Make Testing GroovyMake Testing Groovy
Make Testing Groovy
 
Visualizing Large Greenhouse Gas Datasets in the Browser With deck.gl
Visualizing Large Greenhouse Gas Datasets in the Browser With deck.glVisualizing Large Greenhouse Gas Datasets in the Browser With deck.gl
Visualizing Large Greenhouse Gas Datasets in the Browser With deck.gl
 
Customizing Cisco Collaboration Devices - CL20B - DEVNET-2071
Customizing Cisco Collaboration Devices - CL20B - DEVNET-2071Customizing Cisco Collaboration Devices - CL20B - DEVNET-2071
Customizing Cisco Collaboration Devices - CL20B - DEVNET-2071
 
How Do I Contribute?
How Do I Contribute?How Do I Contribute?
How Do I Contribute?
 
Android data binding
Android data bindingAndroid data binding
Android data binding
 
2022 SF Summit - Improving Developer Experience with CDK
2022 SF Summit - Improving Developer Experience with CDK2022 SF Summit - Improving Developer Experience with CDK
2022 SF Summit - Improving Developer Experience with CDK
 
Scott Anderson [InfluxData] | Flux Alerts and Notifications | InfluxDays NA 2021
Scott Anderson [InfluxData] | Flux Alerts and Notifications | InfluxDays NA 2021Scott Anderson [InfluxData] | Flux Alerts and Notifications | InfluxDays NA 2021
Scott Anderson [InfluxData] | Flux Alerts and Notifications | InfluxDays NA 2021
 
Integrated, Automated Video Room Systems - Webex Devices - Cisco Live Orlando...
Integrated, Automated Video Room Systems - Webex Devices - Cisco Live Orlando...Integrated, Automated Video Room Systems - Webex Devices - Cisco Live Orlando...
Integrated, Automated Video Room Systems - Webex Devices - Cisco Live Orlando...
 
Apollo. The client we deserve
Apollo. The client we deserveApollo. The client we deserve
Apollo. The client we deserve
 
Google Developer Fest 2010
Google Developer Fest 2010Google Developer Fest 2010
Google Developer Fest 2010
 
Webex Devices xAPI - DEVNET_2071 - Cisco Live - San Diego 2019
Webex Devices xAPI - DEVNET_2071 - Cisco Live - San Diego 2019Webex Devices xAPI - DEVNET_2071 - Cisco Live - San Diego 2019
Webex Devices xAPI - DEVNET_2071 - Cisco Live - San Diego 2019
 
Altinity Quickstart for ClickHouse
Altinity Quickstart for ClickHouseAltinity Quickstart for ClickHouse
Altinity Quickstart for ClickHouse
 
Kubeflow on google kubernetes engine
Kubeflow on google kubernetes engineKubeflow on google kubernetes engine
Kubeflow on google kubernetes engine
 
Zero-Copy Compositing in WebKitGTK+ for GUADEC 2015 (GUADEC 2015)
Zero-Copy Compositing in WebKitGTK+ for GUADEC 2015 (GUADEC 2015)Zero-Copy Compositing in WebKitGTK+ for GUADEC 2015 (GUADEC 2015)
Zero-Copy Compositing in WebKitGTK+ for GUADEC 2015 (GUADEC 2015)
 
Sbt baby steps
Sbt baby stepsSbt baby steps
Sbt baby steps
 
JBoss World 2010
JBoss World 2010JBoss World 2010
JBoss World 2010
 
Devoxx UK 2022 - Application security: What should the attack landscape look ...
Devoxx UK 2022 - Application security: What should the attack landscape look ...Devoxx UK 2022 - Application security: What should the attack landscape look ...
Devoxx UK 2022 - Application security: What should the attack landscape look ...
 
Introduction to Vitess on Kubernetes for MySQL - Webinar
Introduction to Vitess on Kubernetes for MySQL -  WebinarIntroduction to Vitess on Kubernetes for MySQL -  Webinar
Introduction to Vitess on Kubernetes for MySQL - Webinar
 

Recently uploaded

SURVEY I created for uni project research
SURVEY I created for uni project researchSURVEY I created for uni project research
SURVEY I created for uni project research
CaitlinCummins3
 
SPLICE Working Group: Reusable Code Examples
SPLICE Working Group:Reusable Code ExamplesSPLICE Working Group:Reusable Code Examples
SPLICE Working Group: Reusable Code Examples
Peter Brusilovsky
 

Recently uploaded (20)

male presentation...pdf.................
male presentation...pdf.................male presentation...pdf.................
male presentation...pdf.................
 
An overview of the various scriptures in Hinduism
An overview of the various scriptures in HinduismAn overview of the various scriptures in Hinduism
An overview of the various scriptures in Hinduism
 
OSCM Unit 2_Operations Processes & Systems
OSCM Unit 2_Operations Processes & SystemsOSCM Unit 2_Operations Processes & Systems
OSCM Unit 2_Operations Processes & Systems
 
TỔNG HỢP HƠN 100 ĐỀ THI THỬ TỐT NGHIỆP THPT TOÁN 2024 - TỪ CÁC TRƯỜNG, TRƯỜNG...
TỔNG HỢP HƠN 100 ĐỀ THI THỬ TỐT NGHIỆP THPT TOÁN 2024 - TỪ CÁC TRƯỜNG, TRƯỜNG...TỔNG HỢP HƠN 100 ĐỀ THI THỬ TỐT NGHIỆP THPT TOÁN 2024 - TỪ CÁC TRƯỜNG, TRƯỜNG...
TỔNG HỢP HƠN 100 ĐỀ THI THỬ TỐT NGHIỆP THPT TOÁN 2024 - TỪ CÁC TRƯỜNG, TRƯỜNG...
 
Basic Civil Engineering notes on Transportation Engineering & Modes of Transport
Basic Civil Engineering notes on Transportation Engineering & Modes of TransportBasic Civil Engineering notes on Transportation Engineering & Modes of Transport
Basic Civil Engineering notes on Transportation Engineering & Modes of Transport
 
The Story of Village Palampur Class 9 Free Study Material PDF
The Story of Village Palampur Class 9 Free Study Material PDFThe Story of Village Palampur Class 9 Free Study Material PDF
The Story of Village Palampur Class 9 Free Study Material PDF
 
Mattingly "AI and Prompt Design: LLMs with NER"
Mattingly "AI and Prompt Design: LLMs with NER"Mattingly "AI and Prompt Design: LLMs with NER"
Mattingly "AI and Prompt Design: LLMs with NER"
 
SURVEY I created for uni project research
SURVEY I created for uni project researchSURVEY I created for uni project research
SURVEY I created for uni project research
 
ĐỀ THAM KHẢO KÌ THI TUYỂN SINH VÀO LỚP 10 MÔN TIẾNG ANH FORM 50 CÂU TRẮC NGHI...
ĐỀ THAM KHẢO KÌ THI TUYỂN SINH VÀO LỚP 10 MÔN TIẾNG ANH FORM 50 CÂU TRẮC NGHI...ĐỀ THAM KHẢO KÌ THI TUYỂN SINH VÀO LỚP 10 MÔN TIẾNG ANH FORM 50 CÂU TRẮC NGHI...
ĐỀ THAM KHẢO KÌ THI TUYỂN SINH VÀO LỚP 10 MÔN TIẾNG ANH FORM 50 CÂU TRẮC NGHI...
 
Observing-Correct-Grammar-in-Making-Definitions.pptx
Observing-Correct-Grammar-in-Making-Definitions.pptxObserving-Correct-Grammar-in-Making-Definitions.pptx
Observing-Correct-Grammar-in-Making-Definitions.pptx
 
An Overview of the Odoo 17 Knowledge App
An Overview of the Odoo 17 Knowledge AppAn Overview of the Odoo 17 Knowledge App
An Overview of the Odoo 17 Knowledge App
 
Improved Approval Flow in Odoo 17 Studio App
Improved Approval Flow in Odoo 17 Studio AppImproved Approval Flow in Odoo 17 Studio App
Improved Approval Flow in Odoo 17 Studio App
 
Book Review of Run For Your Life Powerpoint
Book Review of Run For Your Life PowerpointBook Review of Run For Your Life Powerpoint
Book Review of Run For Your Life Powerpoint
 
ANTI PARKISON DRUGS.pptx
ANTI         PARKISON          DRUGS.pptxANTI         PARKISON          DRUGS.pptx
ANTI PARKISON DRUGS.pptx
 
SPLICE Working Group: Reusable Code Examples
SPLICE Working Group:Reusable Code ExamplesSPLICE Working Group:Reusable Code Examples
SPLICE Working Group: Reusable Code Examples
 
Supporting Newcomer Multilingual Learners
Supporting Newcomer  Multilingual LearnersSupporting Newcomer  Multilingual Learners
Supporting Newcomer Multilingual Learners
 
How to Send Pro Forma Invoice to Your Customers in Odoo 17
How to Send Pro Forma Invoice to Your Customers in Odoo 17How to Send Pro Forma Invoice to Your Customers in Odoo 17
How to Send Pro Forma Invoice to Your Customers in Odoo 17
 
diagnosting testing bsc 2nd sem.pptx....
diagnosting testing bsc 2nd sem.pptx....diagnosting testing bsc 2nd sem.pptx....
diagnosting testing bsc 2nd sem.pptx....
 
How to Manage Website in Odoo 17 Studio App.pptx
How to Manage Website in Odoo 17 Studio App.pptxHow to Manage Website in Odoo 17 Studio App.pptx
How to Manage Website in Odoo 17 Studio App.pptx
 
DEMONSTRATION LESSON IN ENGLISH 4 MATATAG CURRICULUM
DEMONSTRATION LESSON IN ENGLISH 4 MATATAG CURRICULUMDEMONSTRATION LESSON IN ENGLISH 4 MATATAG CURRICULUM
DEMONSTRATION LESSON IN ENGLISH 4 MATATAG CURRICULUM
 

Vladislav Zavialov – Introduction to GUI programming in Haskell

  • 1. © Serokell OÜ, 2020 Introduction to GUI programming in Haskell GTK+ Nix Haskell
  • 2. © Serokell OÜ, 2020 Case Study: Temperature Converter 2
  • 3. © Serokell OÜ, 2020 The Setup Part I
  • 4. © Serokell OÜ, 20204 Installing Dependencies $ brew install gtk+3
  • 8. © Serokell OÜ, 20208 $ pkg-config --modversion gtk+-3.0 3.24.23 Too new, and brew does not let us downgrade.
  • 9. © Serokell OÜ, 20209 Nix to the Rescue 1. Declarative configuration 2. Reliable and reproducible 3. System and Haskell libraries
  • 10. © Serokell OÜ, 202010 Nix configuration: nixpkgs let nixpkgsPin = { url = https://github.com/nixos/nixpkgs/archive/....tar.gz; sha256 = "..."; }; pkgs = import (builtins.fetchTarball nixpkgsPin) {}; in ... default.nix
  • 11. © Serokell OÜ, 202011 Nix configuration: mkDerivation pkgs.stdenv.mkDerivation rec { name = "gui-haskell-app"; src = ./.; buildInputs = [ ... ]; } default.nix
  • 12. © Serokell OÜ, 202012 Nix configuration: build-inputs buildInputs = [ (pkgs.haskell.packages.ghc865.ghcWithPackages (p: [ p.gi-gtk ])) pkgs.gtk3 pkgs.pkgconfig pkgs.gobject-introspection ]; default.nix
  • 13. © Serokell OÜ, 202013 Nix configuration: miscellaneous libPath = pkgs.lib.makeLibraryPath buildInputs; shellHook = '' export LD_LIBRARY_PATH=${libPath}:$LD_LIBRARY_PATH export LANG=en_US.UTF-8 ''; LOCALE_ARCHIVE = if pkgs.stdenv.isLinux then "${pkgs.glibcLocales}/lib/locale/locale-archive" else ""; default.nix
  • 14. © Serokell OÜ, 202014 30 lines of config. Enter nix-shell and you're set.
  • 15. © Serokell OÜ, 2020 ghc ./Main.hs -outputdir _build -o gui-haskell-app -threaded -Wall -O2 15 ghcid ./Main.hs Full Build Development
  • 16. © Serokell OÜ, 2020 The User Interface Part II
  • 17. © Serokell OÜ, 202017 import qualified GI.Gtk as Gtk main = do window <- Gtk.windowNew Gtk.WindowTypeToplevel Gtk.widgetShow window $ ./gui-haskell-app (process:23372): Gtk-CRITICAL **: 14:37:37.314: _gtk_style_provider_private_get_settings: assertion 'GTK_IS_STYLE_PROVIDER_PRIVATE (provider)' failed Segmentation fault: 11
  • 18. © Serokell OÜ, 202018 main = do Just app <- Gtk.applicationNew (Just appId) [] _ <- Gio.onApplicationActivate app (appActivate app) _ <- Gio.applicationRun app Nothing return () appId = Text.pack "io.serokell.gui-haskell-app" appActivate :: Gtk.Application -> IO () appActivate app = do window <- Gtk.applicationWindowNew app Gtk.widgetShowAll window
  • 19. © Serokell OÜ, 202019 main = do Just app <- Gtk.applicationNew (Just appId) [] _ <- Gio.onApplicationActivate app (appActivate app) _ <- Gio.applicationRun app Nothing return () appId = Text.pack "io.serokell.gui-haskell-app" appActivate :: Gtk.Application -> IO () appActivate app = do window <- Gtk.applicationWindowNew app Gtk.widgetShowAll window
  • 20. © Serokell OÜ, 202020 main = do Just app <- Gtk.applicationNew (Just appId) [] _ <- Gio.onApplicationActivate app (appActivate app) _ <- Gio.applicationRun app Nothing return () appId = Text.pack "io.serokell.gui-haskell-app" appActivate :: Gtk.Application -> IO () appActivate app = do window <- Gtk.applicationWindowNew app Gtk.widgetShow window
  • 21. © Serokell OÜ, 2020 Checkpoint: Window Created 21
  • 22. © Serokell OÜ, 202022 appActivate app = do window <- Gtk.applicationWindowNew app Gtk.setWindowTitle window (Text.pack "GUI Haskell App") Gtk.widgetShow window
  • 23. © Serokell OÜ, 202023 appActivate app = do window <- Gtk.applicationWindowNew app Gtk.setWindowTitle window (Text.pack "GUI Haskell App") entry <- Gtk.entryNew Gtk.containerAdd window entry Gtk.widgetShow entry Gtk.widgetShow window
  • 24. © Serokell OÜ, 202024 appActivate app = do window <- Gtk.applicationWindowNew app Gtk.setWindowTitle window (Text.pack "GUI Haskell App") _entryC <- addEntry window _entryF <- addEntry window Gtk.widgetShow window addEntry :: Gtk.IsContainer a => a -> IO Gtk.Entry addEntry container = do entry <- Gtk.entryNew Gtk.containerAdd container entry Gtk.widgetShow entry return entry
  • 25. © Serokell OÜ, 202025 $ ./gui-haskell-app (<unknown>:27651): Gtk-WARNING **: 15:36:57.816: Attempting to add a widget with type GtkEntry to a GtkApplicationWindow, but as a GtkBin subclass a GtkApplicationWindow can only contain one widget at a time; it already contains a widget of type GtkEntry
  • 26. © Serokell OÜ, 202026 appActivate app = do window <- Gtk.applicationWindowNew app Gtk.setWindowTitle window (Text.pack "GUI Haskell App") vbox <- Gtk.boxNew Gtk.OrientationVertical 10 Gtk.setWidgetMargin vbox 10 Gtk.containerAdd window vbox Gtk.widgetShow vbox _entryC <- addEntry vbox _entryF <- addEntry vbox Gtk.widgetShow window
  • 28. © Serokell OÜ, 202028 appActivate app = do window <- Gtk.applicationWindowNew app Gtk.setWindowTitle window (Text.pack "GUI Haskell App") Gtk.setWindowResizable window False Gtk.setWindowDefaultWidth window 300 ...
  • 29. © Serokell OÜ, 202029 appActivate app = do ... _entryC <- addEntry (Text.pack "°C") vbox _entryF <- addEntry (Text.pack "°F") vbox Gtk.widgetShow window addEntry :: Gtk.IsContainer a => Text -> a -> IO Gtk.Entry addEntry labelStr container = ...
  • 30. © Serokell OÜ, 202030 addEntry labelStr container = do hbox <- Gtk.boxNew Gtk.OrientationHorizontal 5 entry <- Gtk.entryNew label <- Gtk.labelNew (Just labelStr) Gtk.containerAdd hbox entry Gtk.containerAdd hbox label Gtk.containerAdd container hbox Gtk.widgetShow entry Gtk.widgetShow label Gtk.widgetShow hbox return entry
  • 31. © Serokell OÜ, 202031 addEntry labelStr container = do hbox <- Gtk.boxNew Gtk.OrientationHorizontal 5 entry <- Gtk.entryNew label <- Gtk.labelNew (Just labelStr) Gtk.containerAdd hbox entry Gtk.containerAdd hbox label Gtk.containerAdd container hbox Gtk.widgetShow entry Gtk.widgetShow label Gtk.widgetShow hbox return entry
  • 32. © Serokell OÜ, 202032 Gtk.setWidgetExpand entry True Gtk.setEntryXalign entry 1No alignment
  • 33. © Serokell OÜ, 202033 appActivate app = do window <- Gtk.applicationWindowNew app ... vbox <- Gtk.boxNew Gtk.OrientationVertical 10 ... _entryC <- addEntry (Text.pack "°C") vbox _entryF <- addEntry (Text.pack "°F") vbox button <- Gtk.buttonNew Gtk.setButtonLabel button (Text.pack "Get Weather") Gtk.setWidgetHalign button Gtk.AlignCenter Gtk.containerAdd vbox button Gtk.widgetShow button Gtk.widgetShow window
  • 34. © Serokell OÜ, 202034 Checkpoint: Widget Layout
  • 35. © Serokell OÜ, 2020 Application Logic and Behavior Part III
  • 36. © Serokell OÜ, 2020 entryC <- addEntry (Text.pack "°C") vbox entryF <- addEntry (Text.pack "°F") vbox _ <- Gtk.onEditableChanged entryC $ do s <- Gtk.entryGetText entryC Gtk.entrySetText entryF (Text.reverse s) 36
  • 37. © Serokell OÜ, 202037 c_to_f, f_to_c :: Double -> Double c_to_f = (+32) . (*1.8) f_to_c = (/1.8) . subtract 32 parseDouble :: Text -> Maybe Double parseDouble = readMaybe . Text.unpack renderDouble :: Double -> Text renderDouble = Text.pack . show @Centi . realToFrac getWeather :: IO Double getWeather = do threadDelay (3 * 1000000) randomRIO (-30, 30)
  • 38. © Serokell OÜ, 202038 do s <- Gtk.entryGetText entryC case parseDouble s of Nothing -> return () Just v -> let s' = renderDouble (c_to_f v) in Gtk.entrySetText entryF s'
  • 39. © Serokell OÜ, 202039 setEntryRelation :: Gtk.Entry -> (Double -> Double) -> Gtk.Entry -> IO () setEntryRelation entrySource conv entryTarget = do _ <- Gtk.onEditableChanged entrySource $ do s <- Gtk.entryGetText entrySource case parseDouble s of Nothing -> return () Just v -> let s' = renderDouble (conv v) in Gtk.entrySetText entryTarget s' return () entryC <- addEntry (Text.pack "°C") vbox entryF <- addEntry (Text.pack "°F") vbox setEntryRelation entryC c_to_f entryF setEntryRelation entryF f_to_c entryC
  • 40. © Serokell OÜ, 202040 A Feedback Loop?
  • 41. © Serokell OÜ, 202041 do target_focused <- Gtk.widgetHasFocus entryTarget unless target_focused $ do s <- Gtk.entryGetText entrySource case parseDouble s of Nothing -> return () Just v -> let s' = renderDouble (conv v) in Gtk.entrySetText entryTarget s'
  • 42. © Serokell OÜ, 202042 Checkpoint: Bidirectional Conversion
  • 43. © Serokell OÜ, 202043 _ <- Gtk.onButtonClicked button $ do c <- getWeather Gtk.entrySetText entryC (renderDouble c)
  • 44. © Serokell OÜ, 202044 _ <- Gtk.onButtonClicked button $ do _ <- forkIO $ do c <- getWeather Gtk.entrySetText entryC (renderDouble c) return ()
  • 45. © Serokell OÜ, 202045 Gtk.onButtonClicked button $ do _ <- forkIO $ do c <- getWeather _ <- GLib.idleAdd GLib.PRIORITY_HIGH_IDLE $ do _ <- Gtk.entrySetText entryC (renderDouble c) return False return () return ()
  • 46. © Serokell OÜ, 202046 Gtk.onButtonClicked button $ do Gtk.widgetSetSensitive button False _ <- forkIO $ do c <- getWeather _ <- GLib.idleAdd GLib.PRIORITY_HIGH_IDLE $ do _ <- Gtk.entrySetText entryC (renderDouble c) Gtk.widgetSetSensitive button True return False return () return ()
  • 48. © Serokell OÜ, 2020 Documentation and Resources Part IV
  • 49. © Serokell OÜ, 202049 https://github.com/serokell/gui-haskell-app dd96394 Unclickable button fcb6778 Safe multi-threading 703f605 Bad forkIO def6ebc Button click e2f212b Break the feedback loop 9264545 Mutual updates 5771689 Convert C to F ae3d8b7 Application logic functions b3a3f5b Connect to the editable-changed signal e60afad Add a button e182920 Expand/align entries 84ce7ee Entry labels 2c88179 Disable resizing cc8f4d8 Vertical box 34c037c Create second entry (not visible) f6f5dbe Add an entry (input field) f8206a8 Set window title 3657b3a Checkpoint: window created 4ab77ef Initial commit
  • 50. © Serokell OÜ, 202050 https://developer.gnome.org/gtk3/stable/
  • 51. © Serokell OÜ, 202051 https://github.com/haskell-gi/haskell- gi/tree/master/examples/
  • 52. © Serokell OÜ, 202052 https://hackage.haskell.org/package/gi-gtk https://hackage.haskell.org/package/gi-gdk https://hackage.haskell.org/package/gi-glib https://hackage.haskell.org/package/haskell-gi-base
  • 53. © Serokell OÜ, 2020 Questions? GTK+ Nix Haskell

Editor's Notes

  1. Being the naive person that I am, I tried to do it with 'brew'. What could possibly go wrong? Okay, gtk+ installed.
  2. Now we need the Haskell bindings to it. gi-gtk is great, mostly auto-generated and well-maintained.
  3. The problem is due to version incompatibility. Either an older 'gtk+' or a newer 'haskell-gi' would work.
  4. The system library (GTK+) and the haskell-gi library must be compatible. Better use a package manager that lets you specify the exact version.
  5. You might think that we could start by creating a window, but GTK+ will swiftly remind you that it's a C library, and greet you with a segfault.
  6. To make it work, we start by creating a Gtk.Application. It handles GTK initialization, application uniqueness, session management, desktop shell integration, and so on. In our small application, we will use it just for initialization. As input, it takes a string that identifies your application.
  7. Next we attach an activation handler. This code will be run when GTK+ initialization is complete, so, this time, no segfaults.
  8. Then we create a window and, quite importantly, show it – GTK+ widgets are created hidden by default.
  9. We can set the window title via the Gtk.setWindowTitle method.
  10. Now let's create another widget – an input field, called "entry" in GTK+. We create it with Gtk.entryNew, add it to the window with Gtk.containerAdd, and then make it visible with Gtk.widgetShow
  11. Since we want to create a converter, let's have to input fields rather than one. Factor out its creation into a helper function 'addEntry', and call it twice.
  12. Unfortunately, this doesn't quite cut it: we still see only one input field, and also a warning in stderr.
  13. To position the entries within the window, we create a box with vertical orientation. We also set the space between its elements and its padding to 10, so it doesn't look cluttered. And then we add the entries to the vertical box, instead of adding them to the window directly.
  14. Here's a problem: we don't want this window to be resizable. Its layout supports no meaningful way of filling additional vertical space. Easy to fix.
  15. Set the width, disable resizing.
  16. Now let's add labels to the input fields. Firstly, we will need to pass the label text to 'addEntry'
  17. Then we will create a box to lay out the entry and the label horizontally, and the label widget.
  18. Use Gtk.containerAdd now: the entry and the label go into the hbox, and the hbox goes into the outer container (which is vbox in our case).
  19. That gets us the result shown on the left. With two more lines, we can configure the alignment/expansion and get the result on the right.
  20. Recall that we have a window and a vertical box in it. With a few more lines, we can add a button there.
  21. Here we have it: two entries, two labels, and a button, laid out using horizontal and vertical boxes.
  22. When the user types into the first field, we want to react to that and set the value of the second field. So we connect to the the "editable-changed" signal of the first field. In the handler, we take the text of the field, and (just for example) reverse it and put into the other field.
  23. To implement the actual application logic instead of using Text.reverse, let's discuss a few helper functions. – c_to_f and f_to_c do temperature conversions – parseDouble and renderDouble handle conversions between floating-point numbers and text – getWeather could make a network request to some weather API, and that could take a long time to complete; but since this is a demo, it just waits 3 seconds and returns a random temperature.
  24. Now let's modify our event handler to use the conversion functions. Parsing the input as a number gives us a Maybe. In the Nothing case, we simply don't update the other field. In the Just case, we do the conversion.
  25. Now, to make the conversion in the opposite direction also possible, (when the user modifies the second field), let's factor out this logic into a helper and call it for both fields.
  26. However, this results in a strange behavior. The user enters a digit, and the system appends ".00" to it. 9 degrees Fahrenheit turns into 8.42 The reason for this is a feedback loop. User input gets converted in one direction, then it gets converted back, and so on, until it reaches a fixed point.
  27. To break the loop, let's not modify an entry if the user is typing into it. We can check it using Gtk.widgetHasFocus
  28. To handle the button click, you could try calling getWeather right there, in the handler. But this will make your app unresponsive during this blocking computation. Better do it in another thread.
  29. If you think you can just insert forkIO to do offload the work to another thread, GTK+ will swiftly remind you that it's a C library, and crash.
  30. Let's also make the button unclickable while it's getting the weather for us, so that the user doesn't click it multiple times in a row.
  31. And this basically gives us an app with the desired user experience. The work is offloaded to another thread, but the UI remains responsive. There's one minor bug though, finding it is left as an exercise for the reader (or should I say 'viewer').
  32. There's a repository available with the application from this talk. The commit history roughly follows the slides.
  33. There's a comprehensive references for the C library. haskell-gi is mostly auto-generated, so you can expect to find many of the same functions.
  34. You can find buildable examples of varying complexity in the haskell-gi repository
  35. And of course there's Haddock documentation for the bindings, where the index is of particular interest.