Kivy AttributeError: объект 'NoneType' не имеет атрибута 'bind' - PullRequest
0 голосов
/ 16 марта 2020

Я клонировал свой текущий проект на другой p c, установил python и все зависимости и выполнил мой GUI просто отлично. После нескольких попыток отладки с pyCharm мой GUI больше не будет запускаться ни из pyCharm, ни из командной строки. Я попытался установить другую версию python с нуля, чтобы проверить, не испортил ли я что-то, однако ошибка на одном p c осталась прежней, а на моем ноутбуке работает точно такой же код.

My python Файл уже достаточно большой, поэтому я удалил много своих функций, надеюсь, мой код все еще несколько понятен (я оставил в нем несколько более сложных функций).

Я получаю следующую ошибку: AttributeError : Объект 'NoneType' не имеет атрибута 'bind' . Однако я не использую * .bind ни в своем .py, ни в своем файле .kv, поэтому я предполагаю, что это может повлиять на то, как я создаю всплывающие окна и добавляю в него виджеты? Или как я загружаю компоновщик / инициализирую мой GUI?

Ошибка:

Traceback (most recent call last):
   File "GUI.py", line 800, in <module>
     GUI().run()
   File "C:\Python38\lib\site-packages\kivy\app.py", line 949, in run
     self._run_prepare()
   File "C:\Python38\lib\site-packages\kivy\app.py", line 919, in _run_prepare
     root = self.build()
   File "GUI.py", line 797, in build
     return BoxL()
   File "GUI.py", line 76, in __init__
     super(BoxL, self).__init__()
   File "C:\Python38\lib\site-packages\kivy\uix\boxlayout.py", line 145, in __init__
     super(BoxLayout, self).__init__(**kwargs)
   File "C:\Python38\lib\site-packages\kivy\uix\layout.py", line 76, in __init__
     super(Layout, self).__init__(**kwargs)
   File "C:\Python38\lib\site-packages\kivy\uix\widget.py", line 359, in __init__
     self.apply_class_lang_rules(
   File "C:\Python38\lib\site-packages\kivy\uix\widget.py", line 463, in apply_class_lang_rules
     Builder.apply(
   File "C:\Python38\lib\site-packages\kivy\lang\builder.py", line 539, in apply
     self._apply_rule(
   File "C:\Python38\lib\site-packages\kivy\lang\builder.py", line 661, in _apply_rule
     self._apply_rule(
   File "C:\Python38\lib\site-packages\kivy\lang\builder.py", line 661, in _apply_rule
     self._apply_rule(
   File "C:\Python38\lib\site-packages\kivy\lang\builder.py", line 661, in _apply_rule
     self._apply_rule(
   [Previous line repeated 4 more times]
   File "C:\Python38\lib\site-packages\kivy\lang\builder.py", line 657, in _apply_rule
     child = cls(__no_builder=True)
   File "C:\Python38\lib\site-packages\kivy\uix\spinner.py", line 155, in __init__
     build_dropdown()
   File "C:\Python38\lib\site-packages\kivy\uix\spinner.py", line 166, in _build_dropdown
     self._dropdown = cls()
   File "C:\Python38\lib\site-packages\kivy\uix\dropdown.py", line 218, in __init__
     Window.bind(
 AttributeError: 'NoneType' object has no attribute 'bind'

Python файл (сокращенный):

#kivy imports
from kivy.app import App
from kivy.config import Config
from kivy.properties import StringProperty
from kivy.properties import ObjectProperty
from kivy.properties import NumericProperty
from kivy.properties import BooleanProperty
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.popup import Popup
from kivy.uix.video import Video
from kivy.uix.spinner import Spinner
from kivy.uix.button import Button
from kivy.uix.togglebutton import ToggleButton
from kivy.uix.label import Label
from kivy.uix.switch import Switch
from kivy.uix.filechooser import FileChooserListView
from kivy.uix.slider import Slider
from kivy.uix.progressbar import ProgressBar
from kivy.uix.textinput import TextInput
from kivy.clock import Clock


#various imports
from datetime import datetime #for time in logging
import logging
import webbrowser #for mailto
import os #for opening files or folders, currently only under windows
import glob #for opening most recent file

#from threading import Thread
import concurrent.futures #higher level threading API for loading big scripts

#imports functions
from restart_gui import restart_program #restarts GUI
from XY import open_com_port
from XY import open_sys_port
from XY import check_connectivity
from XY import check_connectivity_silent
from get_board_id import get_board_id
from get_resolution import get_resolution


class BoxL(BoxLayout): 
    """ Define class PageLayout here; This Class is used to create functions 
    and variables, which are then called in the GUI.kv file """
    def __init__(self): 
    # The super function in Python can be 
    # used to gain access to inherited methods 
    # which is either from a parent or sibling class. 
        super(BoxL, self).__init__()

    """ ##################### ROOT VARIABLES ##################### """

    s = 0
    c = 0
    comEnabled = BooleanProperty(False)
    sysEnabled = BooleanProperty(False)
    sWork = BooleanProperty(False)
    cWork = BooleanProperty(False)


    laserColor = StringProperty()
    redEnabled = BooleanProperty(True)
    greenEnabled = BooleanProperty(True)
    blueEnabled = BooleanProperty(True)
    #Calibration (True = enabled)
    calibrationEnabled = BooleanProperty(True)
    someOtherVariable = NumericProperty(0)
    scanUp = StringProperty('normal')
    scanDown = StringProperty('down')
    scanLeft = StringProperty('normal')
    scanRight = StringProperty('down')

    """ Defines column width; Kivy use: size_hint_x: root.devCol1 """
    devCol1 = NumericProperty(0.2) #First colum width: 20%
    devCol2 = NumericProperty(0.8) #Second column width: 80%

    """ Messages between python/kivy and output to kivy/shell """
    labelText = StringProperty() #Status+Debug Message on bottom of GUI
                                 #Output to kivy (text: root.labelText);
                                 #access in python: self.change_label('TEXT'), 
                                 #in kivy file: root.change_label('text')
    boardID = StringProperty('No ID') #Variable for board ID
    boardRes = StringProperty('No Res') #Variable for board resolution

    """ Logging Variables; used by function change_label; 
    additional logging with: logging.[debug/info/error]('TEXT') """
    logger = logging.getLogger()
    logger.setLevel(logging.DEBUG)
    logDateTime = datetime.now().strftime('%d-%b-%Y_%H-%M-%S')
    fh = logging.FileHandler('GUI\\logs\\logfile_' + logDateTime + '.log')
    fh.setLevel(logging.DEBUG)
    logger.addHandler(fh)
    logging.debug('############## START OF LOG #### ' + \
                  logDateTime + ' ##############')

    """ ############################ FUNCTIONS ########################## """

    def change_label(self, labelValue):
        """ change_label function to write to labelText (stringProperty)
        Arguments: labelValue (string)
        example: call from widget in kivy file: root.changelabel('TEXT')
        call from python: self.change_label('TEXT') """
        dateTimeObj = datetime.now()
        self.labelText = dateTimeObj.strftime("%H:%M:%S") + \
                         ' ' + labelValue
        logging.debug(dateTimeObj.strftime('%d-%b-%Y %H-%M-%S: ') + \
                      labelValue)     #writes to log

    def mailto_me(self):
        """ Opens standard email client to send email; Package: webbrowser """
        recipient = 'xy@xy.com'
        subject = 'Support'
        body = 'Name: \n Text: '
        body = body.replace(' ', '%20')
        body = body.replace('\n','%0D%0A')
        webbrowser.open('mailto:?to=' + recipient + '&subject=' + subject + '&body=' + body, new=1)
        self.change_label('XY')

    def open_file(self, filename):
        """ Function to open file; Package: os """
        os.startfile(filename)
        self.change_label('Opened file: ' + str(filename))

    def open_logfolder(self):
        """ Function to open folder GUI\logs; Package: os """
        logfolder = 'GUI\\logs'
        os.startfile(logfolder)
        self.change_label('Opened folder: ' + str(logfolder))

    def get_latest_log(self):
        """ Function to get latest log file from folder GUI\logs; Packages: os, glob """
        currentPath = os.getcwd()
        logPath = currentPath + '\\GUI\\logs\\*'
        fileList = glob.glob(logPath)
        lastFile = max(fileList, key=os.path.getctime)
        os.startfile(lastFile) 
        self.change_label('Opened latest logfile: ' + str(lastFile))

    def getID(self, kivyObject):
        """ Gets ID (defined in kivy file) from kivy object and returns string
        Arguments: kivyObject
        Example kivy: on_release: root.function(*args) (*args includes object and objectValue)
        Example python: def function(self, kivyObject, kivyObjectValue)
                            objectID = self.getID(kivyObject) """
        for i_d in self.ids:
            if kivyObject == self.ids[i_d]:
                thisID = i_d
                break
        return thisID

    def disable_everything(self, bool): #TODO: implement, disable all gui elements besides open com/sys
        if True: #condition when disable_everything(True) is called
            print('yet to be implemented')

        else: #Condition when disable_everything(False) is called
            pass

    def reset_laser(self):
        """ Restores configuration from flash-values """
        if self.check():
            restore_calibration(self.c) #Function call
            self.read_lasers()
            self.change_ledstatus()
            self.ids.switchEnableRed.active = True
            self.ids.switchEnableGreen.active = True
            self.ids.switchEnableBlue.active = True

    def save_config_to_file_(self):
        """ Saves the Flash configuration to a BIN-file """
        self.change_label('Initializing save configuration from flash to file ... (~3min)')
        if self.check():

            executor = concurrent.futures.ThreadPoolExecutor(max_workers=2) #This outside of function? create second thread from another location/popup/?    
            threadB = executor.submit(save_config_to_file, self.s, self.c) #starts save_config_to_file in thread
            return_value = threadB.result()         
            self.change_label('Configuration saved to: ' + str(return_value) )
            self.confPop.dismiss()


    """ KIVY Popups """ 
    def devPopup(self): #call from kivy-file with root.devPopup()
        #create popup
        self.devPop = Popup()
        self.devPop.title = 'GUI Developer Warning'
        self.devPop.title_size = 20
        self.devPop.title_align = 'center'
        self.devPop.auto_dismiss = True
        self.devPop.size_hint =  (None, None)
        self.devPop.size = (400, 300)
        #create popup-content
        box = BoxLayout()
        box.add_widget(Label(text = '[u][b]Warning!![/b][/u]\n\nUsing the developer mode might damage\nyour device, use with caution!',
                             color = (1, 1, 1, 1), #White text
                             pos_hint = {'center_x': .5, 'center_y': .5},
                             halign = 'center',
                             valign = 'middle'))
        #add content, open popup
        self.devPop.content = box
        self.devPop.open()

    def confirmPopup(self): #call from kivy-file with root.confirmPopup()
        """ Popup to confirm action save_config_to_file_ """ 
        #create popup
        self.confPop = Popup()
        self.confPop.title = 'Confirm ction'
        self.confPop.title_size = 20
        self.confPop.title_align = 'center'
        self.confPop.auto_dismiss = False
        self.confPop.size_hint =  (None, None)
        self.confPop.size = (250, 300)
        #create popup-content      
        confBox = BoxLayout()
        confBox.orientation = 'vertical'
        confBox.spacing = 5
        confBox.padding = (10, 30, 10, 30)
        confBox.add_widget(Label(text = 'Please confirm your action!',
                                 color = (1, 1, 1, 1), #white text
                                 pos_hint = {'center_x': .5, 'center_y': .5},
                                 halign = 'center'))
        confBox.add_widget(Button(text = 'Accept',
                                  #size_hint=(1,0.5),
                                  on_release = lambda *args: confAcc()))
        confBox.add_widget(Button(text='Cancel',
                                  on_release = lambda *args: self.confPop.dismiss()))
        #inner function
        def confAcc():
            print('IMPLEMENT CLOCK CALLBACK')
            self.save_config_to_file_()
            self.confPop.dismiss()
        #add content, open popup
        self.confPop.content = confBox
        self.confPop.open()

    def chooseFilePopup(self):
        """ Popup to chose file for picture-screen """
        #create popup
        self.chosePop = Popup()
        self.chosePop.title = '[b]Choose picture file to display on XYZ\u00AE[/b]'
        self.chosePop.title_color = (0, 0, 0, 1)
        self.chosePop.title_size = 20
        self.chosePop.title_align = 'center'
        self.chosePop.auto_dismiss = False
        self.chosePop.size_hint = (None, None)
        self.chosePop.size = (600, 600)
        self.chosePop.background = '' #white background basis
        self.chosePop.background_color = (71/255, 118/255, 176/255, 0.75) #Blue, 80% transparency
        #create popup-content
        choseBox = BoxLayout()
        choseBox.orientation = 'vertical'
        choseBox.spacing = 6
        fichoo = FileChooserListView(dirselect = True, path = './')
        choseBox.add_widget(Label(text = 'Warning!\n\nMake sure to chose *.png file with resolution 640x480!!', size_hint_y=0.3,
                                  font_size = '22',
                                  halign = 'center'))
        choseBox.add_widget(fichoo)
        choseBox.add_widget(Button(text='Load File', 
                                   size_hint_y = 0.2,
                                   size_hint_x = 0.5,
                                   pos_hint = {'center_x': 0.5},
                                   on_release = lambda *args: load_from_filechooser(fichoo.selection)))   
        choseBox.add_widget(Button(text='Cancel', 
                                   size_hint_y = 0.2,
                                   size_hint_x = 0.5,
                                   pos_hint = {'center_x': 0.5},
                                   on_release = lambda *args: self.chosePop.dismiss()))
        #inner function
        def load_from_filechooser(selection):
            print(str(selection[0]))
            print('demo(decision, self.s, show)')
            if self.check():
                demo(str(selection[0]), self.s, 0)    
                self.change_label('Image from file ' + str(selection[0] + ' displayed'))
                self.ids.pictureBox.source = selection[0]
                self.chosePop.dismiss()
        self.chosePop.content = choseBox
        self.chosePop.open()

#Creates the App Class
class GUI(App):
    #Build Function:
    def restart(self):
        BoxL.change_label(BoxL, '### RESTARTING GUI .. ###')
        restart_program()

    def build(self):
        """ GUI Window Configuration """
        self.title = 'MyApp\u00AE' #\u00AE for registered trademark
        #self.icon = 'trl_icon.ico'
        Config.set('kivy','window_icon','GUI\icon2.png')
        Config.set('graphics','width','1376') #1280
        Config.set('graphics','height','774') #720
        Config.set('kivy','exit_on_escape','0') #Disables ESC to exit GUI
        fonts = ['GUI/fonts/calibril.ttf',
                 'GUI/fonts/calibrili.ttf',
                 'GUI/fonts/calibrilb.ttf',
                 'GUI/fonts/calibrilz.ttf']
        Config.set('kivy','default_font',fonts)
        Config.write()
        return BoxL()

if __name__ == "__main__":
    GUI().run()

...