Search code examples
pythonuser-interfacepython-3.xtkinterpymysql

Checkbox values not being read properly


I have a Python program that gets information from a database; in this case, we're making hotel reservations. My issue is that if I use checkboxes and select multiple rooms--only the first in the list shows up. I believe the error to be a counting error of sorts that resides in my checkDetails method.

It would appear, then when trying to get the value of the checkbox, it only works for the first checkbox in the list. To be perfectly honest, I'm not 100% sure how to describe the issue. In lieu of words, I've uploaded four gifs showing the issue.

GOOD: Selecting the first two works fine. Selecting the first item alone works as well. No matter what, the first item always works.

BAD_1:Selecting the first item and another item (any item except the second) only displays the first item regardless.

BAD_2:Selecting any two items that don't involve the first item, results in neither item being displayed.

BAD_3:Selecting any single item that isn't the first item results in it not being displayed.

##(USER)MAKE NEW RESERVATION##
    def searchReservation(self):
        self.root = tk.Tk()
        self.root.title("Search Rooms")
    # city option button
        self.cityvariable = StringVar(self.root)
        self.cityvariable.set("Select City")
        w = OptionMenu(self.root, self.cityvariable, "Atlanta", "Charlotte",
                       "Savannah", "Orlando", "Miami").grid(row=0, column=0)
    # start and end labels
        self.startDate = tk.Label(
            self.root, text="Start Date (YYYY-MM-DD)") .grid(row=1, column=0)
        self.endDate = tk.Label(
            self.root, text="End Date (YYYY-MM-DD)").grid(row=1, column=1)
    # start and end entries
        self.startStringEntry = tk.Entry(self.root, state=NORMAL, width=10)
        self.startStringEntry.grid(row=2, column=0)
        self.startStringEntry.insert(0, '2015-11-23') #TODO debugging--remove
        self.endStringEntry = tk.Entry(self.root, state=NORMAL,     width=10)
        self.endStringEntry.grid(row=2, column=1)
        self.endStringEntry.insert(0, '2015-11-25') #TODO debugging--remove
    # search button
        self.search_button = tk.Button(
            self.root, text="Search Availabilities", command=self.makeReservation)
        self.search_button.grid()

        self.root.mainloop()

    def validDate(self, date):
        correctdate = None

        try:
            startdate = datetime.datetime.strptime(date, "%Y-%m-%d")
            correctdate = True
        except ValueError:
            correctdate = False

        if correctdate is False:
            messagebox.showerror(
                title="Warning", message="A valid date format is required.")
        return correctdate

    def makeReservation(self):
        if self.startStringEntry.get() == self.endStringEntry.get():
            messagebox.showerror(
                title="Warning", message="Reservation must be at least a day")
        elif not self.validDate(self.startStringEntry.get()) or not self.validDate(self.endStringEntry.get()):
            return
        elif datetime.datetime.strptime(self.startStringEntry.get(), '%Y-%m-%d') > datetime.datetime.strptime(self.endStringEntry.get(), '%Y-%m-%d'):
            messagebox.showerror(
                title="Warning", message="End date must be after start date")
        else:
            self.search_button.config(state='disabled')

            self.root.withdraw()
            self.makeRes = tk.Tk()
            self.makeRes.title("Make a Reservation")
        # date labels
            Label(self.makeRes, text="Start Date") .grid(row=0, column=0)
            Label(self.makeRes, text="End Date").grid(row=0, column=1)
            Label(self.makeRes, text=self.startStringEntry.get()).grid(
                row=1, column=0)
            Label(self.makeRes, text=self.endStringEntry.get()).grid(
                row=1, column=1)
            Label(self.makeRes, text='City').grid(row=0, column=2)
            Label(self.makeRes, text=self.cityvariable.get()).grid(
                row=1, column=2)
        # column headers
            roomnumber = tk.Label(
                self.makeRes, text="Room Number").grid(row=2, column=0)
            roomcat = tk.Label(self.makeRes, text="Room Category").grid(
                row=2, column=1)
            capacity = tk.Label(
                self.makeRes, text="Room Capacity").grid(row=2, column=2)
            costperday = tk.Label(
                self.makeRes, text="Cost Per Day").grid(row=2, column=3)
            costextra = tk.Label(
                self.makeRes, text="Extra Bed Cost").grid(row=2, column=4)
            availability = tk.Label(
                self.makeRes, text="Select Room").grid(row=2, column=5)
        # insert available rooms
            sql = """SELECT a.RoomNum, a.Category, b.RoomCapacity, a.CostPerDay, a.ExtraBedCost
                    FROM ROOM a, ROOM_CAPACITY b
                    WHERE a.Category = b.Category
                    AND a.RoomLocation = '""" + self.cityvariable.get() + """'
                    AND (
                    a.RoomNum, a.RoomLocation
                    ) NOT
                    IN (
                    SELECT DISTINCT c.RoomNum, c.Location
                    FROM ROOM_RESERVATION c, RESERVATION d
                    WHERE c.ReservationID = d.ReservationID
                    AND c.Location = a.RoomLocation
                    AND d.RefundAmount IS NULL
                    AND """ + self.endStringEntry.get() + """ <= d.EndDate
                    AND """ + self.startStringEntry.get() + """ >= d.StartDate
                    )"""

            cursor.execute(sql)
            self.count = 3
            self.data = []
            for data in cursor:
                self.data.append(data)
                for i in range(5):
                    Label(self.makeRes, text=data[i]).grid(
                        row=self.count, column=i)
                self.count = self.count + 1
            if self.count == 3:
                messagebox.showerror(
                    title="Warning", message="No rooms available for city and dates")
                self.makeRes.destroy()
                self.searchReservation()
            else:
                # making checkboxes to select room
                self.vars = []
                for rowNum in range(3, self.count):
                    self.var = IntVar(self.makeRes)
                    Checkbutton(self.makeRes, variable=self.var).grid(
                        row=rowNum, column=5)
                    self.vars.append(self.var)
                self.check_detail_button = Button(
                    self.makeRes, text='Check Details', command=self.checkDetails)
                self.check_detail_button.grid(row=self.count + 1, column=5)

    def checkDetails(self):
        noneselected = True
        for i in self.vars:
            if i.get() == 1:
                noneselected = False
                break
        if noneselected is True:
            messagebox.showerror(title="Warning", message="No rooms selected")
        else:
            self.check_detail_button.config(state='disabled')

            ttk.Separator(self.makeRes, orient=HORIZONTAL).grid(
                column=0, row=self.count+2, columnspan=10, sticky=(W, E))

        # column headers
            Label(self.makeRes, text="Room Number").grid(
                row=self.count + 3, column=0)
            Label(self.makeRes, text="Room Category").grid(
                row=self.count + 3, column=1)
            Label(self.makeRes, text="Room Capacity").grid(
                row=self.count + 3, column=2)
            Label(self.makeRes, text="Cost Per Day").grid(
                row=self.count + 3, column=3)
            Label(self.makeRes, text="Extra Bed Cost").grid(
                row=self.count + 3, column=4)
            Label(self.makeRes, text="Select Extra Bed").grid(
                row=self.count + 3, column=5)

            self.count += 4
            new_count = 0

            for data in self.data:
                print(data, self.vars[new_count].get()) #TODO remove
                if self.vars[new_count].get() == 1:
                    for i in range(5):
                        Label(self.makeRes, text=data[i]).grid(
                            row=self.count + new_count, column=i)
                    new_count += 1

            # making checkboxes to select beds
            self.beds = []
            count = 0
            for rowNum in range(self.count, self.count + new_count):
                if self.vars[count].get() == 1:
                    var = IntVar(self.makeRes)
                    checkBox = tk.Checkbutton(
                        self.makeRes, variable=var).grid(row=rowNum, column=5)
                    self.beds.append(var)
                count += 1
            self.count += new_count
            Label(self.makeRes, text='Total Cost').grid(
                row=self.count + 1, column=2)

            # CALCULATE TOTAL COSTS
            total_cost = 0
            count = 0
            self.new_data = []
            for i in self.data:
                if self.vars[count].get() == 1:
                    total_cost += i[3]
                    self.new_data.append(i)
                count += 1

            val = str(total_cost)
            self.total_costs_entry = Entry(self.makeRes)
            self.total_costs_entry.grid(row=self.count + 1, column=3)
            self.total_costs_entry.insert(0, val)
            self.total_costs_entry.config(state='readonly')
            Button(self.makeRes, text='Update total', command=self.updateTotalCosts).grid(
                row=self.count + 1, column=4)

            Label(self.makeRes, text='Use Card').grid(
                row=self.count + 2, column=2)
            cursor.execute(
                'SELECT CardNum FROM PAYMENT_INFO WHERE Username="' + self.user + '"')
            cards = ['-']
            for i in cursor:
                cards.append(i)
            self.card = StringVar(self.makeRes)
            self.opts = OptionMenu(self.makeRes, self.card, *cards)
            self.opts.grid(row=self.count + 2, column=3)
            Button(self.makeRes, text="Add Card", command=self.addCreditCard).grid(
                row=self.count + 2, column=4)
            Button(self.makeRes, text="Delete Card", command=self.deleteCard).grid(
                row=self.count + 2, column=5)

            Button(self.makeRes, text='Submit', command=self.submitReservation).grid(
                row=self.count + 3, column=5)

    def updateTotalCosts(self):
        total_cost = 0
        count = 0
        self.new_data = []
        for i in self.data:
            if self.vars[count].get() == 1:
                total_cost += i[3]
                self.new_data.append(i)
            count += 1
        count = 0
        for i in self.new_data:
            if self.beds[count].get() == 1:
                total_cost += i[4]
            count += 1
        cursor.execute("SELECT DATEDIFF(%s, %s)",
                       (self.endStringEntry.get(), self.startStringEntry.get()))
        diff = cursor.fetchone()
        diff = diff[0]
        total_cost = diff * total_cost

        self.total_costs_entry.destroy()
        self.total_costs_entry = Entry(self.makeRes)
        self.total_costs_entry.grid(row=self.count + 1, column=3)
        self.total_costs_entry.insert(0, str(total_cost))
        self.total_costs_entry.config(state='readonly')

