How to Build Desktop App Using Tkinter and Django Rest Framework Backend -Part 2

||
Posted 3 years, 9 months ago
||
Views 4147
||
11 min read
1 reaction

 

In this section, we are going to develop our Tkinter portion which is continous of Part 1

As we have developed our backend portion, its time to focus on front end developement(Dekstop App)

For this tutorial, we will use Tkinter module which is Python out of the box module.

We need to create the python file in order to handle the logic, as we have already enabled the virtualenv in Part 1.

We can use the same folder for design our front end.

Folder structure would be like this in my environment,

Now, its time to create the new file called as app.py. You can name it as any name.

Then , just like any other module, we need to import the module,

from tkinter import *
from tkinter import messagebox

In Tkinter, in order to work with widgets, we need to initiate the Tk instance and also want to set the title and size resolution,

app = Tk()

app.title('Hotel Management')
app.geometry('750x350')

Output looks like below, depends upon the OS it may varies,

Ah, we did our outline design. Congrats smileyyes

Now, we can start design our label & text box using below code,

from tkinter import *
from tkinter import messagebox
# from db import Database
import requests
import json


# Create window object
app = Tk()

# Room
room_text = StringVar()
room_label = Label(app, text='Room No',
                   font=('bold', 14), pady=20)
room_label.grid(row=0, column=0, sticky=W)
room_entry = Entry(app, textvariable=room_text)
room_entry.grid(row=0, column=1)
# Gues
guest_text = StringVar()
guest_label = Label(app, text='Guest', font=('bold', 14))
guest_label.grid(row=0, column=2, sticky=W)
guest_entry = Entry(app, textvariable=guest_text)
guest_entry.grid(row=0, column=3)
# Check in Date
check_in_text = StringVar()
check_in_label = Label(app, text='Check In Date', font=('bold', 14))
check_in_label.grid(row=1, column=0, sticky=W)
check_in_entry = Entry(app, textvariable=check_in_text)
check_in_entry.grid(row=1, column=1)
# Check Out Date
check_out_text = StringVar()
check_out_label = Label(app, text='Check Out Date', font=('bold', 14))
check_out_label.grid(row=1, column=2, sticky=W)
check_out_entry = Entry(app, textvariable=check_out_text)
check_out_entry.grid(row=1, column=3)

app.title('Hotel Management')
app.geometry('750x350')

# Populate data
# retrieve_list()

# Start program
app.mainloop()

Design the textbox & label in various approach. I would like to go with GRID approach as we can control our widths compartively very easy.

Output of the above code looks like below,

Woow !!!. It looks amazing right. Ok just think our design before jump into the next level. I want to display all the rooms which we have booked so far when admin/Hotel Manager login into our app. So we need to think about having below designs,

  • Username & Password Login Option
  • List all rooms which is available in backend.
  • Also we need to think about adding room, update & delete as well.

Now, lets focus on adding Username, Password & Login Button. Just like above entry, we need to create the label, Entry & one more new things call Button.

from tkinter import *
from tkinter import messagebox



def login():
    pass


# Create window object
app = Tk()

username_text = StringVar()
username_label = Label(app, text='Username', fg="red",
                       font=('bold', 10), padx=20)
username_label.grid(row=0, column=8, sticky=W)
username_entry = Entry(app, textvariable=username_text)
username_entry.grid(row=0, column=9)


password_text = StringVar()
password_label = Label(app, text='Password', fg="red",
                       font=('bold', 10), padx=20)
password_label.grid(row=1, column=8, sticky=W)
password_entry = Entry(app, show="*", textvariable=password_text)
password_entry.grid(row=1, column=9)

login_btn = Button(app, text='Login', fg="red", width=15, command=login)
login_btn.grid(row=2, column=9, pady=20)

# Room
room_text = StringVar()
room_label = Label(app, text='Room No',
                   font=('bold', 14), pady=20)
