使用 Kivy 1.9.1 时出现一些奇怪的错误

Some weird error using Kivy 1.9.1

我正在尝试将 class 项目中的一些 python 代码用于 Kivy 应用程序,只是为了练习。我知道无论我 运行 只是在 IDLE 中还是作为 IPython 笔记本,代码都可以正常工作,但是当我 运行 kivy 应用程序中的代码时,我遇到了一些奇怪的错误。以下是回溯:

Traceback (most recent call last):
   File "/home/havik/workspace/kivyapp2.7/main.py", line 215, in <module>
     TutorialApp().run()
   File "/usr/lib/python2.7/dist-packages/kivy/app.py", line 824, in run
     runTouchApp()
   File "/usr/lib/python2.7/dist-packages/kivy/base.py", line 487, in runTouchApp
     EventLoop.window.mainloop()
   File "/usr/lib/python2.7/dist-packages/kivy/core/window/window_sdl2.py", line 539, in mainloop
     self._mainloop()
   File "/usr/lib/python2.7/dist-packages/kivy/core/window/window_sdl2.py", line 300, in _mainloop
     EventLoop.idle()
   File "/usr/lib/python2.7/dist-packages/kivy/base.py", line 330, in idle
     self.dispatch_input()
   File "/usr/lib/python2.7/dist-packages/kivy/base.py", line 315, in dispatch_input
     post_dispatch_input(*pop(0))
   File "/usr/lib/python2.7/dist-packages/kivy/base.py", line 221, in post_dispatch_input
     listener.dispatch('on_motion', etype, me)
   File "_event.pyx", line 699, in kivy._event.EventDispatcher.dispatch (kivy/_event.c:6856)
   File "/usr/lib/python2.7/dist-packages/kivy/core/window/__init__.py", line 904, in on_motion
     self.dispatch('on_touch_down', me)
   File "_event.pyx", line 699, in kivy._event.EventDispatcher.dispatch (kivy/_event.c:6856)
   File "/usr/lib/python2.7/dist-packages/kivy/core/window/__init__.py", line 920, in on_touch_down
     if w.dispatch('on_touch_down', touch):
   File "_event.pyx", line 699, in kivy._event.EventDispatcher.dispatch (kivy/_event.c:6856)
   File "/usr/lib/python2.7/dist-packages/kivy/uix/screenmanager.py", line 1069, in on_touch_down
     return super(ScreenManager, self).on_touch_down(touch)
   File "/usr/lib/python2.7/dist-packages/kivy/uix/widget.py", line 382, in on_touch_down
     if child.dispatch('on_touch_down', touch):
   File "_event.pyx", line 699, in kivy._event.EventDispatcher.dispatch (kivy/_event.c:6856)
   File "/usr/lib/python2.7/dist-packages/kivy/uix/relativelayout.py", line 276, in on_touch_down
     ret = super(RelativeLayout, self).on_touch_down(touch)
   File "/usr/lib/python2.7/dist-packages/kivy/uix/widget.py", line 382, in on_touch_down
     if child.dispatch('on_touch_down', touch):
   File "_event.pyx", line 699, in kivy._event.EventDispatcher.dispatch (kivy/_event.c:6856)
   File "/usr/lib/python2.7/dist-packages/kivy/uix/relativelayout.py", line 276, in on_touch_down
     ret = super(RelativeLayout, self).on_touch_down(touch)
   File "/usr/lib/python2.7/dist-packages/kivy/uix/widget.py", line 382, in on_touch_down
     if child.dispatch('on_touch_down', touch):
   File "_event.pyx", line 699, in kivy._event.EventDispatcher.dispatch (kivy/_event.c:6856)
   File "/usr/lib/python2.7/dist-packages/kivy/uix/behaviors.py", line 135, in on_touch_down
     self.dispatch('on_press')
   File "_event.pyx", line 695, in kivy._event.EventDispatcher.dispatch (kivy/_event.c:6815)
   File "_event.pyx", line 1168, in kivy._event.EventObservers.dispatch (kivy/_event.c:11690)
   File "_event.pyx", line 1052, in kivy._event.EventObservers._dispatch (kivy/_event.c:10730)
   File "/usr/lib/python2.7/dist-packages/kivy/lang.py", line 1465, in custom_callback
     exec(__kvlang__.co_value, idmap)
   File "/home/havik/workspace/kivyapp2.7/tutorial.kv", line 27, in <module>
     on_press: app.updateDatabase()
   File "/home/havik/workspace/kivyapp2.7/main.py", line 125, in updateDatabase
     if sheetnames[x-1] == "MegaMillions":
 TypeError: unsupported operand type(s) for -: '_ElementStringResult' and 'int'

