{ "cells": [ { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "GUI Programming in Python \n", "==============\n", "DesertPy 4/22/2015 \n", "---------------------\n", "@ftlphys \n", "Michael Gilbert" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "### GUI programming is a mix of science and art..." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "###A myriad of options...\n", "* TCL/TK\n", " - Comes with your python distribution\n", " - Good for basic diaglogues and simple user interaction\n", "* WxPython\n", " - Built on WxWidgets written in C++\n", " - Feature rich and extensible\n", " - Full 2.7 support, 3+ support in the works\n" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "* QT\n", " - PyQT: Feature rich, modern UI, QT5 support\n", " - PySide: Feature rich, only QT4 support\n", "* Kivy\n", " - Built on OpenGL ES 2 in Cython\n", " - Cross Platform to mobile and desktop\n", "* ..." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "###The Basics in Python\n", "* Most GUI frameworks use OOP\n", "* Class structures define objects like Windows, frames, dialogues, buttons, etc.\n", "* These are all defined to run within a main GUI loop or event loop" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "###Some tips for getting started...\n", "* Documenation as always is your friend\n", "* Stackoverflow and [WxPython Phoenix Doc site](http://wxpython.org/Phoenix/docs/html/index.html)\n", "* Get a Good IDE! (Eclipse with Pydev, Sublime, Komodo, PyCharm, etc.)\n", "* Make your life easier by starting from pre-existing examples or using things like [WxGlade](http://wxglade.sourceforge.net/)\n", "* Try a mockup utility like the opensource Pencil or a real pencil. :-)\n", "* Keep in mind that your code base will likely increase 2-10x in size" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "###WxPython \n", "* We start with a GUI main loop\n", "* We usually begin by building a class that inherits from \"Frame\"\n", " - Upon inheriting, we can set certain attributes such as size, title, etc.\n", "* WxPython is designed to have attributes either set upon inheritance or edited later, but the design flow is strictly linear\n", " " ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "* We will divide things into 4 parts\n", " - Define widgets (buttons, stat text, input text, checkbox, listctrl, etc.)\n", " - Define properties & bindings\n", " - Define layout\n", " - Define methods" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "###Boxes Boxes Everywhere...\n", "*Note*: Everything in WxPython is contained within some sort of box sizer.\n", "The benefit is that your layout and sizing are done relative to your content" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "###Hello World! in WxPython" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": false, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "import wx\n", "\n", "app = wx.App(False) # Create a new app, don't redirect stdout/stderr to a window.\n", "frame = wx.Frame(None, wx.ID_ANY, \"Hello World\") # A Frame is a top-level window.\n", "frame.Show(True) # Show the frame.\n", "app.MainLoop()" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Let's Add a few things for the User to interact with..." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true, "slideshow": { "slide_type": "subslide" } }, "outputs": [], "source": [ "import wx\n", "import datetime\n", "import math\n", "\n", "class HelloWorld(wx.Frame):\n", " def __init__(self, parent, id=-1, title=\"\", pos=wx.DefaultPosition,\n", " size=wx.DefaultSize, style=wx.DEFAULT_FRAME_STYLE |\n", " wx.SUNKEN_BORDER |\n", " wx.CLIP_CHILDREN):\n", " wx.Frame.__init__(self, parent, id, title, pos, size, style)\n", " \n", " diagnal=math.sqrt(3200**2+1800**2)\n", " display_res=wx.GetDisplaySize()\n", " self.multiplier=math.sqrt(display_res[0]**2+display_res[1]**2)/diagnal\n", " \n", " self.master_panel = wx.Panel(self, -1)\n", " self.hello_button = wx.Button(self.master_panel, -1 ,\"Hello World\")\n", " self.slider_label = wx.StaticText(self.master_panel, -1, \n", " \"Select frequency of hellos\")\n", " self.slider = wx.Slider(self.master_panel, -1, 25, 1, 100, \n", " size = (int(self.multiplier*250), -1), \n", " style = wx.SL_HORIZONTAL | wx.SL_AUTOTICKS | \n", " wx.SL_LABELS \n", " )\n", " self.hellos_to_send=25\n", " \n", " self.__set_properties()\n", " self.__do_layout()\n", " \n", " def __set_properties(self):\n", " self.menu_bar = wx.MenuBar()\n", " self.help = wx.Menu()\n", " self.about_me = wx.MenuItem(self.help, wx.NewId(), \"About...\")\n", " self.help.AppendItem(self.about_me)\n", " self.menu_bar.Append(self.help, \"Help\")\n", " self.SetMenuBar(self.menu_bar)\n", " \n", " self.statusbar = self.CreateStatusBar()\n", " self.statusbar.SetStatusText(datetime.datetime.now().\n", " strftime(\"%d %b %Y %H:%M:%S\"))\n", " \n", " self.Bind(wx.EVT_MENU,self.about_program,self.about_me)\n", " self.Bind(wx.EVT_BUTTON, self.output_hellos, self.hello_button)\n", " self.Bind(wx.EVT_SCROLL_CHANGED, self.hello_count, self.slider)\n", " \n", " def __do_layout(self):\n", " self.panel_sizer = wx.BoxSizer(wx.VERTICAL)\n", " self.main_sizer = wx.BoxSizer(wx.VERTICAL)\n", " \n", " self.main_sizer.Add(self.hello_button, 0, \n", " wx.ALL | wx.ALIGN_CENTER_HORIZONTAL, \n", " int(self.multiplier*10))\n", " self.main_sizer.Add(self.slider_label, 0, \n", " wx.BOTTOM | wx.LEFT | wx.RIGHT | \n", " wx.ALIGN_CENTER_HORIZONTAL, int(self.multiplier*10))\n", " self.main_sizer.Add(self.slider, 0, wx.BOTTOM | \n", " wx.ALIGN_CENTER_HORIZONTAL, int(self.multiplier*10))\n", " \n", " self.master_panel.SetSizer(self.main_sizer)\n", " self.main_sizer.Fit(self.master_panel)\n", " self.panel_sizer.Add(self.master_panel, 1, wx.EXPAND)\n", " self.SetSizer(self.panel_sizer)\n", " self.panel_sizer.Fit(self)\n", " self.Layout()\n", " \n", " def about_program(self, evt):\n", " dlg = wx.MessageDialog(self, 'This program says hello to the world.',\n", " 'Greetings!',\n", " wx.OK | wx.ICON_INFORMATION\n", " )\n", " dlg.ShowModal()\n", " dlg.Destroy()\n", " \n", " def hello_count(self, evt):\n", " self.hellos_to_send=int(evt.EventObject.GetValue())\n", " \n", " def output_hellos(self, evt):\n", " for i in range(self.hellos_to_send):\n", " print(i+1, 'hello!')\n", " \n", "if __name__ == '__main__':\n", " app = wx.App()\n", " HelloWorldApp = HelloWorld(None, title = 'Hello World')\n", " HelloWorldApp.Show()\n", " app.MainLoop()\n", " " ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Kivy\n", "* We have a main GUI loop just like WxPython\n", "* We divide our code between python and KV lang\n", " - This allows for separation of layout and logic\n", " - MVC architecture (Model - View - Control)\n", "* KV lang provides a pythonic domain specific language for GUI design\n", "* We can deploy applications to Android and iOS\n", " - This is made possible only under Linux (at the moment) using Buildozer\n", " - Pyler: Generic Python API for all platforms (wraps Pyjnius & Pyobjus)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "###Hello World! in Kivy" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "collapsed": false, "slideshow": { "slide_type": "subslide" } }, "outputs": [], "source": [ "from kivy.app import App\n", "from kivy.uix.button import Button\n", "\n", "class TestApp(App):\n", " def build(self):\n", " return Button(text='Hello World')\n", "\n", "TestApp().run()" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "###Hello World! in Kivy with kv lang" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true, "slideshow": { "slide_type": "subslide" } }, "outputs": [], "source": [ "from kivy.app import App\n", "\n", "class TestApp(App):\n", " pass\n", "\n", "if __name__ == '__main__':\n", " TestApp().run()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "Button:\n", " text: 'Hello from test.kv'" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "###Kivy Resources\n", "* [Creating Apps in Kivy by Dusty Phillips](http://shop.oreilly.com/product/0636920032595.do)\n", " - [Code that accompanies the book including the Weather App](https://github.com/oreillymedia/creating_apps_in_kivy)\n", "* [Kivy main site](http://kivy.org/#home)\n", "* If on Linux, examples, including the gesture_board, are located /usr/share/kivy-examples\n" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "###General Tips & Tricks\n", "* Use MVC whenever possible\n", "* Avoid pixel declarations where possible and/or account for resolution on development platform\n", "* Allow the GUI system to do all layouts in a relative fashion and fit to your content\n" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "###Questions?" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "#PyCon 2015 Montreal!\n", "" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "###Some Interesting Take aways...\n", "* [coLaboratory](http://colaboratory.jupyter.org/welcome/)\n", "* Type hints in Python 3.5\n", "* Microsoft kicking it up a notch in 3.5!\n", "* [Keynote Catherine Bracy](https://www.youtube.com/watch?v=LOZk5ttyC9Y&feature=youtu.be)\n", "* [Keynote Jacob Kaplan-Moss](https://www.youtube.com/watch?v=hIJdFxYlEKE&feature=youtu.be)\n" ] } ], "metadata": { "celltoolbar": "Slideshow", "kernelspec": { "display_name": "Python 2", "language": "python", "name": "python2" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 2 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython2", "version": "2.7.6" } }, "nbformat": 4, "nbformat_minor": 0 }