seanspotatobusiness
Posts: 185
Joined: Tue May 22, 2012 11:19 pm

Command line program that doesn't end when pressing Ctrl + C

Wed May 22, 2019 12:01 am

I copied this program (from here: https://grantwinney.com/how-to-use-an-r ... pberry-pi/) to make random RGB colour transitions with an RGB LED. The code seems to include an attempt to tell the user how to terminate the program (by pressing Ctrl + C) but for some reason this doesn't work. Pressing Ctrl + C just puts a ^ and a C on the screen. Any idea why it's not working the way it was intended? Thanks.

Script:

Code: Select all

import RPi.GPIO as GPIO
import threading
import time
import random
 
R = 12
G = 33
B = 32
 
PINS = [R,G,B]

 
def initialize_gpio():
    GPIO.setmode(GPIO.BOARD)
    GPIO.setup(PINS, GPIO.OUT, initial=GPIO.LOW)
 
 
def color_test(channel, frequency, speed, step):
    p = GPIO.PWM(channel, frequency)
    p.start(0)
    while True:
        for dutyCycle in range(0, 101, step):
            p.ChangeDutyCycle(dutyCycle)
            time.sleep(speed)
        for dutyCycle in range(100, -1, -step):
            p.ChangeDutyCycle(dutyCycle)
            time.sleep(speed)
 
 
def color_test_thread():
    threads = []
    threads.append(threading.Thread(target=color_test, args=(R, 300, 0.02, 5)))
    threads.append(threading.Thread(target=color_test, args=(G, 300, 0.035, 5)))
    threads.append(threading.Thread(target=color_test, args=(B, 300, 0.045, 5)))
    for t in threads:
        t.daemon = True
        t.start()
    for t in threads:
        t.join()
 
 
def main():
    try:
        initialize_gpio()
        print("\nPress ^C (control-C) to exit the program.\n")
        color_test_thread()
    except KeyboardInterrupt:
        pass
    finally:
        GPIO.cleanup()
 
 
if __name__ == '__main__':
    main()
Result:

Code: Select all

[email protected]:~ $ python ./Documents/RGBrainbow.py

Press ^C (control-C) to exit the program.

^C^C^C^C^C^C
Raspberry Pi 3 B and B+
PRETTY_NAME="Raspbian GNU/Linux 9 (stretch)"
NAME="Raspbian GNU/Linux"
VERSION_ID="9"
VERSION="9 (stretch)"

dl324
Posts: 119
Joined: Mon May 06, 2019 7:33 pm
Location: Pacific Northwest, USA

Re: Command line program that doesn't end when pressing Ctrl + C

Wed May 22, 2019 12:16 am

Works for me:

Code: Select all

35 work> while (1)
while? echo -n .
while? sleep 1
while? end
....^C
39 work> 

seanspotatobusiness
Posts: 185
Joined: Tue May 22, 2012 11:19 pm

Re: Command line program that doesn't end when pressing Ctrl + C

Wed May 22, 2019 12:31 am

dl324 wrote:
Wed May 22, 2019 12:16 am
Works for me:

Code: Select all

35 work> while (1)
while? echo -n .
while? sleep 1
while? end
....^C
39 work> 
Thanks for responding. What is the code you posted? Is that some output you get on the CLI?
Raspberry Pi 3 B and B+
PRETTY_NAME="Raspbian GNU/Linux 9 (stretch)"
NAME="Raspbian GNU/Linux"
VERSION_ID="9"
VERSION="9 (stretch)"

dl324
Posts: 119
Joined: Mon May 06, 2019 7:33 pm
Location: Pacific Northwest, USA

Re: Command line program that doesn't end when pressing Ctrl + C

Wed May 22, 2019 1:46 am

seanspotatobusiness wrote:
Wed May 22, 2019 12:31 am
Thanks for responding. What is the code you posted? Is that some output you get on the CLI?
It's csh or, more accurately, tcsh. Tcsh is a superset of csh.

I used csh for several decades at work and prefer to use what I know. I never did like sh, that's why I used csh. Bash is another sh variant and I don't care for it.

Andyroo
Posts: 3384
Joined: Sat Jun 16, 2018 12:49 am
Location: Lincs U.K.

Re: Command line program that doesn't end when pressing Ctrl + C

Wed May 22, 2019 9:27 pm

Actually I think you have found a bug in the Python2 threading module in that your threads are not ending with the CTRL-C - I think the code shown by dl324 is single threaded basically and not the same structure as yours.

Have you tried this in Python 3 at all?

For any of my programs that rely on threads I build in a 'kill' routine to handle the end of threads cleanly and this is called by a function that picks up kill commands and ctrl-c. Normally for me I keep it reasonably simple as follows:

In the main program part I create a callback function handler for the kill style messages (SIGTERM, SIGHUP, SIGINT):

Code: Select all

for sig in ('TERM', 'HUP', 'INT'):
    signal.signal(getattr(signal, 'SIG' + sig), kill_handler);
This now points to a specific function outside of the main code:

Code: Select all

def kill_handler(signal, frame):
    shutdown_all_bits('Forced')
    sys.exit(0)
Basically, all this function does is to call the close down routine (shutdown_all_bits) with a flag saying this was forced and when that routine is done the program exits with a zero error level as things will have been closed down correctly (I hope). This routines has most of its code in try...except blocks as this should be a last resort function in most cases.

Within each thread I use a global flag (RunThread) to say if they should end or not. This is set FALSE by the shutdown_all_bits routine that then joins each thread to let it end correctly.

So a very simple thread to put an entry on a queue would then look like:

Code: Select all

def push_check_temp():
    global Computer
    global RunThread
    
    cmd = {
        "Device" : Computer ,
        "Command" : 'CoreTemp'
    }

    while RunThread:                                    # Global to see if I can still run or not
        delay = 10 * 60 * 2                            # 10 minute delay in 1/2 second intervals
        while (delay > 0) and RunThread:
            time.sleep(0.5)
            delay = delay - 1
        if RunThread:
            cmdq.put(cmd)
My shutdown routine is also called by the normal shutdown code (be that MQTT / user input / end GPIO signal etc) but uses the parameter to determine how it should handle things.
Need Pi spray - these things are breeding in my house...

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

Re: Command line program that doesn't end when pressing Ctrl + C

Thu May 23, 2019 2:56 am

Andyroo wrote:
Wed May 22, 2019 9:27 pm
Actually I think you have found a bug in the Python2 threading module in that your threads are not ending with the CTRL-C.
I don't think it's a bug, it looks like under Python2 when the main process executes Thread.join() the signal handler is being blocked until the join returns (it doesn't seem to be being called until the join returns), but since the threads didn't have any way of ending the join would never return hence CTRL-C was never handled. Python3 seems to not be blocking on the join, I haven't looked any further to see if it is either Python2 deliberately blocking or Python3 deliberately not blocking (way past my bedtime).

Addendum:
I just noticed a difference in the code that acquires the lock used by Thread.join(), the comments say it all
Python2: "The blocking operation is not interruptible."
Python3: "The blocking operation is interruptible."
She who travels light — forgot something.

Andyroo
Posts: 3384
Joined: Sat Jun 16, 2018 12:49 am
Location: Lincs U.K.

Re: Command line program that doesn't end when pressing Ctrl + C

Thu May 23, 2019 8:13 am

Glad I’ve moved all my threaded programs to Python 3 :lol:
Need Pi spray - these things are breeding in my house...

gkreidl
Posts: 5956
Joined: Thu Jan 26, 2012 1:07 pm
Location: Germany

Re: Command line program that doesn't end when pressing Ctrl + C

Thu May 23, 2019 8:24 am

You can set the daemon property on any thread (Python2 at least). Then Python will not wait for the thread to finish.
Minimal Kiosk Browser (kweb)
Slim, fast webkit browser with support for audio+video+playlists+youtube+pdf+download
Optional fullscreen kiosk mode and command interface for embedded applications
Includes omxplayerGUI, an X front end for omxplayer

Return to “Python”