Help Needed Type error when closing app

39 views
Skip to first unread message

Gibbsy FPV

unread,
May 10, 2022, 7:27:29 AM5/10/22
to kivy-...@googlegroups.com

Hi All

 

I have made an app that monitors a product over serial communication it mostly  works fine but I get an error when closing but only once I have connected / disconnected to the serial. I am very new to kivy so I probably have a number of mistakes.

 

Any help would be greatly appreciated

 

Thanks

GibbsyFPV

 

Code below

 

.py

 

 from kivy.lang import Builder

from kivy.metrics import dp

from kivymd.app import MDApp

from kivymd.uix.menu import MDDropdownMenu

from kivy.core.window import Window

from kivy.clock import Clock, mainthread

from kivy.uix.widget import Widget

from kivy.properties import ObjectProperty

from kivymd.uix.boxlayout import MDBoxLayout

from kivymd.uix.dialog import MDDialog

from kivymd.uix.button import MDFlatButton, MDRectangleFlatButton

 

import serial.tools.list_ports

import serial

import time

from threading import Thread

import threading

import re

 

class MenuHeader(MDBoxLayout):

    '''An instance of the class that will be added to the menu header.'''

 

class KFSMonitor(MDApp):

    def __init__(self, serialPort = None, serialBaud = 2400, **kwargs):

        super(KFSMonitor, self).__init__(**kwargs)

        self.dialog = 0

        self.port = serialPort

        self.baud = serialBaud

        self.runcheck = 0

        self.closethread = None

        self.data = 0

        self.thread = None

        self.threadKill = None

        self.start = 0

        self.port_list = []

 

        ports = serial.tools.list_ports.comports()

 

        #scan for ports

        for port, desc, hwid in sorted(ports):

            self.port_list.append(port)

 

        self.screen = Builder.load_file('kfsmonitor.kv')

        items_d = self.port_list

        menu_items = [

            {

                "text": f"{i}",

                "viewclass": "OneLineListItem",

                "height": dp(56),

                "on_release": lambda x=f"{i}": self.menu_callback(x),

            } for i in items_d

        ]

        self.menu = MDDropdownMenu(

            header_cls=MenuHeader(),

            caller=self.screen.ids.toolBarTop,

            items=menu_items,

            width_mult=2,

            position="bottom",

        )

 

    def showAlertBox(self):

        if not self.dialog:

            self.dialog = MDDialog(

                title = "Port Error",

                text = "Please try another Port",

                buttons = [

                    MDRectangleFlatButton(

                        text = "OK", text_color=self.theme_cls.primary_color, on_release = self.close_dialog

                    )

                ],

            )

        self.dialog.open()

   

    def close_dialog(self, obj):

        self.dialog.dismiss()

 

 

    def menu_callback(self, text_item):

        self.serialPort = text_item

        self.menu.dismiss()

        try:

            self.serialConnection = serial.Serial(self.serialPort, self.baud, timeout=1)

            self.root.ids.toolBarBottom.title = self.serialPort

            self.root.ids.mainToolBarBottom.disabled = False

 

        except serial.serialutil.SerialException:

            #print("no port")

            self.root.ids.mainToolBarBottom.disabled = True

            self.root.ids.toolBarBottom.title = ""

            try:

                self.serialConnection.close()

            except:

                pass

           

            self.showAlertBox()

 

        #print(self.serialPort)

 

    def runchecker(self):

        if self.runcheck == 0:

            self.runcheck = 1

            #print(self.runcheck)

            self.root.ids.toolBarBottom.icon = "pause"

            self.readSerialStart(1)

 

        else:

            self.runcheck = 0

            #print(self.runcheck)

            self.root.ids.toolBarBottom.icon = "play"

            self.StopSerial()

   

    def StopSerial(self):

        if self.stop == 0:

            Clock.unschedule(self.readSerialStart)

            self.serialConnection.reset_input_buffer()

            self.threadKill = 1

            self.thread.join()

            self.thread = None

 

            while(self.stop == 0):

                if self.thread == None:

                    self.serialConnection.write(b'999999')

                    serial_data = self.serialConnection.readline()

                    serial_data = serial_data.decode()

                    #print(serial_data)

 

                    if "DONE.  Resetting....." in serial_data:

                        #print("here")

                        self.start = 0

                        self.stop = 1

                        self.StopSerial()

                                                               

 

    def readSerialStart(self, dt):

        self.threadKill = None

        if self.thread == None:

            #self.ids.clear_button.disabled = True

 

            self.serialConnection.reset_input_buffer()

 

            if self.start == 0:

                #Put SEISMO In test mode

                while(self.start == 0):

                    self.serialConnection.write(b'\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r')

                    serial_data = self.serialConnection.readline()

                    serial_data = serial_data.decode()

                    #print(serial_data)

 

                    if "Factory Mode Entered" in serial_data:

                        #print("here")

                        self.start = 1

                        self.stop = 0

                   

 

            self.thread = Thread(target=self.backgroundThread, args = (1, ))

            self.thread.daemon=True

            self.thread.start()

            # Set the timer for redrawing the screen

            refresh_time = 0.3

            Clock.schedule_interval(self.readSerialStart, refresh_time)

           

 

    def backgroundThread(self, dt):    # retrieve data

        while(self.threadKill == None):

            time.sleep(.001)                    # delay of 1ms

            serial_data = self.serialConnection.readline()                # read complete line from serial output

            while not '\\n'in str(serial_data):         # check if full data is received.

                # This loop is entered only if serial read value doesn't contain \n

                # which indicates end of a sentence.

                # str(serial_data) - serial_data is byte where string operation to check `\\n`

                # can't be performed

                time.sleep(.001)                # delay of 1ms

                temp = self.serialConnection.readline()           # check for serial output.

                if not not temp.decode():       # if temp is not empty.

                    serial_data = (serial_data.decode()+temp.decode()).encode()

                    # requrired to decode, sum, then encode because

                    # long values might require multiple passes

            serial_data = serial_data.decode()                  # decoding from bytes

            serial_data = serial_data.strip()                   # stripping leading and trailing spaces.

            if self.threadKill == None:

               

                #Debug

                #print(serial_data)

 

                if "SEISMO" in serial_data:

                    # Update a widget property in the main thread by decorating the

                    # called function with @mainthread.

                    self.update_model_label_text(serial_data)

                    #print(serial_data)

 

                elif "Pulse Count = " in serial_data:

                    # Update a widget property in the main thread by decorating the

                    # called function with @mainthread.

                    self.update_pulse_count_label_text(re.sub('\D', '', serial_data))

               

                elif "Detection Mode =" in serial_data:

                   

                    if "Detection Mode = SHOCK ONLY" in serial_data:

                        serial_data = "SHOCK ONLY"

                        # Update a widget property in the main thread by decorating the

                        # called function with @mainthread.

                        self.update_detection_mode_label_text(serial_data)

                   

                    elif "Detection Mode = MOTION ONLY" in serial_data:

                        serial_data = "MOTION ONLY"

                        # Update a widget property in the main thread by decorating the

                        # called function with @mainthread.

                        self.update_detection_mode_label_text(serial_data)

                   

                    elif "Detection Mode = SHOCK AND MOTION" in serial_data:

                        serial_data = "SHOCK AND MOTION"

                        # Update a widget property in the main thread by decorating the

                        # called function with @mainthread.

                        self.update_detection_mode_label_text(serial_data)

                    

                    elif "Detection Mode = SHOCK OR MOTION" in serial_data:

                        serial_data = "SHOCK OR MOTION"

                        # Update a widget property in the main thread by decorating the

                        # called function with @mainthread.

                        self.update_detection_mode_label_text(serial_data)

               

                elif "Sensitivity Pot =" in serial_data:

                    # Update a widget property in the main thread by decorating the

                    # called function with @mainthread.

                    self.update_sensitivity_pot_label_text(re.sub('\D', '', serial_data))

               

                elif "LEDs Enabled =" in serial_data:

                    if "LEDs Enabled = Enabled" in serial_data:

                        serial_data = "Enabled"

                        # Update a widget property in the main thread by decorating the

                        # called function with @mainthread.

                        self.update_leds_enabled_label_text(serial_data)

                   

                    elif "LEDs Enabled = Disabled" in serial_data:

                        serial_data = "Disabled"

                        # Update a widget property in the main thread by decorating the

                        # called function with @mainthread.

                        self.update_leds_enabled_label_text(serial_data)

               

                elif "Latch =" in serial_data:

                    if "Latch = 0" in serial_data:

                        serial_data = "Enabled"

                        # Update a widget property in the main thread by decorating the

                        # called function with @mainthread.

                        self.update_latch_alarm_label_text("Disabled")

                   

                    elif "Latch = 1" in serial_data:

                        serial_data = "Disabled"

                        # Update a widget property in the main thread by decorating the

                        # called function with @mainthread.

                        self.update_latch_alarm_label_text("First to Alarm")

                   

                    elif "Latch = 2" in serial_data:

                        serial_data = "Disabled"

                        # Update a widget property in the main thread by decorating the

                        # called function with @mainthread.

                        self.update_latch_alarm_label_text("Latched")

                               

               

 

    def clear_results(self):

        self.root.ids.model.secondary_text = "-"

        self.root.ids.pulse_count.secondary_text = "-"

        self.root.ids.detection_mode.secondary_text = "-"

        self.root.ids.sensitivity_pot.secondary_text = "-"

        self.root.ids.leds_enabled.secondary_text = "-"

        self.root.ids.latch_alarm.secondary_text = "-"

 

 

    def build(self):

        self.title = "KFS SEISMO Monitor"

        self.theme_cls.theme_style = "Dark"

        self.theme_cls.primary_palette = "DeepPurple"

        Window.bind(on_request_close=self.on_request_close)

        return self.screen

   

    def on_request_close(self, *args):

        self.get_running_app().stop()

        # removing window

        Window.close()

 

 

    @mainthread

    def update_model_label_text(self, new_text):

        #self.ids.model.text = new_text

        self.root.ids.model.secondary_text = new_text

 

    @mainthread

    def update_pulse_count_label_text(self, new_text):

        #self.ids.pulse_count.text = new_text

        self.root.ids.pulse_count.secondary_text = new_text

 

    @mainthread

    def update_detection_mode_label_text(self, new_text):

        #self.ids.detection_mode.text = new_text

        self.root.ids.detection_mode.secondary_text = new_text

 

    @mainthread

    def update_sensitivity_pot_label_text(self, new_text):

        #self.ids.sensitivity_pot.text = new_text

        self.root.ids.sensitivity_pot.secondary_text = new_text

 

    @mainthread

    def update_leds_enabled_label_text(self, new_text):

        #self.ids.leds_enabled.text = new_text

        self.root.ids.leds_enabled.secondary_text = new_text

 

    @mainthread

    def update_latch_alarm_label_text(self, new_text):

        #self.ids.latch_alarm.text = new_text

        self.root.ids.latch_alarm.secondary_text = new_text

 

 