我也 运行 代码中的另一行出现同样的错误,当我使用它时 ---> x -= 1.

此错误发生在 updateDatabase() 中。 我只能通过使用这个 --> x = int(x) - 1 来克服递减错误,否则错误仍然存​​在。如果我使用这个 --> sheetnames[int(x)-1] 它将克服错误,但我不是为什么我必须这样做,因为 x 已经包含一个整数值首先。在传递这个特定错误之后,程序出于某种原因在我使用代码的函数内的区域为我提供了同一函数内超出范围的列表索引 --> sheetnames[x-1].

我在 Python 3.4 IDLE 和 IPython Notebook 2.1 和 2.2 中测试了该功能,它运行良好。如果有人想知道我是什么 IDE,我使用的是最新版本的 Eclipse Luna。

以下是我的应用程序的完整代码:

'''
Created on May 6, 2015

@author: havik
'''
from kivy.app import App
from kivy.uix.scatter import Scatter
from kivy.uix.label import Label
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.textinput import TextInput
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.gridlayout import GridLayout
from kivy.uix.relativelayout import RelativeLayout
from kivy.core.window import Window
from kivy.uix.image import Image
from kivy.graphics import BorderImage
from kivy.graphics import Color, Rectangle, Line
from kivy.uix.button import Button


import random
import matplotlib.pyplot as plt
import pandas as pd
#import pylab as pl
import requests
import openpyxl as xl
#from operator import itemgetter
from collections import Counter
from lxml import html
import numpy as np

class HomeScreen(Screen):
    pass

class LotterySelcetionScreen(Screen):
    pass

class TableScreen(Screen):
    pass