room_label.grid(row=0, column=0, sticky=W)
room_entry = Entry(app, textvariable=room_text)
room_entry.grid(row=0, column=1)
# Gues
guest_text = StringVar()
guest_label = Label(app, text='Guest', font=('bold', 14))
guest_label.grid(row=0, column=2, sticky=W)
guest_entry = Entry(app, textvariable=guest_text)
guest_entry.grid(row=0, column=3)
# Check in Date
check_in_text = StringVar()
check_in_label = Label(app, text='Check In Date', font=('bold', 14))
check_in_label.grid(row=1, column=0, sticky=W)
check_in_entry = Entry(app, textvariable=check_in_text)
check_in_entry.grid(row=1, column=1)
# Check Out Date
check_out_text = StringVar()
check_out_label = Label(app, text='Check Out Date', font=('bold', 14))
check_out_label.grid(row=1, column=2, sticky=W)
check_out_entry = Entry(app, textvariable=check_out_text)
check_out_entry.grid(row=1, column=3)

app.title('Hotel Management')
app.geometry('750x350')

# Populate data
# retrieve_list()

# Start program
app.mainloop()

We can write the same code using Class based approach but according to me, which is quite confusing for beginer so lets stick with same approach,

If you notice the above code, we have introduced button , the way button works based on any event, and we have give command=login basically, this will call when we click on "Login" button as shown in below,

Now , we need to think about where we can land the list data in our app. So we can use Listbox & scrollbar option to select the list items from the list box,

# Room Details (Listbox)

room_list = Listbox(app, height=8, width=50, border=1)
room_list.grid(row=2, column=0, columnspan=3, rowspan=6, pady=20, padx=20)
# Create scrollbar
scrollbar = Scrollbar(app)
scrollbar.grid(row=3, column=3)
# Set scroll to listbox
room_list.configure(yscrollcommand=scrollbar.set)
scrollbar.configure(command=room_list.yview)
# Bind select
room_list.bind('<<ListboxSelect>>', select_item)

Ah woow, we have developed almost 40 % , now its time to add related button to perform CRUD opeartion,


add_btn = Button(app, text='Add Room', width=15, command=add_item)
add_btn.grid(row=3, column=9)

remove_btn = Button(app, text='Delete Room', width=15, command=remove_item)
remove_btn.grid(row=4, column=9)

update_btn = Button(app, text='Update Room', width=15, command=update_item)
update_btn.grid(row=5, column=9)

clear_btn = Button(app, text='Clear', width=15, command=clear_text)
clear_btn.grid(row=6, column=9)

Before jump into CRUD operation , lets develop LOGIN functionality, as we have already exposed the token in Part 1.

We can use the END_POINT to define the logic, Lets see the below code for clear understanding,

def login():
    global token_out
    global username
    global password
    username = username_entry.get()
    password = password_entry.get()
    if username == '' or password == '':
        messagebox.showerror(
            'Required Fields', 'Username/password should not be empty')

    else:
        data = {
            "username": username,
            "password": password
        }
        try:
            response = requests.post(
                'http://localhost:8000/api-token-auth/', data=data)
            response_dict = json.loads(response.text)
            token_out = response_dict['token']

        except Exception as e:
            messagebox.showerror(
                'Login Failure', 'Username/Credentials Invalid. Please try again !!!')

        messagebox.showinfo('Login Successful')

So, basically, get the username & password from the user and pass to Django DRF login page for authentication and get the token from Django for CRUD operations,

Output looks like below

Now, its time to add our Add room, retrieve room list, delete  & room function,

import requests
import json





def retrieve_list():
    room_list.delete(0, END)
    if token_out:
        url = 'http://localhost:8000/api/'
        headers = {'Authorization': f'Token {token_out}'}
        r = requests.get(url, headers=headers)

        out_data = json.loads(r.text)
        data = []
        for out in out_data:
            res = out.values()
            data.append(list(res))

        for k in data:

            room_list.insert(END, k)


