ActiveState Code

Recipe 442481: Creating Browser-Based Desktop Apps with CherryPy 2.1


You want to create a simple browser-based desktop application with minimal fuss.

Python
 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
import cherrypy

class Root(object):
    @cherrypy.expose
    def index(self):
        return "Hello, world! <br /><a href='stop'>Stop</a>"
    # index.exposed = True # for python < 2.4

    @cherrypy.expose
    def stop(self):
        raise SystemExit
    # stop.exposed = True

if __name__ == '__main__':

    import webbrowser

    cherrypy.root = Root()

    cherrypy.config.update({
            'server.environment':'production',
            'server.socketHost':'127.0.0.1',
            })

    cherrypy.server.start_with_callback(
            webbrowser.open,
            ('http://localhost:8080',),
            )

Discussion

The browser is an attractive UI target for an application for many reasons. This recipe shows you how to use CherryPy and the standard library module 'webbrowser' to create a simple 'Hello, World' browser-based application.

First, we create a class that exposes index() and stop() methods. Second, we bind an instance of our Root class to cherrypy.root and configure our server to run in production mode and only accept connections on the loopback interface. Third, and finally, we start cherrypy with start_with_callback() and specify 'webbrowser.open' as the callback function. This launches the application server and then the browser as the user interface.

Though not fleshed out here, persistence can be added with ease using the standard module shelve, a database, CSV files, etc. Even though CherryPy uses a threaded server by default, synchronization of the datasource shouldn't be much of a concern as we are dealing with a single-user desktop application. To be on the safe side, you can set 'server.threadPool':1 to have a single request handler thread.

This recipe should be cross platform, as both CherryPy and the webbrowser module run in Windows and Unix environments. For Windows platforms, it should also be possible to bundle the application as an 'exe' file using Py2exe.

For more information on CherryPy, see http://www.cherrypy.org

Comments

  1. 1. At 1:57 p.m. on 21 nov 2007, Terry Brown said:

    Update for CherryPy 3.0.2. Here's a version that works for me with CherryPy 3.0.2, the above doesn't work in 3.0.2, but was very helpful.

    import cherrypy
    
    class Root(object):
        @cherrypy.expose
        def index(self):
            return "Hello, world!
    Stop"
    
        @cherrypy.expose
        def stop(self):
            raise SystemExit
    
    if __name__ == '__main__':
    
        import webbrowser
    
        cherrypy.config.update({'server.socket_port':8888})
    
        cherrypy.tree.mount(Root(), '/')
    
        cherrypy.server.quickstart()
    
        cherrypy.engine.start_with_callback(
                webbrowser.open,
                ('http://131.212.123.122:8888/',),
                )
        cherrypy.engine.block()
    
  2. 2. At 2 p.m. on 21 nov 2007, Terry Brown said:

    whoops, mangled markup, here's the code again.

    import cherrypy
    
    class Root(object):
        @cherrypy.expose
        def index(self):
            return "Hello, world! &lt;br />&lt;a href='stop'>Stop&lt;/a>"
    
        @cherrypy.expose
        def stop(self):
            raise SystemExit
    
    if __name__ == '__main__':
    
        import webbrowser
    
        cherrypy.config.update({'server.socket_port':8888})
    
        cherrypy.tree.mount(Root(), '/')
    
        cherrypy.server.quickstart()
    
        cherrypy.engine.start_with_callback(
                webbrowser.open,
                ('http://131.212.123.122:8888/',),
                )
        cherrypy.engine.block()
    
  3. 3. At 1:45 p.m. on 23 dec 2007, Terry Brown said:

    A more graceful exit. Here's another idea:

    @cherrypy.expose
    def stop(self):
        cherrypy.response.headers['Content-Type'] = 'text/plain'
        def content():
            yield 'App. has closed'
            raise SystemExit
        return content()
    stop._cp_config = {'response.stream': True}
    

    This defines stop as a streaming method as described here: http://www.cherrypy.org/wiki/ReturnVsYield This gives you a chance to tell the user the application's ended, rather than just a "can't access page" error as provided by the original.

Sign in to comment