Build An Elasticsearch Query GUI Application Using Kivy and Python (Part 1)

Introduction

If you’re looking to create an Elasticsearch GUI app, using Kivy is a natural choice. The Kivy library is a cross-platform toolkit for developing applications with natural user interfaces using the Python programming language. When you use Kivy, you can deploy applications for all platforms including iOS, Android, Windows, Linux, and Windows.

In this first installment of our two-article series, we’ll show you how to install Kivy and the Elasticsearch client for Python, and we’ll explain how to import the package libraries inside a Python script. Our second article will include all the instructions and example code needed to create the Elasticsearch GUI app with Kivy and Python. part 2 of this Elasticsearch-Kivy series will explain how to create the class structure for the GUI application.

Kivy and Elasticsearch prerequisites and installation

Before we proceed with this tutorial, it’s important to review a few prerequisites that will be necessary to build an Elasticsearch GUI app with Kivy and Python:

  • You’ll need to have Python 3 installed. It’s recommended that you use Python 3.x instead of Python 2.7 when testing out the example code in this article as the older version of Python is deprecated and will be losing support.

  • The Elasticsearch cluster needs to be running on the machine that will be running the GUI app. This will ensure that Kivy can display the document information returned by Elasticsearch. You can use the following cURL request to verify that Elasticsearch is running:

curl -XGET localhost:9200
  • The Kivy library has several dependencies such as PIL and Pygame, so you’ll need to have the PIP3 package manager installed on your system.

  • You’ll need to install the Pygame GUI library for Python using pip3:

pip3 install pygame
  • You’ll also need to install the Kivy library using pip3:
pip3 install kivy
  • Finally, you’ll need to make sure that the Elasticsearch low-level client for Python is installed:
pip3 install elasticsearch

NOTE: You may need to use elevated sudo permissions while installing with PIP on a Linux system.

Import the Kivy library to check if PIP3 installed it properly

Once you’re done getting the prerequisites in place, it’s time to start working with Python. Open a Python interpreter or IDLE environment and try to import kivy to make sure that the library was installed properly:

import kivy

Two ways to create an application with Kivy and Python

Kivy allows you to develop applications using Python or using a Kv script (with a .kv file extension) written in the Kivy language. For the sake of simplicity, this tutorial will focus on an application written in “pure” Python.

There are also three ways to load a Kivy string in Python. You can use the Build.load() or Builder.load_string() methods, or you can have Kivy look for a Kv file automatically. Let’s look at some of these possibilities:

Use the Builder.load() method to load a Kivy string

In this example, we simply use Kivy’s Builder() method found in the kivy.lang library:

from kivy.lang import Builder

Builder.load_string('''
<MainApp>:
    Label:
        text: 'ObjectRocket AppLabel'
    Button:
        text: 'CLICK ME'
'''
)

Use the Builder.load() method to load the Kv file

You can also pass a string containing the file name and the Kv file’s path to the Builder library’s load_file() method:

from kivy.lang import Builder
Builder.load_file('directory/path/to/kvfile.kv')

Have Kivy load the Kv file automatically using the app name

Kivy will get a Kv file automatically if the filename is the same as your app’s class name, but spelled out in lowercase letters and without App at the end. For example, if the name of your app is ElasticApp then the .kv file should be named elastic.kv, and it should be located in the same directory.

Screenshot of Python IDLE import the Builder() method in the kivy.lang library

Set up the Python script and the Kivy project directory

Next, let’s create a directory for the Elasticsearch Kivy app. You can use the mkdir command if you’re working on a UNIX-based terminal:

mkdir elastic_app

Change into the directory (cd elastic_app) and use the touch command to create a new Python script for the Kivy project:

touch main.py

NOTE: The typical naming convention for Python project directory and script names is all lowercase with hyphens (_) separating the words.

Import the Kivy and Elasticsearch libraries

Now that we’ve created our script, let’s edit it for the Kivy project and import the necessary Python packages so that we can have access to their method calls and libraries.

Import Python’s JSON library for the Elasticsearch API response

We’ll need to import Python’s native JSON library so that its dumps() method call can be used. The dumps() method is needed to convert the dict response returned by the Elasticsearch client into a human-readable JSON string that can be assigned to a Kivy widget’s text property:

# import the JSON library to prettify the API response
import json

Import the Elasticsearch low-level client library for Python

We’ll also need to import the Elasticsearch client package library:

# import the Elasticsearch client library
from elasticsearch import Elasticsearch

Import the Kivy app and UIX libraries

Our Elasticsearch application will require several different types of widgets, including labels, buttons, and text input UIX objects. In the following section of code, we import each respective library for Kivy:

# import the necessary UIX libraries for the Kivy app
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput

# import the FloatLayout library for UIX object location
from kivy.uix.floatlayout import FloatLayout

# import the Config library to set window size
from kivy.config import Config

