How to close the thread?
#1
Hi all,

I need some help regarding how to kill or terminate the thread. I can be able to open the thread when I hit the enter button of the keyboard, but I can't be able to find out how to kill or terminate the thread when I hit the backspace button.

Here is the code:

Code:
import urllib2
import StringIO
import threading

ACTION_ENTER = 7
ACTION_BACKSPACE = 110


def allchannels_timer(self):
   for i in range(1):
        time.sleep(0.3)
        self.getControl(4202).setLabel("0%")
        #DOWNLOAD THE XML SOURCE HERE
        url = ADDON.getSetting('allchannel.url')
        req = urllib2.Request(url)
        response = urllib2.urlopen(req)
        data = response.read()
        response.close()


if action == ACTION_BACKSPACE:
     if img1_yellow == False:
       self.getControl(4202).setLabel("")
       #cancel the connection and close the database

if action == ACTION_ENTER:
     if img1_yellow == True:
         self.thread = threading.Thread(target=self.allchannels_timer)
         self.thread.setDaemon(True)
         self.thread.start()


Do you know how I can kill or terminate the thread when I hit the backspace button?

What method do I need to use to kill or terminate the thread?

I have try to find the answer on google, but unfortunately I can't find the answer. Sad

Any advice would be much appreciated.
Reply
#2
Do you have a link to the complete code?
In general, from within the loop in your thread, you need to check if a flag has been set and then exit from the loop if that is detected.
pseudocode:
Code:
import threading
__killthread__ = False

def myloop (self):
    while __killthread__ is False:
         do useful stuff here
    call database_close
...

if action == ACTION_ENTER:
    self.thread = threading.Thread(target=self.myloop)
    self.thread.setDaemon(True)
    self.thread.start()    

if action == ACTION_BACKSPACE:
    __killthread__ = True
    self.thread.join(0.5)
Reply
#3
Yes I do, here is the complete code:

Code:
import urllib2
import StringIO
import sqlite3
import threading
from sqlite3 import dbapi2 as database
from xml.etree import ElementTree
import xml.etree.ElementTree as ET

#get actioncodes from keyboard.xml
ACTION_ENTER = 7
ACTION_BACKSPACE = 110

def cSetVisible(WiNdOw,iD,V=True): WiNdOw.getControl(iD).setVisible(V)

class MyClass(xbmcgui.WindowXML):
     def timer1_8percent(self):
         for i in range(1):
             time.sleep(1)
             self.getControl(4202).setLabel("8%")


     def timer1_12percent(self):
         for i in range(1):
             time.sleep(2)
             self.getControl(4202).setLabel("12%")


     def timer1_18percent(self):
         for i in range(1):
             time.sleep(3)
             self.getControl(4202).setLabel("18%")


     def allchannels_timer(self):
         for i in range(1):
             time.sleep(0.3)
             self.getControl(4202).setLabel("0%")

             #DOWNLOAD THE XML SOURCE HERE
             url = ADDON.getSetting('allchannel.url')
             req = urllib2.Request(url)
             response = urllib2.urlopen(req)
             data = response.read()
             response.close()
             profilePath = xbmc.translatePath(os.path.join('special://userdata/addon_data/script.tvguide', ''))
             self.getControl(4202).setLabel("1%")
             self.thread = threading.Thread(target=self.timer1_8percent)
             self.thread.setDaemon(True)
             self.thread.start()
             self.thread = threading.Thread(target=self.timer1_12percent)
             self.thread.setDaemon(True)
             self.thread.start()
             self.thread = threading.Thread(target=self.timer1_18percent)
             self.thread.setDaemon(True)
             self.thread.start()


             if os.path.exists(profilePath):
                 profilePath = profilePath + 'source.db'
                 con = database.connect(profilePath)
                 cur = con.cursor()
                 cur.execute('CREATE TABLE programs(channel TEXT, title TEXT, start_date TIMESTAMP, stop_date TIMESTAMP, description TEXT)')
                 con.commit()
                 con.close
                 tv_elem = ElementTree.parse(StringIO.StringIO(data)).getroot()
                 profilePath = xbmc.translatePath(os.path.join('special://userdata/addon_data/script.tvguide', ''))
                 profilePath = profilePath + 'source.db'
                 con = sqlite3.connect(profilePath)
                 cur = con.cursor()
                 channels = OrderedDict()

                 # Get the loaded data
                 for channel in tv_elem.findall('channel'):
                     channel_name = channel.find('display-name').text
                     for program in channel.findall('programme'):
                         title = program.find('title').text
                         start_time = program.get("start")
                         stop_time = program.get("stop")
                         cur.execute("INSERT INTO programs(channel, title, start_date, stop_date)" + " VALUES(?, ?, ?, ?)", [channel_name, title, start_time, stop_time])
                         con.commit()
                         con.close

                 print 'Channels store into database are now successfully!'
                 program = None
                 now = datetime.datetime.now()
                 #strCh = '(\'' + '\',\''.join(channelMap.keys()) + '\')'
                 cur.execute('SELECT channel, title, start_date, stop_date FROM programs WHERE channel')
                 getprogram_info = cur.fetchall()

                 for row in getprogram_info:
                     programming = row[0], row[1], row[2], row[3]
                     print programming
                     #print row[0], row[1], row[2], row[3]
                     #programming = row[0], row[1], row[2], row[3]
                     #programming = row[0], row[1], row[2], row[3]
                     #cur.close()


