User avatar
NPDedyukhin
Posts: 25
Joined: Fri Sep 20, 2019 3:23 am
Location: RU

Parent script termination

Sun Dec 01, 2019 2:19 pm

Good day!

I have a parent script (it iterates over the list items when I press the arrows on the keyboard, and if it reaches the last item, it starts over, and when you press Enter, the following script is launched.):

Code: Select all

lines = ["A","B","C","D","E","F"]

import pygame
pygame.quit()
pygame.init()
screen = pygame.display.set_mode((400,400))

done = False
while not done:
    
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            done = True
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_LEFT:
                if a > 0:
                    a = a - 1
                    print(lines[a])
                else:
                    a = len(lines) - 1
                    print(lines[a])
            if event.key == pygame.K_RIGHT:
                if a < len(lines) - 1:
                    a = a + 1
                    print(lines[a])
                else:
                    a = 0
                    print(lines[a])
            if event.key == pygame.K_RETURN:
                import child_script 
In the child script I want to start performing other tasks.

But I noticed that when the child script is already running, then when you click on the arrows, elements from the previous (parent) script continue to be displayed on the screen. This is wrong, I need the parent script commands to stop running in the child script.

Tell me, please, how can this be done? :|
Respectfully,
Nikita Dedyukhin

User avatar
Paeryn
Posts: 2741
Joined: Wed Nov 23, 2011 1:10 am
Location: Sheffield, England

Re: Parent script termination

Mon Dec 02, 2019 2:49 am

Your understanding of import is flawed and using import like that is not going to do what you want. Import loads a module and makes the definitions available to your program, it isn't supposed to be used to run code directly. Python manual for import

When your program reaches import child_script it will first look to see if it has already been imported. If not then it loads that module (sort of running it) and binds the name to the resultant module object for your program to use. However, if the module has already been imported then it won't re-load it or run any code in it that isn't in functions as all that has already been done, it just binds the name to the already loaded module object.

In your code, after the first time around pressing Enter won't do anything from child_script as the module has already been loaded and you don't call any functions that it contains or anything.
She who travels light — forgot something.

User avatar
Zilla707
Posts: 69
Joined: Fri Aug 23, 2019 11:04 pm

Re: Parent script termination

Mon Dec 02, 2019 2:59 am

You could use threading to import/start the new script, and then "exit()" to quit the original program.

Code: Select all

import threading

def startScript(): 
	# The code for the thread is best in a function or class.
	import child_script

newThread = threading.Thread(target=startScript)
newThread.start()
This imports threading and the starts the new thread, splitting the flow of operations and spinning off a new thread, which imports the child script. Then you can call exit after "newThread.start()" and quit the original program.
P.S.
To Paeryn, that is also how I start a new python program running, do you have any other ideas on how to start a new program besides "import"?
Aim for perfect and you'll hit somewhere near pretty good. (maybe...)

User avatar
Paeryn
Posts: 2741
Joined: Wed Nov 23, 2011 1:10 am
Location: Sheffield, England

Re: Parent script termination

Mon Dec 02, 2019 4:40 am

Zilla707 wrote:
Mon Dec 02, 2019 2:59 am
To Paeryn, that is also how I start a new python program running, do you have any other ideas on how to start a new program besides "import"?
Python's import module_name does not, has never and never will "start a new program module_name running". If you want your Python program to start another program then run it. Import just brings the named module into the current program as is implied by its name, it imports a module.

From Python 3.5 subprocess.run() will run a program, prior to Python 3.5 use subprocess.call(). If you need more advanced control than what run() or call() provide then use the class subprocess.Popen

Code: Select all

import subprocess

subprocess.run(['filename_of_program_to_run'])
If you are wanting to run a Python program that doesn't have a shebang line and isn't flagged as executable then you'd need to run it via python3 (or python2 if it's a python2 program)

Code: Select all

subprocess.run(['python3', 'filename_of_program_to_run.py'])
Don't forget to include pathnames to said programs if they aren't in your default PATH. run() will run the named program and wait for it to finish before continuing.
She who travels light — forgot something.

User avatar
Zilla707
Posts: 69
Joined: Fri Aug 23, 2019 11:04 pm

Re: Parent script termination

Mon Dec 02, 2019 8:54 pm

Ok, that makes sense, I was wondering if "subprocess" could be used instead. thx.
Aim for perfect and you'll hit somewhere near pretty good. (maybe...)

User avatar
NPDedyukhin
Posts: 25
Joined: Fri Sep 20, 2019 3:23 am
Location: RU

Re: Parent script termination

Tue Dec 03, 2019 3:58 am