if __name__ == "__main__":

                KFSMonitor().run()

 

.kv

MDBoxLayout:

    orientation:'vertical'

 

    MDToolbar:

        id:toolBarTop

        title:'KFS SEISMO Monitor'

        type:'top'

        pos_hint:{'top':1}

        right_action_items : [["usb-port", lambda x: app.menu.open()],["eraser", lambda x: app.clear_results()]]

 

    #MDLabel:

    #    text:"Some stuff"

    #    halign:"center"

 

    ScrollView:

        MDList:

            TwoLineAvatarListItem:

                id: model

                text: "SEISMO Type"

                secondary_text: "-"

 

                IconLeftWidget:

                    icon: "alpha-m-box"

 

            TwoLineAvatarListItem:

                id: pulse_count

                text: "Pulse Count"

                secondary_text: "-"

 

                IconLeftWidget:

                    icon: "pulse"

           

            TwoLineAvatarListItem:

                id: detection_mode

                text: "Detection Mode"

                secondary_text: "-"

 

                IconLeftWidget:

                    icon: "alarm-light"

           

            TwoLineAvatarListItem:

                id: sensitivity_pot

                text: "Sensitivity Pot"

                secondary_text: "-"

 

                IconLeftWidget:

                    icon: "contrast"

           

            TwoLineAvatarListItem:

                id: leds_enabled

                text: "LEDs Enabled"

                secondary_text: "-"

 

                IconLeftWidget:

                    icon: "led-variant-on"

 

            TwoLineAvatarListItem:

                id: latch_alarm

                text: "Latch Alarm"

                secondary_text: "-"

 

                IconLeftWidget:

                    icon: "download-lock"

   

    MDBottomAppBar:

        disabled: True

        id:mainToolBarBottom

        MDToolbar:

            id:toolBarBottom

            icon: 'play'

            type:'bottom'

            mode:'end'

            #mode:'center'

            title:''

            left_action_items: [["lan-connect"]]

            on_action_button: app.runchecker()