EDIT: Why not just do this?

    for data in self.data:
            print(data, self.vars[new_count].get()) #TODO debugging--remove
            if self.vars[new_count].get() == 1:
                for i in range(5):
                    Label(self.makeRes, text=data[i]).grid(
                        row=self.count + new_count, column=i)
            new_count += 1

Solution

  • Definitely one problem is that you're creating more than one instance of tk.Tk(). Tkinter isn't designed to work that way, and it will yield problems similar to what you're seeing.

    If you need additional windows, you must create instances of tk.Toplevel.

    Another problem is this loop:

    for data in self.data:
        if self.vars[new_count].get() == 1:
            for i in range(5):
                Label(self.makeRes, text=data[i]).grid(
                    row=self.count + new_count, column=i)
            new_count += 1
    

    There is a fundamental flaw in the logic. Even though you loop over the data, you keep checking self.vars[new_count]. So, even though you eventually land on data item 3, 4, 5, etc, you keep checking self.vars[0] over and over and over. Once you find one checked item, you now keep looking at self.vars[1] even though you may now be on data item number 3, 4, 5, etc.

    One simple fix is to make sure you're iterating over the variables the same as you are iterating over the data values:

    for var, data in zip(self.vars, self.data):
        if var.get() == 1:
            for i in range(5):
                Label(self.makeRes, text=str(data[i]) + "?").grid(
                    row=self.count + new_count, column=i)
            new_count += 1
    

    The above assumes there is a 1:1 relationship between the items in self.vars and the items in self.data. It's hard to tell if that's a valid assumption or not.