Paeryn wrote:
Mon Dec 02, 2019 4:40 am
Zilla707 wrote:
Mon Dec 02, 2019 2:59 am
To Paeryn, that is also how I start a new python program running, do you have any other ideas on how to start a new program besides "import"?
Python's import module_name does not, has never and never will "start a new program module_name running". If you want your Python program to start another program then run it. Import just brings the named module into the current program as is implied by its name, it imports a module.

From Python 3.5 subprocess.run() will run a program, prior to Python 3.5 use subprocess.call(). If you need more advanced control than what run() or call() provide then use the class subprocess.Popen

Code: Select all

import subprocess

subprocess.run(['filename_of_program_to_run'])
If you are wanting to run a Python program that doesn't have a shebang line and isn't flagged as executable then you'd need to run it via python3 (or python2 if it's a python2 program)

Code: Select all

subprocess.run(['python3', 'filename_of_program_to_run.py'])
Don't forget to include pathnames to said programs if they aren't in your default PATH. run() will run the named program and wait for it to finish before continuing.
I tried to do this:

Code: Select all

lines = ["A","B","C","D","E","F"]

import pygame
pygame.quit()
pygame.init()
screen = pygame.display.set_mode((400,400))

done = False
while not done:
    
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            done = True
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_LEFT:
                if a > 0:
                    a = a - 1
                    print(lines[a])
                else:
                    a = len(lines) - 1
                    print(lines[a])
            if event.key == pygame.K_RIGHT:
                if a < len(lines) - 1:
                    a = a + 1
                    print(lines[a])
                else:
                    a = 0
                    print(lines[a])
            if event.key == pygame.K_RETURN:
                import subprocess 
                subprocess.run('child_script.py', shell=True) #added shell = True, since in the compiler without this entry, the script was launched with an error
On the device, when this script is executed, after switching to the child script, the parent continues to execute (when you click on the keyboard arrows, the letters "A", "B", "C" appear again and so on).

Maybe I'm doing something wrong? :|
Respectfully,
Nikita Dedyukhin

scotty101
Posts: 3750
Joined: Fri Jun 08, 2012 6:03 pm

Re: Parent script termination

Tue Dec 03, 2019 9:44 am

Almost certain that both subprocess, threading, import etc are all not the way forward here.

Your main program will have a "main loop" which updates the screen and responds to any key/mouse events. You should keep a record of what mode you are currently in, main program or sub-program. When the main program "calls" the second program, what it should actually do it call the update and key binding functions for the the sub-program.

I'll throw together some non-working "psuedo" code as an example.

Code: Select all

#Main program.
import subprogram

done = False

def main_program_handle(event):
    global program
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            done = True
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_LEFT:
                if a > 0:
                    a = a - 1
                    print(lines[a])
                else:
                    a = len(lines) - 1
                    print(lines[a])
            if event.key == pygame.K_RIGHT:
                if a < len(lines) - 1:
                    a = a + 1
                    print(lines[a])
                else:
                    a = 0
                    print(lines[a])
            if event.key == pygame.K_RETURN:
                program = 'subprogram'

def main_program_update():
    #Do stuff here to update the main program like redrawing screens
    pass

while not done:
    for event in pygame.event.get():
        if program = 'main':
            main_program_handle(event)
        elif program = 'subprogram':
            subprogram.sub_program_handle(event)

    if program = 'main':
        main_program_update(event)
    elif program = 'subprogram':
        subprogram.sub_program_update(event)

Code: Select all

#Sub-program
def sub_program_handle(event):
    #Handle the key presses for the subprogram

def sub_program_update():
    #Update the screen etc for the subprogram

Totally un-working but shows a concept for calling functionality from another python module the correct way. subprocess is almost never the correct way to call python code from python code.
Electronic and Computer Engineer
Pi Interests: Home Automation, IOT, Python and Tkinter

User avatar
Zilla707
Posts: 69
Joined: Fri Aug 23, 2019 11:04 pm

Re: Parent script termination

Wed Dec 04, 2019 12:05 am

Does this work? I changed a few things.

Code: Select all

lines = ["A","B","C","D","E","F"]

import pygame
pygame.quit()
pygame.init()
screen = pygame.display.set_mode((400,400))
a = 0

# Starts the new program.
def start():
	subprocess.run('child_script.py', shell=True)

done = False
while not done:
    
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            done = True
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_LEFT:
                if a > 0:
                    a = a - 1
                    print(lines[a])
                else:
                    a = len(lines) - 1
                    print(lines[a])
            if event.key == pygame.K_RIGHT:
                if a < len(lines) - 1:
                    a = a + 1
                    print(lines[a])
                else:
                    a = 0
                    print(lines[a])
            if event.key == pygame.K_RETURN:
                import subprocess, threading, sys # Import threading and sys
                thread = threading.Thread(target=start) # This starts the second program in a thread. 
                thread.start() # Starts the new thread.
                pygame.quit() # Quit pygame
                sys.exit() # Stop the program

                
