Sunday, September 4, 2016

Pymongo, Tkinter, New Review Form

Overview


After hours of trial and error and more than a little foul language, I have managed to create a tkinter form that retrieves the products from the TechStore mongo database and lists them in a combo box, and that allows a user to choose one of them and then write a new review, which is then written to the database. There is much more to do, but this is good beginning for now.

Here is a list of the Mongo and python tutorials so far

  1. First steps with Mongo
  2. Mongo Database Two
  3. Mongo three beginning python
  4. More Pymongo inserts
  5. Python Objects Data Classes

Creating the Window


In keeping with using an object oriented approach, I created a class called Window that inherits from Frame.

def __init__(self, master=None):
        Frame.__init__(self, master)                 
        self.master = master
        self.init_window()

All the self and master stuff takes some getting used to and part of what made all this more difficult that it should have been. The init_Window is a method that initializes all the windows controls.

The first thing I do is a Label to prompt the user to choose a product from the drop down list.

  # creating a label instance
        self.prompt = Label(self, text="Choose a product to Review")

        # placing the lable on the window
        self.prompt.place(x=10, y=0)

To get the products, I initialize the ProductManagment class and call two methods: populateProducts() and getProducts().The method populateProducts() is an addition since the blog on the data classes. It is there to make sure the array of products is populated. The whole code is available on the Github link at the end of the blog. The method getProducts() returns a cursor or array of product strings which I can use for the values argument of the ComboBox control.

  #calling the product management class
        #to get the products
        pm = ProductManagement.ProductManagement()
        #making sure the product list is populated
        pm.populateProducts()
        products=pm.getProducts()

        #creating the Combobox and populating it with products
        self.productsCombo=ttk.Combobox(self, values=products,state=READABLE)
        self.productsCombo.place(x=10, y=25)

I am not going to comment on all the objects in the form, but I will note the two buttons, briefly. The first button is used to save the user review. It calls a method called insertReview().

save=Button(self.master, text="Save Review",command = self.insertReview)
        save.place(x=10, y=360)

Here is the method the button calls that inserts the material from the form.

def insertReview(self):
         p=self.productsCombo.get()
         
         email=self.emailEntry.get()
         r=self.rating.get()
         rev=self.review.get("1.0",END)
         revDate=time.strftime("%c")

         document = {
            "product" : p,
            "email" : email,
            "date" : revDate,
            "rating" : r,
            "reveiw" : rev
            }
         md = MongoData.MongoData()
         result=md.insertReview(document)
         insertResult=Label(self, text=result)
         insertResult.place(x=10, y=385)

The second command is to exit the program. It uses a lambda pointer so that the button can call itself and execute a built in method in its own constructor. It kills the program, but doesn't close the form. A bug to work out later.

 exit=Button(self.master, text="Exit",command = lambda:exit.quit())
        exit.place(x=100, y=360)

Here is the complete code for the Windows class

from tkinter import *
from tkinter import ttk
import ProductManagement
import MongoData
import time

#class for creating a window and an
#entry form for a new review
#it inherits from tkinter.Frame
#I used absolute placement
#instead of a grid, which would
#probably provide more flexibility

class Window(Frame):

    def __init__(self, master=None):
        Frame.__init__(self, master)                 
        self.master = master
        self.init_window()

    #Creation of init_window
    def init_window(self):

        # changing the title of the master widget      
        self.master.title("TechStore")

        # allowing the widget to take the full space of the root window
        self.pack(fill=BOTH, expand=1)

        # creating a label instance
        self.prompt = Label(self, text="Choose a product to Review")

        # placing the lable on the window
        self.prompt.place(x=10, y=0)
        
        #calling the product management class
        #to get the products
        pm = ProductManagement.ProductManagement()
        #making sure the product list is populated
        pm.populateProducts()
        products=pm.getProducts()

        #creating the Combobox and populating it with products
        self.productsCombo=ttk.Combobox(self, values=products,state=READABLE)
        self.productsCombo.place(x=10, y=25)
        
        # creating email label
        self.emailLabel=Label(self, text="Enter your email")
        self.emailLabel.place(x=10, y=55)

        #creating email entry box
        self.emailEntry=Entry(self)
        self.emailEntry.place(x=10, y=75) 

        #creating the rating label
        self.ratingLabel=Label(self, text="Rate the product 1 to 5")
        self.ratingLabel.place(x=10, y=105)
        
        #creating the Entry for ratings
        self.rating=Entry(self)
        self.rating.place(x=10, y=125)

        #creating the review label
        self.reviewLabel =Label(self, text="Enter your review")
        self.reviewLabel.place(x=10, y=150)

        #creating the multiline Text enty
        #I wanted to add a scollbar, but every
        #attempt at it made the text box take
        #over the whole screen
        self.review =Text(self, height=10, width=50)
        self.review.place(x=10, y=170)
        
        #add the save review button
        save=Button(self.master, text="Save Review",command = self.insertReview)
        save.place(x=10, y=360)

        #add the exit button, which kills the window
        #but doesn't make it disappear
        #the lambda allows the button to contain
        #the quit command in its own constructor
        exit=Button(self.master, text="Exit",command = lambda:exit.quit())
        exit.place(x=100, y=360)
       
    #this method takes the values from the combo
    #and text boxes and passes them to
    #the MongoData class where they are
    #inserted. The structure of the review
    #is somewhat different and simpler
    #than the original in the Mongo examples
    def insertReview(self):
         p=self.productsCombo.get()
         
         email=self.emailEntry.get()
         r=self.rating.get()
         rev=self.review.get("1.0",END)
         revDate=time.strftime("%c")

         document = {
            "product" : p,
            "email" : email,
            "date" : revDate,
            "rating" : r,
            "reveiw" : rev
            }
         md = MongoData.MongoData()
         result=md.insertReview(document)
         insertResult=Label(self, text=result)
         insertResult.place(x=10, y=385)

   

         

root = Tk()

#size of the window
root.geometry("500x400")

app = Window(root)
root.mainloop()  

It is important to note that the Mongo server must be running for the program to execute. Here is a screenshot of the program, after having filled in all the entry boxes and clicked the save button:

techStore form

All the code, including the data classes https://github.com/spconger/ProductReviewPythonMongo

No comments:

Post a Comment