Moritz
Moritz

Reputation: 5408

class instance as process within kivy app: kivy app gets stuck

I do have an interface to store settings and to start a process. However, I cannot close everything once the process starts because kivy gets stuck after calling process.run. Here is a minimal example:

#! /usr/bin/env python

"""
Activate the touch keyboard. It is important that this part is on top
because the global config should be initiated first.
"""
from kivy.config import Config
Config.set('kivy', 'keyboard_mode', 'multi')

# the main app
from kivy.app import App

# The Builder is used to define the main interface.
from kivy.lang import Builder

# The start screen is defined as BoxLayout
from kivy.uix.boxlayout import BoxLayout

# The pHBot class
from phbot import pHBot

# The pHBot is defined as process. Otherwise it would not be possible to use the close button.
from multiprocessing import Process, Queue



# Definition of the Main interface
Builder.load_string('''
<Interface>:
    orientation: 'vertical'
    Button:
        text: 'Run pH-Bot'
        font_size: 40
        on_release: app.run_worker()
    Button:
        text: 'Close pH-Bot'
        font_size: 40
        on_release: app.stop_phbot()
''')


# Interface as a subclass of BoxLayout without any further changes. This part is used by kivy.
class Interface(BoxLayout):
    pass


class SettingsApp(App):
    """
    The settings App is the main app of the pHBot application. It is initiated by kivy and contains the functions
    defining the main interface.
    """

    def build(self):
        """
        This function initializes the app interface and has to be called "build(self)". It returns the user interface
        defined by the Builder.
        """

        # A queque for the control all processes.
        self.qu_stop = Queue()

        # returns the user interface defined by the Builder
        return Interface()

    def run_worker(self):
        """
        The pHBot application is started as a second process.
        """
        bot = pHBot(self.qu_stop)
        phbot = Process(target=bot.ph_control())
        # start the process
        phbot.run()

    def stop_phbot(self):
        self.qu_stop.put('STOP')

if __name__ == '__main__':
    SettingsApp().run()

The second class is within a file called phbot.py:

import time


class pHBot:

    def __init__(self,  qu_stop_in):
        self.qu_stop_in = qu_stop_in

    def ph_control(self):

        while True:
            if self.qu_stop_in.full():
                if self.qu_stop_in.get() == 'STOP':
                    break
            print('Back and forth forever ...')
            time.sleep(2)

What am I missing here ?

Upvotes: 0

Views: 154

Answers (2)

zeeMonkeez
zeeMonkeez

Reputation: 5157

Note that a Process is started with start(). Calling run() really immediately launches the worker from the same process, and thus it is blocking. The relevant lines in run_worker should therefore be:

    bot = pHBot(self.qu_stop)
    phbot = Process(target=bot.ph_control)
    # start the process
    phbot.start()

In addition, in your worker, don't check whether the Queue is full. Rather, do a non-blocking get and handle the Queue.Empty exception:

import Queue
...
    def ph_control(self):

        while True:
            try:
                item = self.qu_stop_in.get(False)
                if item == 'STOP':
                    break
            except Queue.Empty:
                print "Nothing to see"
            print('Back and forth forever ...')
            time.sleep(2)

Upvotes: 1

inclement
inclement

Reputation: 29450

phbot = Process(target=bot.ph_control())

You are calling bot.ph_control - that's what the () syntax does. I'd guess you need to pass the function itself (Process(target=bot.ph_control))), but I'm not familiar with multiprocessing.

Upvotes: 0

Related Questions