def onAction(self, action):
   img1_yellow = xbmc.getCondVisibility('Control.IsVisible(3)')

   if action == ACTION_BACKSPACE:
     if img1_yellow:
       cSetVisible(self,3,True)
       self.getControl(4202).setLabel("")
       #cancel the connection and close the database


  if action == ACTION_ENTER:
     if img1_yellow:
         cSetVisible(self,3,False)
         self.thread = threading.Thread(target=self.allchannels_timer)
         self.thread.setDaemon(True)
         self.thread.start()
Reply
#4
I was going to stick some stuff in your code to help, but now I'm confused as to why you are using so many threads for something that seems to be a sequential set of operations. Your spawning a bunch of threads just to bump the percentage information on some kind of status I think, but the percentage just seems to be an arbitrary amount of time unrelated to the actual actions happening below.

You might want to take a look at the new progress dialog available in Gotham:

http://romanvm.github.io/xbmcstubs/docs/...gress.html

Or maybe I just completely don't get what you're trying to do.
Reply
#5
I agree with pkscout. If the main goal of providing a progress meter is due to the actual download of info, an example of using the xbmcgui.DialogProgress() control to monitor a download can be found in lines 83-138 here: https://github.com/AmbiBox/AmbiBox-XBMC/...loadbox.py.

Here I am writing to a file, but this could be modified to accumulate data in your variable 'data'.

You can monitor the state of a global variable during the loop and gracefully kill the download if the user presses the backspace key which then alters the state of the global variable during the download. Or just use the isCanceled() feature of the dialog. Or better yet have either event call the same code to gracefully stop.

You could then close that progress dialog after the download and create another that updates intermittently during your database functions.

It might make more sense to run the I/O in a separate thread if you were doing something else while it was downloading, but here you cannot start the database functions until the whole file has arrived. But if you could find a way to begin processing as the data was arriving - then that might be a good case for running two threads.

One other small thing to note is that you may want to consider using xbmc.sleep() instead of time.sleep() when you need a delay in your code. This plays better with the XBMC ecosystem especially in the main thread. It probably doesn't matter much in your timer1_xx threads.
Reply
#6
@KenV99 I understand what you have said, but I don't like to use progress dialog because I only want to use the label to count the percent how far I am going through when downloading the xml file.

However, I can see the _killthread_ variable which is working.

Do you know how I can block the database from writing when I shutdown the thread and do you know how I can delete the files called "source.db" and "source.db-journal"?