def add_item():
    try:
        if token_out:
            if room_text.get() == '' or guest_text.get() == '' or check_in_text.get() == '' or check_out_text.get() == '':
                messagebox.showerror(
                    'Required Fields', 'Please include all fields')

            else:
                print('hi')
                end_point = 'http://localhost:8000/api/create/'
                data = {
                    "room_num": room_text.get(),
                    "guest": guest_text.get(),
                    "check_in": check_in_text.get(),
                    "check_out": check_out_text.get()
                }

                headers = {
                    "Content-Type": "application/json",
                    "Authorization": f"Token {token_out}"
                }
                response = requests.post(
                    end_point, json=data, headers=headers)
                print(response.content)
    
                room_list.delete(0, END)
                room_list.insert(END, (room_text.get(), guest_text.get(),
                                       check_in_text.get(), check_out_text.get()))
                clear_text()
                retrieve_list()


    except NameError:
        messagebox.showerror(
            'Login Failure', 'You have to Login first !!!')
    except Exception as e:
        print(e)


def select_item(event):
    try:
        global selected_item
        index = room_list.curselection()[0]
        selected_item = room_list.get(index)

        room_entry.delete(0, END)
        room_entry.insert(END, selected_item[1])
        guest_entry.delete(0, END)
        guest_entry.insert(END, selected_item[2])
        check_in_entry.delete(0, END)
        check_in_entry.insert(END, selected_item[3])
        check_out_entry.delete(0, END)
        check_out_entry.insert(END, selected_item[4])
    except IndexError:
        pass


def remove_item():
    try:
        if token_out:
            if room_text.get() == '' or guest_text.get() == '' or check_in_text.get() == '' or check_out_text.get() == '':
                messagebox.showerror(
                    'Required Fields', 'Please include all fields')

            else:

                pk = selected_item[0]
                end_point = f'http://localhost:8000/api/{pk}/delete/'
                data = {
                    "room_num": room_text.get(),
                    "guest": guest_text.get(),
                    "check_in": check_in_text.get(),
                    "check_out": check_out_text.get()
                }

                headers = {
                    "Content-Type": "application/json",
                    "Authorization": f"Token {token_out}"
                }
                response = requests.delete(
                    end_point, json=data, headers=headers)
                print(response.content)

                retrieve_list()


    except NameError:
        messagebox.showerror(
            'Login Failure', 'You have to Login first !!!')
    except Exception as e:
        print(e)


def update_item():
    try:
        if token_out:
            if room_text.get() == '' or guest_text.get() == '' or check_in_text.get() == '' or check_out_text.get() == '':
                messagebox.showerror(
                    'Required Fields', 'Please include all fields')

            else:
                pk = selected_item[0]
                end_point = f'http://localhost:8000/api/{pk}/edit/'
                data = {
                    "room_num": room_text.get(),
                    "guest": guest_text.get(),
                    "check_in": check_in_text.get(),
                    "check_out": check_out_text.get()
                }

                headers = {
                    "Content-Type": "application/json",
                    "Authorization": f"Token {token_out}"
                }
                response = requests.put(
                    end_point, json=data, headers=headers)
                if response.status_code >= 200 and response.status_code <= 299:
                    print(response.content)
                else:
                    messagebox.showerror(
                        'Update Failure', f'Incorrect Format, {response.text}')


                retrieve_list()


    except NameError:
        messagebox.showerror(
            'Login Failure', 'You have to Login first !!!')
    except Exception as e:
        print(e)




def clear_text():
    room_entry.delete(0, END)
    guest_entry.delete(0, END)
    check_in_entry.delete(0, END)
    check_out_entry.delete(0, END)


def login():
    global token_out
    global username
    global password
    username = username_entry.get()
    password = password_entry.get()
    if username == '' or password == '':
        messagebox.showerror(
            'Required Fields', 'Username/password should not be empty')

    else:
        data = {
            "username": username,
            "password": password
        }
        try:
            response = requests.post(
                'http://localhost:8000/api-token-auth/', data=data)
            response_dict = json.loads(response.text)
            token_out = response_dict['token']

        except Exception as e:
            messagebox.showerror(
                'Login Failure', 'Username/Credentials Invalid. Please try again !!!')
        retrieve_list()

Lets take a look on output now,

smiley Looks amazing. Now, lets test it out with entire code,