Hope this helps!
Aim for perfect and you'll hit somewhere near pretty good. (maybe...)

User avatar
NPDedyukhin
Posts: 25
Joined: Fri Sep 20, 2019 3:23 am
Location: RU

Re: Parent script termination

Sat Dec 07, 2019 8:00 pm

Zilla707 wrote:
Wed Dec 04, 2019 12:05 am
Does this work? I changed a few things.

Code: Select all

lines = ["A","B","C","D","E","F"]

import pygame
pygame.quit()
pygame.init()
screen = pygame.display.set_mode((400,400))
a = 0

# Starts the new program.
def start():
	subprocess.run('child_script.py', shell=True)

done = False
while not done:
    
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            done = True
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_LEFT:
                if a > 0:
                    a = a - 1
                    print(lines[a])
                else:
                    a = len(lines) - 1
                    print(lines[a])
            if event.key == pygame.K_RIGHT:
                if a < len(lines) - 1:
                    a = a + 1
                    print(lines[a])
                else:
                    a = 0
                    print(lines[a])
            if event.key == pygame.K_RETURN:
                import subprocess, threading, sys # Import threading and sys
                thread = threading.Thread(target=start) # This starts the second program in a thread. 
                thread.start() # Starts the new thread.
                pygame.quit() # Quit pygame
                sys.exit() # Stop the program

                
Hope this helps!
Greetings!

I tried to do as you said.

Everything works well in the compiler on a PC.

But, when I transferred the script to the device, this message appears:

Code: Select all

Exception in thread Thread-1:
Traceback (most recent call last):
   File "/usr/lib/python2.7/threading.py", line 801, in __bootstrap_inner
      self.run()
   File "/usr/lib/python2.7/threading.py", line 754, in run
      self.__target(*self.__args, **self._kwargs)
   File "home/pi/Desktop/project/parent_script.py", line 30, in start
      subprocess.run("child_script.py", shell=True)
AttributeError: 'module' object has no attribute 'run'

Apparently, the whole point is that the script runs in version 2.x of python, and not in 3.x.

I just edited the "etc/rc.local" file and added the line "python /home/pi/Desktop/project/parent_script.py" before "exit (0)" there.

I tried changing the line to "python3 /home/pi/Desktop/project/parent_script.py" - this way the script does not start when the device starts.

In this case, you need to understand how to run the script in python version 3.x. Most likely this caused an error. :|

I also tried to run the child script through python3, as Paeryn advised.

For this, I adjusted the line:

Code: Select all

def start ():
   subprocess.run ('child_script.py', shell = True)
On such a line:

Code: Select all

def start ():
   subprocess.run ('python3', 'child_script.py', shell = True)
The same message still comes out (AttributeError: 'module' object has no attribute 'run').
Respectfully,
Nikita Dedyukhin

User avatar
Paeryn
Posts: 2741
Joined: Wed Nov 23, 2011 1:10 am
Location: Sheffield, England

Re: Parent script termination

Sun Dec 08, 2019 12:28 am

NPDedyukhin wrote:
Sat Dec 07, 2019 8:00 pm
Apparently, the whole point is that the script runs in version 2.x of python, and not in 3.x.
...
I also tried to run the child script through python3, as Paeryn advised.

For this, I adjusted the line:

Code: Select all

def start ():
   subprocess.run ('child_script.py', shell = True)
On such a line:

Code: Select all

def start ():
   subprocess.run ('python3', 'child_script.py', shell = True)
The same message still comes out (AttributeError: 'module' object has no attribute 'run').
Did you not read the fairly important part of what I wrote, namely
Paeryn wrote: From Python 3.5 subprocess.run() will run a program, prior to Python 3.5 use subprocess.call().
If you absolutely need it to run under Python 2 (which is definitely prior to Python 3.5) then use subprocess.call() as subprocess.run() was only introduced in Python 3.5, hence the error about run not being present in the module subprocess (Python 2's error is slightly bad in that it only tells you it wasn't found in a module rather than naming the module).
She who travels light — forgot something.

User avatar
Zilla707
Posts: 69
Joined: Fri Aug 23, 2019 11:04 pm

Re: Parent script termination

Sun Dec 08, 2019 1:40 am

Well, if the change in the subprocess.call/run didn't work, I am assuming that the child script had a shebang "#!/usr/bin/env pyhon3" or "#!/usr/bin/env python". If the script doesn't have the shebang telling it how to run, that might be the problem. If it's trying to run the script in py2 then change the VERY FIRST LINE of the program to "#!/usr/bin/env python3". That *should* tell it to run the child script in python3, and make it able to be run by calling "subprocess.run(child_script.py, shell=True).
Aim for perfect and you'll hit somewhere near pretty good. (maybe...)

Return to “Python”