When I try to delete the files, it will not delete the files and it will continue to write the data into the database until it get finish.

Code:
if action == ACTION_BACKSPACE:
   __killthread__ = True
   self.thread.join(0.0)
   profilePath = xbmc.translatePath(os.path.join('special://userdata/addon_data/script.tvguide', ''))
   database_source = profilePath + 'source.db'
   write_source = profilePath + 'source.db-journal'

   if os.path.exists(database_source):
      os.path.remove(write_source)
      os.path.remove(database_source)
Reply
#7
If you don't need the data to persist between sessions, you can use :memory:. See here: https://docs.python.org/2/library/sqlite3.html

In your inner for loops for the database code, also check the value of __killthread__ and 'return' if it's true.

If you want to abort during the download, you'll need to download to a buffer as done in the link to github that I provided and again check the value of __killthread__ in between blocks of data.
Reply
#8
Thank you very much for that. I'm not sure how to check the value of __killthread__ and return if it's true.

I would like you to have a look at the update code to see if I have done it right or wrong.

Here is the update code:

Code:
__killthread__ = False

def allchannels_timer(self):
         while __killthread__ is False:
             time.sleep(0.3)
             self.getControl(4202).setLabel("0%")
             #DOWNLOAD THE XML SOURCE HERE
             url = ADDON.getSetting('allchannel.url')
             req = urllib2.Request(url)
             response = urllib2.urlopen(req)
             data = response.read()
             response.close()
             profilePath = xbmc.translatePath(os.path.join('special://userdata/addon_data/script.tvguide', ''))
             self.getControl(4202).setLabel("1%")
             self.thread = threading.Thread(target=self.timer1_8percent)
             self.thread.setDaemon(True)
             self.thread.start()
             self.thread = threading.Thread(target=self.timer1_12percent)
             self.thread.setDaemon(True)
             self.thread.start()
             self.thread = threading.Thread(target=self.timer1_18percent)
             self.thread.setDaemon(True)
             self.thread.start()
             self.thread = threading.Thread(target=self.timer1_24percent)
             self.thread.setDaemon(True)
             self.thread.start()
             self.thread = threading.Thread(target=self.timer1_28percent)
             self.thread.setDaemon(True)
             self.thread.start()
             self.thread = threading.Thread(target=self.timer1_32percent)
             self.thread.setDaemon(True)
             self.thread.start()


             if os.path.exists(profilePath):
                 profilePath = profilePath + 'source.db'
                 con = database.connect(profilePath)
                 cur = con.cursor()
                 cur.execute('CREATE TABLE programs(channel TEXT, title TEXT, start_date TIMESTAMP, stop_date TIMESTAMP, description TEXT)')
                 con.commit()
                 con.close
                 tv_elem = ElementTree.parse(StringIO.StringIO(data)).getroot()
                 profilePath = xbmc.translatePath(os.path.join('special://userdata/addon_data/script.tvguide', ''))
                 profilePath = profilePath + 'source.db'
                 con = sqlite3.connect(profilePath)
                 cur = con.cursor()
                 channels = OrderedDict()
                 self.thread = threading.Thread(target=self.timer1_38percent)
                 self.thread.setDaemon(True)
                 self.thread.start()
                 self.thread = threading.Thread(target=self.timer1_42percent)
                 self.thread.setDaemon(True)
                 self.thread.start()
                 self.thread = threading.Thread(target=self.timer1_48percent)
                 self.thread.setDaemon(True)
                 self.thread.start()
                 self.thread = threading.Thread(target=self.timer1_56percent)
                 self.thread.setDaemon(True)
                 self.thread.start()
                 self.thread = threading.Thread(target=self.timer1_64percent)
                 self.thread.setDaemon(True)
                 self.thread.start()
                 self.thread = threading.Thread(target=self.timer1_72percent)
                 self.thread.setDaemon(True)
                 self.thread.start()
                 self.thread = threading.Thread(target=self.timer1_78percent)
                 self.thread.setDaemon(True)
                 self.thread.start()
                 self.thread = threading.Thread(target=self.timer1_86percent)
                 self.thread.setDaemon(True)
                 self.thread.start()
                 self.thread = threading.Thread(target=self.timer1_92percent)
                 self.thread.setDaemon(True)
                 self.thread.start()
                 self.thread = threading.Thread(target=self.timer1_96percent)
                 self.thread.setDaemon(True)
                 self.thread.start()
                 self.thread = threading.Thread(target=self.timer1_98percent)
                 self.thread.setDaemon(True)
                 self.thread.start()
                 self.thread = threading.Thread(target=self.timer1_100percent)
                 self.thread.setDaemon(True)
                 self.thread.start()


                 # Get the loaded data
                 for channel in tv_elem.findall('channel'):
                     channel_name = channel.find('display-name').text
                     for program in channel.findall('programme'):
                         title = program.find('title').text
                         start_time = program.get("start")
                         stop_time = program.get("stop")
                         cur.execute("INSERT INTO programs(channel, title, start_date, stop_date)" + " VALUES(?, ?, ?, ?)", [channel_name, title, start_time, stop_time])
                         con.commit()
                         con.close

                 print 'Channels store into database are now successfully!'
                 program = None
                 now = datetime.datetime.now()
                 #strCh = '(\'' + '\',\''.join(channelMap.keys()) + '\')'
                 cur.execute('SELECT channel, title, start_date, stop_date FROM programs WHERE channel')
                 getprogram_info = cur.fetchall()

                 for row in getprogram_info:
                     programming = row[0], row[1], row[2], row[3]
                     print programming
                     #print row[0], row[1], row[2], row[3]
                     #programming = row[0], row[1], row[2], row[3]
                     #programming = row[0], row[1], row[2], row[3]
                     #cur.close()


