In the code below, after I click the button for the first time, the spinner (MDSpinner
) will start to spin and the logging.info()
will be directed to the MDLabel
. But after I click the button the second time, there will be two copies of logging.info()
directed to MDLabel
. How can I stop the thread after the task my_thread
is finished? Thanks in advance.
import kivy
from kivy.lang import Builder
import logging
import threading
from kivy.uix.floatlayout import FloatLayout
from kivymd.app import MDApp
from kivy.clock import Clock
import time
root_kv = '''
BoxLayout:
id: main
orientation: "vertical"
spacing: "20dp"
padding: "50dp", "10dp", "50dp", "10dp"
MDRaisedButton:
id: button4read_mtx
size_hint: None, None
size: 4 * dp(48), dp(48)
text: "Start"
opposite_colors: True
pos_hint: {"center_x": 0.5, "center_y": 0.5}
on_release: app.analyze_data()
MDSpinner:
id: spinder4button
size_hint: None, None
size: dp(46), dp(46)
pos_hint: {'center_x': .5, 'center_y': .5}
active: False
MDLabel:
id: test_label_log
text: "Log"
halign: "left"
'''
exit_flag = 0
def my_thread(log, app):
app.root.ids.spinder4button.active = True
for i in range(5):
time.sleep(1)
log.info("Step %s", i)
app.root.ids.spinder4button.active = False
class MDLabelHandler(logging.Handler):
def __init__(self, label, level=logging.NOTSET):
logging.Handler.__init__(self, level=level)
self.label = label
def emit(self, record):
"using the Clock module for thread safety with kivy's main loop"
def f(dt=None):
self.label.text += "\n" + self.format(record) #"use += to append..."
Clock.schedule_once(f)
class MyApp(MDApp):
def __init__(self, **kwargs):
super().__init__(**kwargs)
def build(self):
root = Builder.load_string(root_kv)
return root
def analyze_data(self):
log = logging.getLogger("my.logger")
log.level = logging.DEBUG
log.addHandler(MDLabelHandler(self.root.ids.test_label_log, logging.DEBUG))
#thread_info = _thread.start_new(my_thread, (log, self, ))
#print(thread_info)
x = threading.Thread(target=my_thread, args=(log, self, ))
x.start()
if __name__ == '__main__':
MyApp().run()
The thread is terminating, as it should, when the task completes. That is not the problem. The problem is that you are adding a new logging handler each time you run analyze_data()
. The fix is to only add one handler:
class MyApp(MDApp):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.log = None
def build(self):
root = Builder.load_string(root_kv)
return root
def analyze_data(self):
if self.log is None:
self.log = logging.getLogger("my.logger")
self.log.level = logging.DEBUG
self.log.addHandler(MDLabelHandler(self.root.ids.test_label_log, logging.DEBUG))
#thread_info = _thread.start_new(my_thread, (log, self, ))
#print(thread_info)
x = threading.Thread(target=my_thread, args=(self.log, self, ))
x.start()