With a lot of help from ihatehippies I got threads working yesterday. Now I need to stop 'em! More specifically I want to stop a second thread starting until the first has finished.. This is what I have in my threading class at the moment:

class ProcessThread(Thread):
    """Test Worker Thread Class."""

    #----------------------------------------------------------------------
    def __init__(self, args):
        self.args = args
        """Init Worker Thread Class."""
        Thread.__init__(self)
        self.start()    # start the thread
    #----------------------------------------------------------------------
    def run(self):
        os.system(self.args)
        wx.CallAfter(Publisher().sendMessage, "update", "Processing completed")

Any thoughts most welcome. Thanks.

Recommended Answers

All 25 Replies

To tell if a thread has finished it's assigned processing use

thread1 = ProcessThread(xyz)
#
# Then either from the main thread or from inside your second
# thread run thread.isAlive() to check if the thread is still
# running. The same principle as passing the arguments to the
# first thread applies here. You can pass the first thread to 
# the second thread and have the second thread continually poll 
# the first thread to determine when it should start.
thread2 = SecondThreadClass(thread1)

# the second thread's run method could look like:

   def run(self):
      from time import sleep
      # waits for thread1 to finish
      while self.thread1.isAlive():
         sleep(.1)
      # add your code here

Although probably the smarter way would be to have thread1 spawn thread2
or have the mainframe spawn thread two. Is there some reason you need thread2
started before thread1 finishes or why thread1 can't just do thread2's work?

Hello again.

I think I will have to re-think the whole script...
How it works is one method copies all the files from a DVD or whatever and processes them in a certain way. Using a loop. Then another method processes the finished files in a different way. Than another method does more work on the files from the previous method.

It is important that everything is done serially. Before I used threads this was fine but now as soon as the thread starts the next method tries to begin work and of course the files aren't ready.

Once the main prog pushes a job out to a thread it believes it is done with. Also each iteration of my loop starts a new thread and the pc resources get swallowed up! It is a mess. And all to stop the GUI looking like its crashed!

Seems like it can be done in one thread...

def run(self):
      method_a()
      method_b()
      method_c()

If you are interested in managing threads you should read about semaphores. I found that a good introduction was a book called "The little book of semaphores" by Allen. B Downey. It's a free book, I've posted a link below:

http://greenteapress.com/semaphores/

Seems like it can be done in one thread...

def run(self):
      method_a()
      method_b()
      method_c()

OK, I like this idea. Now if my methods (a,b&c) are inside another class how do I run them from the ProcessThread class? As a reminder this is the ProcessThread:

class ProcessThread(Thread):
    """Test Worker Thread Class."""

    #----------------------------------------------------------------------
    def __init__(self, args):
        self.args = args
        """Init Worker Thread Class."""
        Thread.__init__(self)
        self.start()    # start the thread
    #----------------------------------------------------------------------
    def run(self):
        os.system(self.args)
        wx.CallAfter(Publisher().sendMessage, "update", "Processing completed")

And my method is here:

class Mymain(model.Background):
    def avs_to_avc(self):

I haven't grasped classes properly yet. So, what I want to do is to call "avs_to_avc" in place of "os.system(self,args)".
The os.system bit is now tagged on to the end of the avs_to_avc method...

If the Mymain class instance is globally available you can reference the method like

instance = Mymain()
#
#...
def run(self):
   instance.avs_to_avc()

or you can pass the Mymain instance to the thread like you did with 'args' and save it to the thread in the __init__ method.

def __init__(self, instance):
   # saving the instance argument to the thread
   self.instance = instance
#
#...
def run(self):
   # referencing the saved instance
   self.instance.avs_to_avc()

In the first example I get Mymail is not defined. In the second its: TypeError: unbound method avs_to_avc() must be called with Mymain instance as first argument (got nothing instead).

For the first one I'm assuming you meant Mymain not Mymail. You should probably read up on python classes. The second error is telling you that you didn't create an instance of the class. You just passed the class itself not an instance of that class. When you define a class you need to create an instance of that class to use it.

# this is defining a class
class aClass(object): pass
# this is creating an instance of the class you just defined
instance_of_aClass = aClass()
# call the methods from the instance
# not from the class

if want to use class methods without having to create an instance you can use the staticmethod decorator

