简体   繁体   中英

How to call a method from an already running python script

I need to check if the python script is already running then calling a method from the same running python script. But it must be on same process(pid), no new process. Is this possible?

I tried some codes but not worked.

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import Tkinter as tk
from Tkinter import *
import socket


class Main():
    def mainFunc(self):
        self.root = tk.Tk()
        self.root.title("Main Window")

        self.lbl = Label(self.root, text = "First Text")
        self.lbl.pack()

        openStngs = Button(self.root, text = "Open Settings", command=self.settingsFunc)
        openStngs.pack()

    def settingsFunc(self):
        stngsRoot = Toplevel()
        stngsRoot.title("Settings Window")

        changeTextOfLabel = Button(stngsRoot, text = "Change Main Window Text", command=self.change_text)
        changeTextOfLabel.pack()

    def change_text(self):
        self.lbl.config(text="Text changed")

# the get_lock from http://stackoverflow.com/a/7758075/3254912
def get_lock(process_name):
    lock_socket = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
    try:
        print lock_socket
        lock_socket.bind('\0' + process_name)
        print 'I got the lock'

        m.mainFunc()
        mainloop()

    except socket.error:
        print 'lock exists'
        m.settingsFunc()
        mainloop()
        # sys.exit()

if __name__ == '__main__':
    m=Main()
    get_lock('myPython.py')

You either need:

  • A proactive check in your running process to look at the environment (for instance, the contents of a file or data coming through a socket) to know when to fire the function,
  • or for your running process to receive unix signals or some other IPC (possibly one of the user-defined signals) and perform a function when one is received.

Either way you can't just reach into a running process and fire a function inside that process (it MIGHT not be literally impossible if you hook the running process up to a debugger, but I wouldn't recommend it).

Tkinter necessarily has its own event loop system, so I recommend reading up on how that works and how to either run something on a timer in that event loop system, or set up a callback that responds to a signal. You could also wrap a non-event loop based system in a try/except block that will catch an exception generated by a UNIX signal, but it may not be straightforward to resume the operation of the rest of the program after that signal is caught, in that case.

Sockets are a good solution to this kind of interprocess communication problem.

One possible approach would be to set up a socket server in a thread in your original process, this can be used as an entry point for external input. A (rather stupid) example might be:

# main.py

import socket
import SocketServer # socketserver in Python 3+
import time
from Queue import Queue
from threading import Thread

# class for handling requests
class QueueHandler(SocketServer.BaseRequestHandler):
    def __init__(self, request, client_address, server):
        self.server = server
        server.client_address = client_address
        SocketServer.BaseRequestHandler.__init__(self,request, client_address, server)

    # receive a block of data
    # put it in a Queue instance
    # send back the block of data (redundant)  
    def handle(self):
        data = self.request.recv(4096)
        self.server.recv_q.put(data)
        self.request.send(data)


class TCPServer(SocketServer.TCPServer):
    def __init__(self, ip, port, handler_class=QueueHandler):
        SocketServer.TCPServer.__init__(self, (ip, port), handler_class, bind_and_activate=False)
        self.recv_q = Queue() # a Queue for data received over the socket
        self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.server_bind()
        self.server_activate()

    def shutdown(self):
        SocketServer.TCPServer.shutdown(self)

    def __del__(self):
        self.server_close()


# This is the equivalent of the main body of your original code
class TheClassThatLovesToAdd(object):
    def __init__(self):
        self.value = 1

        # create and instance of the server attached to some port
        self.server = TCPServer("localhost",9999)

        # start it listening in a separate control thread
        self.server_thread = Thread(target=self.server.serve_forever)
        self.server_thread.start()
        self.stop = False

    def add_one_to_value(self):
        self.value += 1

    def run(self):
        while not self.stop:
            print "Value =",self.value

            # if there is stuff in the queue...
            while not self.server.recv_q.empty():

                # read and parse the message from the queue
                msg = self.server.recv_q.get()

                # perform some action based on the message
                if msg == "add":
                    self.add_one_to_value()
                elif msg == "shutdown":
                    self.server.shutdown()
                    self.stop = True

            time.sleep(1)

if __name__ == "__main__":
    x = TheClassThatLovesToAdd()
    x.run()

When you start this running, it should just loop over and over printing to the screen. Output:

Value = 1
Value = 1
Value = 1
...

However the TCPServer instance attached to the TheClassThatLovesToAdd instance now gives us a control path. The simplest looking snippet of control code would be:

# control.py

import socket
import sys

sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sock.settimeout(2)
sock.connect(('localhost',9999))

# send some command line argument through the socket
sock.send(sys.argv[1])
sock.close()

So if I run main.py in one terminal window and call python control.py add from another, the output of main.py will change:

Value = 1
Value = 1
Value = 1
Value = 2
Value = 2
...

Finally to kill it all we can run python control.py shutdown , which will gently bring main.py to a halt.

This is by no means the only solution to your problem, but it is likely to be one of the simplest.

One can try GDB, but not sure how to call a function from within [an idle thread].

Perhaps someone very versed with gdb and debugging/calling Python functions from within GDB can improve this answer.

One solution would be to use a messaging service (such as ActiveMQ or RabbitMQ). Your application subscribes to a queue/topic and whenever you want to send it a command, you write a message to it's queue. I'm not going to go into details because there are thousands of examples on-line. Queues/messaging/MQTT etc. are very simple to implement and are how most business systems (and modern control systems) communicate. Do a search for paho-mqtt.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM