3

I have a simple graphics program that shows some instructions and touch buttons to a touch screen in a remote raspberry.

Instead of executing directly, I run it through a SSH connection, so I have in my desktop al application log.

I would like to have some short of interaction from the console I run the script, like execute some functions or change values of some variables.

Is that even possible?

I do not want to create a console inside the TKinter window, as alessandro asked: How to embed a terminal in a Tkinter application?

not sure if I should use a short of subprocess, as user1941008, but htis seemes too complicate Write to terminal in Tkinter GUI

and I do prefer not to create a client/server setup or a middle buffer for this thing, too complex, and i will have to rewrite things to send the logs to new program.

I add a little little version of my code:

#!/usr/bin/env python3
import tkinter as tk

class _tkapp(tk.Frame):
    def __init__(self, master=None):
        super().__init__(master)
        self.master = master
        self.pack()
        self.create_widgets()

    def create_widgets(self):

        self.redButton = tk.Button(self, text='Make me red', command=self.paintMeRed)
        self.redButton.pack(side='top')

        self.blueButton = tk.Button(self, text='Make me blue', command=self.paintMeBlue)
        self.blueButton.pack(side='top')

        self.quit = tk.Button(self, text='QUIT', fg='red', command=self.master.destroy)
        self.quit.pack(side='bottom')

    def paintMeRed(self):
        tk_root.configure(background='red')
        print('user click on RED')

    def paintMeBlue(self):
        tk_root.configure(background='blue')
        print('user click on BLUE')


tk_root = tk.Tk()
tk_root.geometry("200x120") 
tk_app = _tkapp(master=tk_root)
tk_app.mainloop()

this allow me to see on console what user cliked, my objetive, change the color also from console

PeterVro
  • 31
  • 2

2 Answers2

0

Here is the answer for your question. My other (deleted) answer didn't fit to your question.

Unfortunately you can't do it without sockets but I add some easy to adapt methods (init_remote_execution, listener, do_remite_call and cleanup) to your example that you can copy and paste in your actually application. You only need to adapt the do_remote_call method:

#!/usr/bin/env python3
import socket
import tkinter as tk
from queue import Queue
from threading import Thread
from uuid import uuid1

UDP_HOST = ""
UDP_PORT = 5005
RECV_BUFFER = 1020


class _tkapp(tk.Frame):
    def __init__(self, master=None):
        super().__init__(master)
        self.master = master
        self.pack()
        self.create_widgets()
        self.init_remote_execution()
        self.master.protocol("WM_DELETE_WINDOW", self.cleanup)  # call cleanup on exit

    def init_remote_execution(self):
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.sock.bind((UDP_HOST, UDP_PORT))
        self.endmsg = str(uuid1())  # Random string to stop threads
        self.queue = Queue()
        self.treads_running = True
        self.listener_thread = Thread(target=self.listener)
        self.worker_thread = Thread(target=self.do_remote_call)
        self.listener_thread.start()
        self.worker_thread.start()

    def listener(self):
        print("listen")
        while self.treads_running:
            data, addr = self.sock.recvfrom(RECV_BUFFER)
            data = data.decode().strip()
            print("from {addr}: {data}".format(addr=addr, data=data))
            if data == self.endmsg:
                self.treads_running = False
            self.queue.put(data)
        self.sock.close()

    def do_remote_call(self):
        while self.treads_running:
            data = self.queue.get()
            if data == self.endmsg:
                print("Bye")
            elif data == "click RED":
                self.paintMeRed()
            elif data == "click BLUE":
                self.paintMeBlue()
            else:
                print(">>> unknown command")

    def cleanup(self):
        print("cleanup")
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        s.sendto(self.endmsg.encode(), ("127.0.0.1", UDP_PORT))
        self.listener_thread.join()
        self.worker_thread.join()
        self.master.destroy()

    def create_widgets(self):
        self.redButton = tk.Button(self, text="Make me red", command=self.paintMeRed)
        self.redButton.pack(side="top")

        self.blueButton = tk.Button(self, text="Make me blue", command=self.paintMeBlue)
        self.blueButton.pack(side="top")

        self.quit = tk.Button(
            self, text="QUIT", fg="red", command=self.cleanup
        )  # call cleanup!!!
        self.quit.pack(side="bottom")

    def paintMeRed(self):
        tk_root.configure(background="red")
        print("user click on RED")

    def paintMeBlue(self):
        tk_root.configure(background="blue")
        print("user click on BLUE")


