0

I am very new to Python (and programming for the most part) and I am working on a project to beacon morse code out and listen for morse code between beacons.

I have beacon working, probably not the best code but it does work, however when the loop starts, the Tkinter screen freezes and the stop beacon button does not work until all beacons are complete.

I would like to run the beacon infinitely and just use the stop beacon button to stop it but so far I can't seem to figure out how to stop the loop.

#!usr/bin/env python

import sys
import re
import tkMessageBox
from Tkinter import *
import pygame
import time


CODE = {'A': '.-',     'B': '-...',   'C': '-.-.', 
    'D': '-..',    'E': '.',      'F': '..-.',
    'G': '--.',    'H': '....',   'I': '..',
    'J': '.---',   'K': '-.-',    'L': '.-..',
    'M': '--',     'N': '-.',     'O': '---',
    'P': '.--.',   'Q': '--.-',   'R': '.-.',
    'S': '...',    'T': '-',      'U': '..-',
    'V': '...-',   'W': '.--',    'X': '-..-',
    'Y': '-.--',   'Z': '--..',

    '0': '-----',  '1': '.----',  '2': '..---',
    '3': '...--',  '4': '....-',  '5': '.....',
    '6': '-....',  '7': '--...',  '8': '---..',
    '9': '----.' 
    }

ONE_UNIT = 0.5
THREE_UNITS = 3 * ONE_UNIT
SEVEN_UNITS = 7 * ONE_UNIT
PATH = 'morse_sound_files/'

def verify(string):
keys = CODE.keys()
for char in string:
    if char.upper() not in keys and char != ' ':
        sys.exit('Error the charcter ' + char + ' cannot be translated    to Morse Code')




beaconout = ''
beaconTEXT = 'this is text that is default'
def ask_quit():
if tkMessageBox.askokcancel("Quit", "are you sure you want to quit?"):
    root.destroy()

def getinput():
incomingTEXT = incoming.get()
outboundTEXT = outbound.get()
beaconTEXT = beaconmessage.get(1.0,"end")
beaconout = outboundTEXT+" "+outboundTEXT+" "+outboundTEXT+" "+beaconTEXT+""+incomingTEXT
print beaconout
beaconout = beaconout.replace('\n', ' ')
print beaconout
print 'Welcome to Alphabet to Morse Code Translator v.01'
msg = beaconout
#verify(msg)
print
pygame.init()

for char in msg:
    if char == ' ':
        print ' '*7,
        time.sleep(SEVEN_UNITS)
    else:
              print CODE[char.upper()],
              pygame.mixer.music.load(PATH + char.upper() + '_morse_code.ogg')
              pygame.mixer.music.play()
              time.sleep(THREE_UNITS)




root = Tk()
root.geometry("800x600+300+300")
frame = Frame(root, width=1000, height=600)
label1 = Label(root, text="To: Call Sign:")
label2 = Label(root, text="Your Call Sign:")
label3 = Label(root, text="Enter your message:")

outbound = StringVar()
outboundcallsign  = Entry(root, textvariable=outbound)

incoming = StringVar()
inboundcallsign = Entry(root, textvariable=incoming)

beacon = StringVar()
beaconmessage = Text(root, height=1, width=30)

label1.grid(row=1, sticky=E)
label2.grid(row=2, sticky=E)
label3.grid(row=3, sticky=E)

outboundcallsign.grid(row=1, column=1)
inboundcallsign.grid(row=2, column=1)
beaconmessage.grid(row=4, columnspan=4)

cbox = Checkbutton(root, text="message is ready to beacon?")
cbox.grid(columnspan=2)
submitbut = Button(root,text="Start Beacon", command = getinput)
submitbut.grid(row=14,column=1)
submitbut.bind("<Button-1>")

cancelbut = Button(root,text="Stop Beacon", command=ask_quit)
cancelbut.grid(row=14, column=3)

root.mainloop()
1
  • 1
    Difficult to reproduce your question, but the problem seems to be that the UI thread is blocked. Either put the stuff from getinput into a thread, or rewrite your for char in msg loop using root.after instead of sleep Commented Jun 18, 2015 at 10:42

1 Answer 1

1

"I would like to run the beacon infinitely and just use the stop beacon button to stop"

Run the "Beacon" code in a separate process, as you want to do 2 things at the same time, [1] Beacon code and [2] check for button press (although it may be possible to find a creative way to use "after" to check for button press, IMHO it is more obvious and less trouble to do it this way). The quit button will cancel the process. This example uses a counter instead of the beacon code for simplicity.

from multiprocessing import Process
from functools import partial

try:
    import Tkinter as tk    ## Python 2.x
except:
    import tkinter as tk    ## Python 3.x

class ProgressBar():
    def __init__(self, root):
        self.root=root
        self.root.geometry("75x50+900+100")
        self.ctr=1

    def counter(self):
        """ a separate process in a separate GUI
        """
        self.top_count=tk.Toplevel(self.root)
        self.top_count.geometry("75x50+750+50")
        self.label_ctr = tk.IntVar()
        self.label_ctr.set(self.ctr)
        label = tk.Label(self.top_count, textvariable=self.label_ctr)
        label.pack()
        self.change_counter()

    def change_counter(self):
        self.ctr += 1
        self.label_ctr.set(self.ctr)
        self.top_count.after(750, self.change_counter)


def stop_process(process_id, PB):
    process_id.terminate()
    PB.top_count.destroy()  ## destroy Toplevel to stop "after"

    ## shut down Tkinter
    ##root.quit()

root = tk.Tk()

PB=ProgressBar(root)
pr1=Process(target=PB.counter(), args=())
pr1.start()

tk.Button(root, text="Exit", bg="orange",
          command=partial(stop_process, pr1, PB)).pack()
root.mainloop()

If you don't want to destroy the Toplevel then you can use a variable, in the following case where these 2 funtions are changed, ctr > 0 To communicate between two processes requires a manager dictionary or list however.

def change_counter(self):
    self.ctr += 1
    self.label_ctr.set(self.ctr)
    if self.ctr > 0:
        self.top_count.after(750, self.change_counter)


def stop_process(pr1, PB):
    PB.ctr = -10
    pr1.terminate()
##    PB.top_count.destroy()
Sign up to request clarification or add additional context in comments.

2 Comments

Hmm, I really wanted to contain this within the same frame, and honestly I need to be doing several things at the same time, I have yet to develop the other listen, decode and print the decoded text functions, but my intention is to listen when not actually beaconing, if the listening decodes an expected call sign then we will print the decoded message to the same frame that we are using to input and start the beacon. Is this possible with Python or am I exceeding Python capabilities trying to do this?
You can certainly change the Toplevel to a Frame and still use either of the 2 methods above. If you want many instances at the same time then the stop_process() function, as well as anything else you may add, should all be in the same class if you want to stop them individually. You then start a separate process for each class instance.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.