class aClass(object):
   @staticmethod
   def aMethod():
      # notice there is no 'self' here
      # the staticmethod does not pass
      # the class instance as the first 
      # argument like normal class methods
      print 'a static method'

# this class method doesn't require an instance
# it can be used like
aClasss.aMethod()

all other class methods require you to create an instance like in the example I posted in my previous post.

On second thought. Is there any reason your methods have to be methods and can't be functions. Why do they need to be inside a class? Are you trying not to pollute the namespace?

OK If I use your simple code for creating a class and an instance:

class aClass(object): pass
instance_of_aClass = aClass()

No problem at all. If I do the same with my class:

class Mymain(model.Background):
  instance=Mymain()

I get: instance = Mymain()
NameError: name 'Mymain' is not defined

This is making my brain hurt.

You can not use it recursively like that, that would be infinite recursion.

Tony, it is very good of you to try to help but your answers/suggestions are way over the head of an absolute beginner...

I believe Tony means that if you have a class which in it's initialisation creates an instance of itself then it will create an infinite number of itself.

On second thought. Is there any reason your methods have to be methods and can't be functions. Why do they need to be inside a class? Are you trying not to pollute the namespace?

Sorry I missed this reply earlier...

I am only using classes because my GUI is made with Pythoncard and it automatically generated the Mymain class. I don't know how to make the GUI work without it. I'm getting on fairly well with functions and would much rather leave classes for the time being...

Ah, OK. Now I see why pass was there.
This:

class Mymain(model.Background):pass
instance_of_Mymain = Mymain()

produces this:

instance_of_Mymain = Mymain()
TypeError: __init__() takes exactly 3 arguments (1 given)

that code you posted won't produce that. The __init__ method you made for Mymain takes 3 arguments

def __init__(self, something, somethingelse):

The only place in the whole script where def __init__ appears is in the ProcessThread class and I'm not calling that at the moment, in fact it is commented out.

Wherever in the script I put:

instance_of_Mymain = Mymain()

I get this response:

instance_of_Mymain = Mymain()
TypeError: __init__() takes exactly 3 arguments (1 given)

Mymain is defined thus:

class Mymain(model.Background):

Your class inherits init method from parent class if it does not override it with its' own. Try and print help(instance_of_MyMain)

What is this parent class? I have only one class in the script now.
Could it be something defined by PythonCard that I cannot see?

model.Background is the class that it inherits. I've never worked with pythoncard so I can't tell you how you are supposed instantiate the classes but it takes an additional 2 arguments that you need to pass to it when you create it. If you look at the source code for the "Background" class you can see what arguments are required for it or look online for other pythoncard examples or use staticmethods.

model.Background is the class that it inherits. I've never worked with pythoncard so I can't tell you how you are supposed instantiate the classes but it takes an additional 2 arguments that you need to pass to it when you create it. If you look at the source code for the "Background" class you can see what arguments are required for it or look online for other pythoncard examples or use staticmethods.

Thanks for the help.
Much as I like the "what you see is what you get" elements of Pythoncard I think I will have a go at making my GUI with wxpython, as that seems more widely used and understood...

model.Background is the class that it inherits. I've never worked with pythoncard so I can't tell you how you are supposed instantiate the classes but it takes an additional 2 arguments that you need to pass to it when you create it. If you look at the source code for the "Background" class you can see what arguments are required for it or look online for other pythoncard examples or use staticmethods.

I found the info about Pythoncard background. Perhaps you could have a quick look? It may be enlightening! Thanks.

http://pythoncard.sourcearchive.com/documentation/0.8.2/model_8py-source.html#l00616

I read it a bit. It takes 2 arguments besides 'self'

00621     def __init__(self, aParent, aBgRsrc):

It's hard to diagnose your issue without actually seeing the code but from my limited vantage I wouldn't rework the pythoncard code, if you have the gui working leave it that way. I'm fairly certain the actual processing methods/functions don't have to be included in the gui class but rather as stand alone functions or in a class created before the pythoncard class is created. You can reference other classes or modules from inside the pythoncard class.

class processing(object):
   def do_work(self):
      print 'working'

process_instance = processing()

class YourPythonCardClass(model.whatever):
   def a_method(self):
      # this is the operative line
      process_instance.do_work()

That works fine, I have no problem calling methods outside the gui class from within it. But all attempts to call methods within the gui class from outside fail. This is necessary to update the status bar of the gui. Giving up.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.