if __name__ == "__main__":
    tk_root = tk.Tk()
    tk_root.geometry("200x120")
    tk_app = _tkapp(master=tk_root)
    tk_app.mainloop()

It is important, that you call the cleanup method on the quit button (otherwise the threads will not stop and the application will hang)!

Now you can "click" the "make me blue" button by sending an udp message (click BLUE) to Port 5005 on your raspberry.

With the tool netcat on the raspberry (you may need to install it with apt) you can send the commad to click blue like this:

echo "click BLUE" | nc -uw0 127.0.0.1 5005

With python:

#!/usr/bin/env python3
import socket


sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.sendto('click BLUE'.encode(), ('127.0.0.1', 5005)
sock.close()

From your desktop you have to switch the '127.0.0.1' with the ip address of your raspberry.

Sebastian Stigler
  • 6,819
  • 2
  • 29
  • 31
0

Here an even simpler version using the cmd module from python. This module allows you to write an repl (read evaluete print loop) for any thing.

#!/usr/bin/env python3
import cmd
import os
import signal
import tkinter as tk
from threading import Thread


class _tkapp(tk.Frame):
    def __init__(self, master=None):
        super().__init__(master)
        self.master = master
        self.pack()
        self.create_widgets()

    def create_widgets(self):

        self.redButton = tk.Button(self, text="Make me red", command=self.paintMeRed)
        self.redButton.pack(side="top")

        self.blueButton = tk.Button(self, text="Make me blue", command=self.paintMeBlue)
        self.blueButton.pack(side="top")

        self.quit = tk.Button(self, text="QUIT", fg="red", command=self.master.destroy)
        self.quit.pack(side="bottom")

    def paintMeRed(self):
        tk_root.configure(background="red")
        print("user click on RED")

    def paintMeBlue(self):
        tk_root.configure(background="blue")
        print("user click on BLUE")


class Command(cmd.Cmd):
    intro = "Welcome to the repl of the Tk app.   Type help or ? to list  commands.\n"
    prompt = ""

    def __init__(self, tkapp):
        super().__init__()
        self.tkapp = tkapp

    # New commands must start with do_ and have one argument.
    # The docstring is the help text.
    # They must not return anything!
    # only the do_quit returns True to indicate that the command loop must stop
    def do_paint(self, arg):
        """Paint the background in the color red or blue:   PAINT RED"""
        clean_arg = arg.strip().lower()
        if clean_arg == "red":
            self.tkapp.paintMeRed()
        elif clean_arg == "blue":
            self.tkapp.paintMeBlue()
        else:
            print("Can't paint the color %s." % clean_arg)

    def do_quit(self, arg):
        """Stop the application:   QUIT"""
        self.tkapp.master.destroy()
        return True


if __name__ == "__main__":
    tk_root = tk.Tk()
    tk_root.geometry("200x120")
    tk_app = _tkapp(master=tk_root)

    command = Command(tk_app)
    command_thread = Thread(target=command.cmdloop)
    command_thread.start()

    tk_app.mainloop()

    os.kill(os.getpid(), signal.SIGTERM)

If you run the program via ssh, you can directly type in command help paint and quit. All outputs from the tk app will be printed here as well.

Just type help or help paint to see how it goes.

Sebastian Stigler
  • 6,819
  • 2
  • 29
  • 31