class TutorialApp(App):

    screen_manager = None
    lotteryName = ''

    #function to load the table form the excel file corresponding to the passed sheet name
    def loadTable(self, sheetName):
        lotteryData = pd.ExcelFile("Lottery databases.xlsx") #grabs and loads the file into memory
        df = lotteryData.parse(sheetName) #loads the data table form the corresponding sheetName into the df data frame
        return df

    #function to display the table
    def showTable(self, table):
        #get the number of rows the table has
        no_of_rows = len(table.index)
        #display the table

        return table.head(no_of_rows)

    #function to display pie charts of a specific column within the database
    #table is the database that the function will be working with
    #and column is a numberical vaule of which column to get the data from
    def printPieChart(self, table, column):
        if column == 6:
            columnList = table.iloc[:, -1:].values.T.ravel()
        else:
            columnList = table.iloc[:, (column - 7): (column - 6)].values.T.ravel()
        countedList = Counter(columnList)

        #set up the size of the pie chart
        fig = plt.figure(figsize=[10, 10])
        ax = fig.add_subplot(111)
        cmap = plt.prism()

        #input variables for pie function
        slices = [float(v) for v in countedList.values()]
        colors = cmap(np.linspace(0., 1., len(slices)))
        labels = [float(k) for k in countedList]
        columnHeaders = list(table.columns.values)

        #the pie chart
        pie_wedge_collection = ax.pie(slices, colors = colors, labels = labels, labeldistance = 1.05, autopct = '%1.1f%%')
        #get rid of the black outlines between the wedges and around the pie
        for pie_wedge in pie_wedge_collection[0]:
            pie_wedge.set_edgecolor('white')
        ax.set_title(columnHeaders[column + 1])
        #can't display a Legends as there's too many for plt.legends() too handle
        #return pyplot.pie([float(v) for v in countedList.values()], labels = [float(k) for k in countedList])

    def updateDatabase(self):
        wb = xl.load_workbook("Lottery databases.xlsx") #load the workbook into memory

        #list of the sheet names within the workbook
        sheetnames = ["SuperLotto", "MegaMillions", "Powerball"]
        days = ["Fri. ", "Wed. ", "Tue. ", "Sat. "] #days the draws on done on
        #list of the webpages to use grab the new draws
        webPages = ['http://www.calottery.com/play/draw-games/superlotto-plus/winning-numbers', 'http://www.calottery.com/play/draw-games/mega-millions/winning-numbers', 'http://www.calottery.com/play/draw-games/powerball/winning-numbers']
        x = 3
        while x != 0:
            ws = wb.get_sheet_by_name(sheetnames[x-1]) # which sheet to update
            rowIndex = ws.get_highest_row() # gets the highest row index in the sheet
            #lastCellValue = ws.cell(row = rowIndex - 1, column = 0).value #gets the last value in the first column, draw number
            lastCellValue = ws.cell(row = rowIndex, column = 1).value #gets the last value in the first column, draw number, for openpyxl 2.0.0+
            page = requests.get(webPages[x-1]) #grabs the webpage needed
            tree = html.fromstring(page.text) #puts the webpage into a tree structure to make it easy to traverse
            #get the newest draw and date from the webpage for comparasion purposes
            draw_and_date = tree.xpath('//*[@id="objBody_content_0_pagecontent_0_objPastWinningNumbers_rptPast_ctl01_lblDrawDateNumber"]/text()')
            #if the table is up to date, it will move on to the next table else it will update it 
            y = int(draw_and_date[0][-4:]) - int(lastCellValue) # checks to see how many draws are missing from the table
            if y == 0:
                #print("The table for " + sheetnames[x-1] + " is up to date.")
                x -= 1 #decrement x by 1 to move on to the next table
            else:
                #while loop to check if the table needs to be updated or not, if yes it will update it
                while y != 0:
                    #grabs the draw and date of the missing draws from the table
                    draw_and_date = tree.xpath('//*[@id="objBody_content_0_pagecontent_0_objPastWinningNumbers_rptPast_ctl0' + str(y) + '_lblDrawDateNumber"]/text()')
                    numbers = tree.xpath(".//*[@id='content']/div[3]/table/tr[" + str(y) + "]/td[2]/span/text()") #numbers
                    numbers = [int(x) for x in numbers] # converts the text to integers
                    numbers.sort() #sort the number from smallest to largest
                    mega = tree.xpath(".//*[@id='content']/div[3]/table/tr[" + str(y) + "]/td[3]/text()") #mega number
                    mega = int(mega[0]) # converts the text to integers
                    #write to the file
                    if sheetnames[x-1] == "MegaMillions":
                        d = 0
                    else:
                        d = 1
                    if int(draw_and_date[0][-4:]) % 2 == 0:
                        # if the draw date is even then the day is a Tuesday/Saturday
                        ws.append([int(draw_and_date[0][-4:]), (days[d+2] + draw_and_date[0][:12]), numbers[0], numbers[1], numbers[2], numbers[3], numbers[4], mega]) # print the draw date
                    else:
                        # if the draw date is odd then the day is a Wednesday/Friday
                        ws.append([int(draw_and_date[0][-4:]), (days[d] + draw_and_date[0][:12]), numbers[0], numbers[1], numbers[2], numbers[3], numbers[4], mega])
                    y -= 1 #decrement y by 1 to get the next missing draw
                #print("Updated the " + sheetnames[x-1] + " table successfully!")
                x -= 1 #decrement x by 1 to move on to the next table
        wb.save("Lottery databases.xlsx") #save the workbook
        #print("Saved the database Sucessfully!")

    # function to get a list of the occurring numbers in each column
    # 6 for the first number columns or 1 for the mega/powerball column
    def getPopularList(self, table, x):
        popular_list = list()
        if x != 1:
            while x != 0:
                column_list = table.iloc[:, (0 - x): (1 - x)].values.T.ravel() # the all of the values in the column
                counted_list = Counter(column_list) # counts how many time each value occurs within the column
                top_five = counted_list.most_common(5) # the top five within the column
                top_five.sort() # sorts the top five
                popular_list = popular_list + top_five
                x -= 1 # decrement x by 1
        else:
            column_list = table.iloc[:, (0 - x):].values.T.ravel() # the all of the values in the column
            counted_list = Counter(column_list) # counts how many time each value occurs within the column
            top_ten = counted_list.most_common(10) # the top five within the column
            top_ten.sort() # sorts the top five
            popular_list = popular_list + top_ten
        #popular_list is actually a tuple of list which contains the value and how many times that value occured
        #but we only want the values by themselves
        popular_list_values = [y[0] for y in popular_list] #this gives us a list of the values
        return popular_list_values

    # Function to generate a ticket based on the popular numbers in the lottery
    def generate_ticket(self, table):
        firstFive = self.getPopularList(table, 6) # get the popular numbers for the first five slots
        mega = self.getPopularList(table, 1) # gets the popular numbers for the mega/powerball slot
        five = self.getNumbers(firstFive, 5) # gets first five numbers for the ticket
        one = self.getNumbers(mega, 1) #gets the mega/powerball for the ticket
        five.append(one)
        return five

    #function to get numbers
    def getNumbers(self, numbers_list, x):
        numbers = list() #empty list
        rand_range_list = list(range(0, len(numbers_list))) #list of the numbers to choice from at random
        if x != 1:
            while x != 0:
                y = random.choice(rand_range_list) #pick a number
                #it's not making the list properly at the moment, FIX IT!
                #numbers = numbers + numbers_list[y] #add a number to our list to return
                numbers.append(numbers_list[y]) #append the number to the end of the list
                rand_range_list.remove(y) #remove y from our rand_range_list to prevent any repeats
                x -= 1
        else:
            numbers = random.choice(numbers_list) # add a number to our list to return
        return numbers

    # function to print the ticket based on which lottery it's from
    def printTicket(self, ticket, lottery):
        #small bubblesort for the first five numbers
        for x in range(1, 5):
            for y in range(0, 4):
                if ticket[y] > ticket[y + 1]:
                    temp = ticket[y]
                    ticket[y] = ticket[y + 1]
                    ticket[y + 1] = temp
        #print statements depending on the lottery
        #print the ticket without any brackets
        if lottery != "Powerball":
            return str(ticket)[1:-4] + " Mega " + str(ticket[-1])
        else:
            return str(ticket)[1:-4] + " Powerball " + str(ticket[-1])

    def build(self):
        #Window.clearcolor = (1,1,1,1)
        self.screen_manager = ScreenManager()
        self.screen_manager.add_widget(HomeScreen(name='home'))
        self.screen_manager.add_widget(LotterySelcetionScreen(name='lottery_selection'))
        self.screen_manager.add_widget(TableScreen(name='table'))

        return self.screen_manager 

if __name__ == '__main__':
    TutorialApp().run()

这是 kv 文件:

<HomeScreen>:
    RelativeLayout:
        Image:
            source: 'images/background3.jpg'
            size_hint: None, None
            size: self.texture_size
            pos: self.pos
        Button:
            id: selectButton
            font_size: sp(30)
            text: 'Select Lottery'
            size_hint_x: None
            width: self.texture_size[0]
            size_hint_y: None
            height: self.texture_size[1]
            pos: root.width / 2 - (self.width / 2), root.height / 2
            on_press: root.manager.current = 'lottery_selection'
        Button:
            id: updateButton
            font_size: sp(30)
            text: 'Update Databases'
            size_hint_x: None
            width: self.texture_size[0]
            size_hint_y: None
            height: self.texture_size[1]
            pos: root.width / 2 - (self.width / 2), root.height / 2 - selectButton.height
            on_press: app.updateDatabase()
        Button:
            size_hint_x: None
            width: self.texture_size[0]
            size_hint_y: None
            height: self.texture_size[1]
            pos: root.width - self.width, 0
            font_size: sp(30)
            text: 'Quit'
            on_press: app.stop()

<LotterySelcetionScreen>:
    BoxLayout:
        orientation: 'vertical'
        Button:
            text: 'SuperLotto'
            on_press: root.manager.current = 'table'
            on_press: app.lotteryName = 'SuperLotto'
        Button:
            text: 'MegaMillions'
            on_press: root.manager.current = 'table'
            on_press: app.lotteryName = 'MegaMillions'
        Button:
            text: 'Powerball'
            on_press: root.manager.current = 'table'
            on_press: app.lotteryName = 'Powerball'
        Button:
            text: 'Back to home'
            on_press: root.manager.current = 'home'
            pos: root.width - self.width, 0