if action == ACTION_BACKSPACE:
   __killthread__ = True
   self.thread.join(0.0)
   profilePath = xbmc.translatePath(os.path.join('special://userdata/addon_data/script.tvguide', ''))
   database_source = profilePath + 'source.db'
   write_source = profilePath + 'source.db-journal'
   os.remove(write_source)
   os.remove(database_source)
Reply
#9
As the amount of code gets longer, it's better if you either put it on github or at lease use pastebin.com and post the link here. Fragments are difficult to use anyway because I can't copy/paste into my IDE and have a working copy, nor are there any line numbers so that I can refer to a specific part of your code. And the forum mods frown upon posts filled with code.
Reply
#10
Oh ok I no problem.

Here it is:

http://pastebin.com/1K0HZJX5
Reply
#11
You don't need use a while loop at 247 since that code is only executed once. If you want to check there just do:
Code:
if __killthread__:
    return

You would also place that same fragment inside the inner loop at 333.

I really think you are going about the whole progress dialog the wrong way. There really should be only one thread that does that. Having 18 different timers on different threads all to artificially update a progress bar based on time is simply not what a progress bar in a UI should do. It really should represent the percent of progress in an overall process. Looking at the code, it seems that you think that downloading should represent 38% of the total process. Based on the percent of the file downloaded, the progress bar should go from 0 to 38. Then your database activity takes the remaining 62%. For some stuff outside the loop, you might have to guess. So say from 347-326 takes another 10 percent. Just let it jump to that there. Then calculate the channels times programs, and count in the loop and have the progress go from 48-100 based on what percent through the loop you are. It's probably more than sufficient to update it just from the outside loop.

And unless the amount of data is too big to hold in memory all at once and.or you need persistence, you can avoid the whole file issue with:

con = database.connect(':memory:')

BTW the last four lines are not properly indented and are outside the onAction() function
Reply
#12
Can you please tell me which code of the loops you are talking about especially this:

Quote:You don't need use a while loop at 247 since that code is only executed once

And which line of the inner loops that I need to input this code?

Code:
if __killthread__:
    return

