未正确读取复选框值

Posted

技术标签:

【中文标题】未正确读取复选框值【英文标题】:Checkbox values not being read properly 【发布时间】:2016-03-06 06:17:50 【问题描述】:

我有一个从数据库获取信息的 Python 程序;在这种情况下,我们正在预订酒店。我的问题是,如果我使用复选框并选择多个房间 - 只会显示列表中的第一个房间。我认为该错误是我的 checkDetails 方法中存在的各种计数错误。

它会出现,然后在尝试获取复选框的值时,它仅适用于列表中的第一个复选框。老实说,我不是 100% 确定如何描述这个问题。我已经上传了四个 GIF 来代替文字。

好: 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')

编辑:为什么不这样做?

    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

【问题讨论】:

(Wince) 像这样把界面和后端代码混在一起真是让人头疼;将代码分成几层是一个非常好的主意——一层用于与数据库通信,一层用于业务逻辑,一层用于接口代码。它将为您节省很多问题。 @HughBothwell 我知道。这部分其实不是我写的;一位团队成员对此部分进行了编码,我正在尝试为她解决它。两个小时后,我一无所获。 你做过任何调试吗?例如,您确定self.data 是否包含您认为在checkDetails 内部的数据? @BryanOakley 如果您查看代码,您会注意到我有一个打印语句,它显示复选框的值和 self.data 的内容。复选框的值不是我所期望的——否则这将起作用。数据正确。 【参考方案1】:

绝对的一个问题是您创建了多个tk.Tk() 的实例。 Tkinter 并非旨在以这种方式工作,它会产生与您所看到的类似的问题。

如果您需要额外的窗口,您必须创建tk.Toplevel 的实例。

另一个问题是这个循环:

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

逻辑上存在根本缺陷。即使您遍历数据,您仍会继续检查self.vars[new_count]。因此,即使您最终登陆数据项 3、4、5 等,您仍会一遍又一遍地检查self.vars[0]。一旦你找到一个选中的项目,你现在继续查看self.vars[1],即使你现在可能在数据项编号 3、4、5 等上。

一个简单的解决方法是确保您对变量的迭代与对数据值的迭代相同:

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

以上假设self.vars 中的项目与self.data 中的项目之间存在1:1 的关系。很难说这是否是一个有效的假设。

【讨论】:

就像我说的——我没有写这段代码。我不想大修它;这个答案让我更接近解决我的问题。我意识到代码有其缺陷——但我非常怀疑tk.Tk() 的多个实例。 我按照你说的做了——但仍然有同样的问题。 @Nxt3:我不是在责怪你。不管是谁写的代码,它都有一个致命的缺陷。这不仅仅是装饰,您实际上是在创建一个新的 tcl 解释器并丢弃旧的解释器,这可能会导致您丢失检查按钮的值等。无论如何,我在代码中发现了另一个错误,我已经更新了我的答案。 我应该补充一点,在整个程序中修复多个 tk.Tk() 已经解决了我遇到的另一个问题。感谢您引起我的注意!我觉得我最初的评论有点苛刻。你是对的——这是一个问题。 难道你不能也只是在我对 OP 的编辑中提出建议吗?

以上是关于未正确读取复选框值的主要内容,如果未能解决你的问题,请参考以下文章

未正确捕获表单数据

Angular 反应复选框仅在第一次单击后正确切换

如何用js读取复选框的值?

回发后未选中复选框

Bootstrap - 未捕获的类型错误:无法读取复选框单击时未定义的属性“单击”

jstree添加复选框插件导致Uncaught TypeError:无法读取未定义的属性'selected'