error = 

Traceback (most recent call last):
   File "main.py", line 315, in <module>
     KFSMonitor().run()
   File "C:\KivyGui\kivy_venv\lib\site-packages\kivy\app.py", line 955, in run
     runTouchApp()
   File "C:\KivyGui\kivy_venv\lib\site-packages\kivy\base.py", line 574, in runTouchApp
     EventLoop.mainloop()
   File "C:\KivyGui\kivy_venv\lib\site-packages\kivy\base.py", line 341, in mainloop
     self.window.mainloop()
   File "C:\KivyGui\kivy_venv\lib\site-packages\kivy\core\window\window_sdl2.py", line 569, in mainloop
     if self.dispatch('on_request_close'):
   File "kivy\_event.pyx", line 727, in kivy._event.EventDispatcher.dispatch
   File "kivy\_event.pyx", line 1307, in kivy._event.EventObservers.dispatch
   File "kivy\_event.pyx", line 1231, in kivy._event.EventObservers._dispatch
   File "main.py", line 278, in on_request_close
     self.get_running_app().stop()
 TypeError: 'int' object is not callable

 

 

                                

Gibbsy FPV

unread,
May 10, 2022, 9:34:50 AM5/10/22
to Kivy users support
Update Issue Fixed it turned out to be  self.stop = 1 it didn't like it so changed all references of it to self,stopComs close perfectly now.