from tkinter import *
from tkinter import messagebox

import requests
import json


def retrieve_list():
    room_list.delete(0, END)
    if token_out:
        url = 'http://localhost:8000/api/'
        headers = {'Authorization': f'Token {token_out}'}
        r = requests.get(url, headers=headers)

        out_data = json.loads(r.text)
        data = []
        for out in out_data:
            res = out.values()
            data.append(list(res))

        for k in data:

            room_list.insert(END, k)


def add_item():
    try:
        if token_out:
            if room_text.get() == '' or guest_text.get() == '' or check_in_text.get() == '' or check_out_text.get() == '':
                messagebox.showerror(
                    'Required Fields', 'Please include all fields')

            else:
                print('hi')
                end_point = 'http://localhost:8000/api/create/'
                data = {
                    "room_num": room_text.get(),
                    "guest": guest_text.get(),
                    "check_in": check_in_text.get(),
                    "check_out": check_out_text.get()
                }

                headers = {
                    "Content-Type": "application/json",
                    "Authorization": f"Token {token_out}"
                }
                response = requests.post(
                    end_point, json=data, headers=headers)
                print(response.content)

                room_list.delete(0, END)
                room_list.insert(END, (room_text.get(), guest_text.get(),
                                       check_in_text.get(), check_out_text.get()))
                clear_text()
                retrieve_list()

    except NameError:
        messagebox.showerror(
            'Login Failure', 'You have to Login first !!!')
    except Exception as e:
        print(e)


def select_item(event):
    try:
        global selected_item
        index = room_list.curselection()[0]
        selected_item = room_list.get(index)

        room_entry.delete(0, END)
        room_entry.insert(END, selected_item[1])
        guest_entry.delete(0, END)
        guest_entry.insert(END, selected_item[2])
        check_in_entry.delete(0, END)
        check_in_entry.insert(END, selected_item[3])
        check_out_entry.delete(0, END)
        check_out_entry.insert(END, selected_item[4])
    except IndexError:
        pass


def remove_item():
    try:
        if token_out:
            if room_text.get() == '' or guest_text.get() == '' or check_in_text.get() == '' or check_out_text.get() == '':
                messagebox.showerror(
                    'Required Fields', 'Please include all fields')

            else:

                pk = selected_item[0]
                end_point = f'http://localhost:8000/api/{pk}/delete/'
                data = {
                    "room_num": room_text.get(),
                    "guest": guest_text.get(),
                    "check_in": check_in_text.get(),
                    "check_out": check_out_text.get()
                }

                headers = {
                    "Content-Type": "application/json",
                    "Authorization": f"Token {token_out}"
                }
                response = requests.delete(
                    end_point, json=data, headers=headers)
                print(response.content)

                retrieve_list()

    except NameError:
        messagebox.showerror(
            'Login Failure', 'You have to Login first !!!')
    except Exception as e:
        print(e)


def update_item():
    try:
        if token_out:
            if room_text.get() == '' or guest_text.get() == '' or check_in_text.get() == '' or check_out_text.get() == '':
                messagebox.showerror(
                    'Required Fields', 'Please include all fields')

            else:
                pk = selected_item[0]
                end_point = f'http://localhost:8000/api/{pk}/edit/'
                data = {
                    "room_num": room_text.get(),
                    "guest": guest_text.get(),
                    "check_in": check_in_text.get(),
                    "check_out": check_out_text.get()
                }

                headers = {
                    "Content-Type": "application/json",
                    "Authorization": f"Token {token_out}"
                }
                response = requests.put(
                    end_point, json=data, headers=headers)
                if response.status_code >= 200 and response.status_code <= 299:
                    print(response.content)
                else:
                    messagebox.showerror(
                        'Update Failure', f'Incorrect Format, {response.text}')

                retrieve_list()

    except NameError:
        messagebox.showerror(
            'Login Failure', 'You have to Login first !!!')
    except Exception as e:
        print(e)


def clear_text():
    room_entry.delete(0, END)
    guest_entry.delete(0, END)
    check_in_entry.delete(0, END)
    check_out_entry.delete(0, END)