<TableScreen>:
    RelativeLayout:
        Image:
            source: 'images/background3.jpg'
            size_hint: None, None
            size: self.texture_size
            pos: self.pos
        Button:
            id: homeButton
            size_hint_x: None
            width: self.texture_size[0]
            size_hint_y: None
            height: self.texture_size[1]
            pos: root.width - self.width - quitButton.width, 0
            font_size: sp(30)
            text: 'Back to home'
            on_press: root.manager.current = 'home'
            on_press: label1.text = ''
            on_press: label2.text = ''
        Button:
            id: quitButton
            size_hint_x: None
            width: self.texture_size[0]
            size_hint_y: None
            height: self.texture_size[1]
            pos: root.width - self.width, 0
            font_size: sp(30)
            text: "Quit"
            on_press: app.stop()
        Label:
            id:label1
            size_hint_x: None
            width: self.texture_size[0]
            size_hint_y: None
            height: self.texture_size[1]
            font_size: sp(21)
            pos: (root.width / 2) - (self.width / 2), root.height / 2
            color: 1,.17,.17,1
        Label:
            id:label2
            size_hint_x: None
            width: self.texture_size[0]
            size_hint_y: None
            height: self.texture_size[1]
            font_size: sp(21)
            pos: (root.width / 2) - (self.width / 2), (root.height / 2) - label1.height
            color: 1,.17,.17,1
        Button:
            id: newButton
            text: 'New Ticket'
            size_hint_x: None
            width: self.texture_size[0]
            size_hint_y: None
            height: self.texture_size[1]
            font_size: sp(30)
            pos: root.width - self.width - quitButton.width - homeButton.width, 0
            on_press: label1.text=str(app.generate_ticket(app.loadTable(app.lotteryName)))[1:-1]
            on_press: label2.text = ""
            on_press: label1.font_size = sp(30)
        Button:
            id: listButton
            text: 'Popular Numbers'
            size_hint_x: None
            size_hint_y: None
            width: self.texture_size[0]
            height: self.texture_size[1]
            font_size: sp(30)
            pos: root.width - self.width - quitButton.width - homeButton.width - newButton.width, 0
            on_press: label1.text = str(app.getPopularList(app.loadTable(app.lotteryName), 6))[1:-1]
            on_press: label1.font_size = sp(21)
            on_press: label2.text = str(app.getPopularList(app.loadTable(app.lotteryName), 1))[1:-1]

您的问题是 python 的作用域规则,updateDatabase 方法中的这一行:

numbers = [int(x) for x in numbers] # converts the text to integers

它在此列表理解中重新分配局部变量 x。在这一行之后,x 将是 numbers 中最后一项的文本。只需在此处使用另一个变量即可修复。

一个好的 Python 惯用语是在结构中使用 _ 作为变量名,例如列表推导式,在这种情况下您只需要一点变量,之后不关心它的值。所以:

numbers = [int(_) for _ in numbers] # converts the text to integers