Thanks
Andrew

Elliot Garbus

unread,
May 10, 2022, 10:08:38 AM5/10/22
to kivy-...@googlegroups.com

Glad to hear you solved it!

 

Here is a tip:

Under the class App or MDApp, self is the running app so:

        self.get_running_app().stop()

is the same as:

        self.stop()

 

def on_request_close(self, *args):

        self.get_running_app().stop()

        # removing window

        Window.close()

 

Additionally, if you are not doing an processing in on_request_close, you do not need to include it.  Kivy will close the window and call stop for you with the window close button is pressed.  I often use on_request_close to save window coordinates to a config file or remind a user to save prior to closing.  Here is an example that saves the window size and position:   https://github.com/ElliotGarbus/KivyWindowSize

--
You received this message because you are subscribed to the Google Groups "Kivy users support" group.
To unsubscribe from this group and stop receiving emails from it, send an email to kivy-users+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/kivy-users/bcd85c5f-85b1-4981-a596-1657499a4905n%40googlegroups.com.

 

Gibbsy FPV

unread,
May 10, 2022, 10:22:15 AM5/10/22
to Kivy users support
Hi  ElliotG

Thanks for the reply.

I am going to add some processing in to stop the serial monitor just in case it has not been stopped by the user. plus would be nice to had a pop up dialog to confirm close which is what i will work on tonight. :-)

Thanks
Andrew
Reply all
Reply to author
Forward
0 new messages