Create an Elasticsearch client instance

We’ll use the Elasticsearch client to declare a client object instance of the library:

# create a client instance of Elasticsearch
client = Elasticsearch("http://localhost:9200")

Since the example app we’re building is designed for development and testing purposes, we use the localhost:9200 domain and port number.

Use Kivy’s Config library to set the app’s window size

Make sure to set your desired application window size by calling Kivy’s Config.set() method graphics configuration with

# set the app's window size with Config
Config.set('graphics', 'width', '1024')
Config.set('graphics', 'height', '1536')

Define a Python function to make Elasticsearch queries

The application that we’re building is designed to make an API query request to Elasticsearch when the user clicks on a GUI button. Let’s take a look at the global function that will get called when the button event is triggered:

# define a function for the Elasticsearch query API call
def make_query(filter, index_name):

    # make an API call to check if the index exists
    index_exists = client.indices.exists(index=index_name)

    # if it exists then make the API call
    if index_exists == True:
        print ("index_name:", index_name, "exists.")
        print ("FILTER:", filter, "\n")

NOTE: This function is designed to return an error response if the index name doesn’t exist.

Use a try-except block to catch Elasticsearch API exceptions

If the Elasticsearch index in question exists, the function call will make an API request to Elasticsearch. This API call will take place inside a try-except indentation block to catch any API or HTTP errors returned by the Elasticsearch cluster. Catching errors allows them to be displayed in the Kivy GUI application itself:

        # catch any exceptions and return them to Kivy app
        try:
            # pass filter query to the client's search() method
            response = client.search(index=index_name, body=filter)

            # print the query response for debugging purposes
            print ('response["hits"]:', len(response["hits"]))
            print ('response TYPE:', type(response))

        except Exception as err:
            print ("search() index ERROR", err)
            response = {"error": str(err)}

Keep in mind that in a more “polished” production stage of the app’s development, you may not want to display errors in a Kivy widget. However, this method of handling errors is ideal for the development and debugging stages.

Return an error message if the Elasticsearch index doesn’t exist

The following section of code handles cases where the Elasticsearch index does not exist. Simply use the else statement for the indices.exists() API response to return a response saying that the index does not exist:

    # error text response if index doesn't exist
    else:
        # build a string for the index-does-not-exist response
        resp_text = "Elasticsearch index name '" + str(index_name)
        resp_text += "' does not exist."
        response = {"response": resp_text}

    # return the dict response to Kivy app
    return response

Conclusion

Building an Elasticsearch GUI app with Kivy and Python is a complex task, so we’ve broken up our tutorial into two articles. This first article in our two-part series demonstrated how to install and import the Elasticsearch and Kivy package libraries for Python. It also explained how to create a function that will pass a filter query dict object to the search() method call. In the next installment in this series, we’ll show you how to create a Kivy application and how to change the UIX labels to reflect the JSON data returned by the API query.

Just the Code

Here’s the Python script we created in its entirety (Please check out part 2 of this Kivy series to see the finished code for the GUI application):

#!/usr/bin/env python3
#-*- coding: utf-8 -*-

# import the JSON library to prettify the API response
import json

# import the Elasticsearch client library
from elasticsearch import Elasticsearch

# import the necessary UIX libraries for the Kivy app
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput

# import the FloatLayout library for UIX widget's location
from kivy.uix.floatlayout import FloatLayout

# import the Config library to set window size
from kivy.config import Config

# create a client instance of Elasticsearch
client = Elasticsearch("http://localhost:9200")

# set the app's window size with Config
Config.set('graphics', 'width', '1024')
Config.set('graphics', 'height', '1536')

# define a function for the Elasticsearch query API call
def make_query(filter, index_name):

    # make an API call to check if the index exists
    index_exists = client.indices.exists(index=index_name)

    # if it exists then make the API call
    if index_exists == True:
        print ("index_name:", index_name, "exists.")
        print ("FILTER:", filter, "\n")

        # catch any exceptions and return them to Kivy app
        try:
            # pass filter query to the client's search() method
            response = client.search(index=index_name, body=filter)

            # print the query response for debugging purposes
            print ('response["hits"]:', len(response["hits"]))
            print ('response TYPE:', type(response))

        except Exception as err:
            print ("search() index ERROR", err)
            response = {"error": str(err)}

    # error text response if index doesn't exist
    else:
        # build a string for the index-does-not-exist response
        resp_text = "Elasticsearch index name '" + str(index_name)
        resp_text += "' does not exist."
        response = {"response": resp_text}

    # return the dict response to Kivy app
    return response

Pilot the ObjectRocket Platform Free!

Try Fully-Managed CockroachDB, Elasticsearch, MongoDB, PostgreSQL (Beta) or Redis.

Get Started

Keep in the know!

Subscribe to our emails and we’ll let you know what’s going on at ObjectRocket. We hate spam and make it easy to unsubscribe.