def login():
    global token_out
    global username
    global password
    username = username_entry.get()
    password = password_entry.get()
    if username == '' or password == '':
        messagebox.showerror(
            'Required Fields', 'Username/password should not be empty')

    else:
        data = {
            "username": username,
            "password": password
        }
        try:
            response = requests.post(
                'http://localhost:8000/api-token-auth/', data=data)
            response_dict = json.loads(response.text)
            token_out = response_dict['token']

        except Exception as e:
            messagebox.showerror(
                'Login Failure', 'Username/Credentials Invalid. Please try again !!!')
        retrieve_list()


# Create window object
app = Tk()

username_text = StringVar()
username_label = Label(app, text='Username', fg="red",
                       font=('bold', 10), padx=20)
username_label.grid(row=0, column=8, sticky=W)
username_entry = Entry(app, textvariable=username_text)
username_entry.grid(row=0, column=9)


password_text = StringVar()
password_label = Label(app, text='Password', fg="red",
                       font=('bold', 10), padx=20)
password_label.grid(row=1, column=8, sticky=W)
password_entry = Entry(app, show="*", textvariable=password_text)
password_entry.grid(row=1, column=9)

login_btn = Button(app, text='Login', fg="red", width=15, command=login)
login_btn.grid(row=2, column=9, pady=20)

# Room
room_text = StringVar()
room_label = Label(app, text='Room No',
                   font=('bold', 14), pady=20)
room_label.grid(row=0, column=0, sticky=W)
room_entry = Entry(app, textvariable=room_text)
room_entry.grid(row=0, column=1)
# Gues
guest_text = StringVar()
guest_label = Label(app, text='Guest', font=('bold', 14))
guest_label.grid(row=0, column=2, sticky=W)
guest_entry = Entry(app, textvariable=guest_text)
guest_entry.grid(row=0, column=3)
# Check in Date
check_in_text = StringVar()
check_in_label = Label(app, text='Check In Date', font=('bold', 14))
check_in_label.grid(row=1, column=0, sticky=W)
check_in_entry = Entry(app, textvariable=check_in_text)
check_in_entry.grid(row=1, column=1)
# Check Out Date
check_out_text = StringVar()
check_out_label = Label(app, text='Check Out Date', font=('bold', 14))
check_out_label.grid(row=1, column=2, sticky=W)
check_out_entry = Entry(app, textvariable=check_out_text)
check_out_entry.grid(row=1, column=3)
# Room Details (Listbox)

room_list = Listbox(app, height=8, width=50, border=1)
room_list.grid(row=2, column=0, columnspan=3, rowspan=6, pady=20, padx=20)
# Create scrollbar
scrollbar = Scrollbar(app)
scrollbar.grid(row=3, column=3)
# Set scroll to listbox
room_list.configure(yscrollcommand=scrollbar.set)
scrollbar.configure(command=room_list.yview)
# Bind select
room_list.bind('<<ListboxSelect>>', select_item)

# Buttons


add_btn = Button(app, text='Add Room', width=15, command=add_item)
add_btn.grid(row=3, column=9)

remove_btn = Button(app, text='Delete Room', width=15, command=remove_item)
remove_btn.grid(row=4, column=9)

update_btn = Button(app, text='Update Room', width=15, command=update_item)
update_btn.grid(row=5, column=9)

clear_btn = Button(app, text='Clear', width=15, command=clear_text)
clear_btn.grid(row=6, column=9)

app.title('Hotel Management')
app.geometry('750x350')

# Populate data
# retrieve_list()

# Start program
app.mainloop()

Retrieve API,

You should able to see something like this in Listbox, Of course if you have data in your Django model. Add/Update/Delete should work like a pro. If not let me know your question is comment section, I will try to help as much I can.

If you are really like this section, you can support me Buy Coffee which help me to post something like this kind of interesting topic.

Happy Coding !!!


1 reaction

Discussion

rocky
Posted 3 years, 4 months ago

good


useful

- rocky Posted 3 years, 4 months ago


Joined on April 19, 2020

Latest Videos