Do I need to create only one thread for the timer instead of 18 threads?
Reply
#13
(2014-07-04, 21:54)coolguy145 Wrote: Can you please tell me which code of the loops you are talking about especially this:

Quote:You don't need use a while loop at 247 since that code is only executed once

And which line of the inner loops that I need to input this code?

Code:
if __killthread__:
    return

Look at the line numbers I listed in pastebin!

(2014-07-04, 21:54)coolguy145 Wrote: Do I need to create only one thread for the timer instead of 18 threads?

Yes and keep track of the elapsed time and intermittently update the label, if you persist in wanting to do it based on time. Then when you hit the database code, kill the timer (again using a flag variable that you keep checking in a loop in the timer) and restart it at 38% or whatever. This would be a major rewrite of your timer code and I can't do that for you, at least at the moment. Seriously, open a free account on Github and put all of the project files there (including your windowXML file) and if I get time in the next few days, I'll try to help more and you'll learn more because you can see more clearly how I am changing things...

Go COLOMBIA #FIFA #WORLD CUP
Reply
#14
Ok, I found this line at 247:

Code:
while __killthread__ is False:

So what I need to do with that line?

As you mentioned in your 2nd post that I need to put the while __killthread__ loop in the allchannels_timers thread, so now you said I don't need to use a while loop at line 247 since that code is only executed once. I'm confused?

Do I need to put this code at line 333 in the inner loop like this?

Code:
for channel in tv_elem.findall('channel'):
channel_name = channel.find('display-name').text
for program in channel.findall('programme'):
   if __killthread__:
      return
   title = program.find('title').text
   start_time = program.get("start")
   stop_time = program.get("stop")
   cur.execute("INSERT INTO programs(channel, title, start_date, stop_date)" + " VALUES(?, ?, ?, ?)", [channel_name, title, start_time, stop_time])
   con.commit()
   con.close


You did not say which line I need to input this code in?

Code:
con = database.connect(':memory:')


By the way I have tried to create the timer in one thread, but in the label it will change the wrong strings in per second, example: 8%, 12%, 18%, 24%. When I have the string of 24%, it will change it back to 8%. Make sense?
Reply
#15
(2014-07-04, 22:23)coolguy145 Wrote: Ok, I found this line at 247:

Code:
while __killthread__ is False:

So what I need to do with that line?

As you mentioned in your 2nd post that I need to put the while __killthread__ loop in the allchannels_timers thread, so now you said I don't need to use a while loop at line 247 since that code is only executed once. I'm confused?

You need it IN THE LOOP. That code only gets executed once. So it's not in a loop. If you want to check there, then replace that code with the code I suggested.

(2014-07-04, 22:23)coolguy145 Wrote: Do I need to put this code at line 333 in the inner loop like this?

Code:
for channel in tv_elem.findall('channel'):
channel_name = channel.find('display-name').text
for program in channel.findall('programme'):
   if __killthread__:
      return
   title = program.find('title').text
   start_time = program.get("start")
   stop_time = program.get("stop")
   cur.execute("INSERT INTO programs(channel, title, start_date, stop_date)" + " VALUES(?, ?, ?, ?)", [channel_name, title, start_time, stop_time])
   con.commit()
   con.close

Yes

(2014-07-04, 22:23)coolguy145 Wrote: You did not say which line I need to input this code in?

Code:
con = database.connect(':memory:')

287 and 288 repeat 279 and 280. So delete 287 and 288.
Delete 284 and replace 280 with the above

(2014-07-04, 22:23)coolguy145 Wrote: By the way I have tried to create the timer in one thread, but in the label it will change the wrong strings in per second, example: 8%, 12%, 18%, 24%. When I have the string of 24%, it will change it back to 8%. Make sense?

The string should be derived from the elapsed time. You need to sort out how to code it.
Please just put all of your stuff on Github and I'll look at it later or tomorrow!
Reply

Logout Mark Read Team Forum Stats Members Help
How to close the thread?0