إعادة توجيه نتائج سطر الأوامر إلى واجهة المستخدم الرسومية tkinter

StackOverflow https://stackoverflow.com/questions/665566

  •  21-08-2019
  •  | 
  •  

سؤال

لقد قمت بإنشاء برنامج يقوم بطباعة النتائج على سطر الأوامر.(إنه خادم ويقوم بطباعة السجل على سطر الأوامر.)

الآن، أريد أن أرى نفس النتيجة لواجهة المستخدم الرسومية.

كيف يمكنني إعادة توجيه نتائج سطر الأوامر إلى واجهة المستخدم الرسومية؟

من فضلك، اقترح خدعة لتحويل تطبيق وحدة التحكم بسهولة إلى واجهة مستخدم رسومية بسيطة.

لاحظ أنه يجب أن يعمل على Linux وWindows.

هل كانت مفيدة؟

المحلول

يمكنك إنشاء غلاف برنامج نصي يقوم بتشغيل برنامج سطر الأوامر الخاص بك كعملية فرعية، ثم إضافة الإخراج إلى شيء مثل عنصر واجهة المستخدم النصي.

from tkinter import *
import subprocess as sub
p = sub.Popen('./script',stdout=sub.PIPE,stderr=sub.PIPE)
output, errors = p.communicate()

root = Tk()
text = Text(root)
text.pack()
text.insert(END, output)
root.mainloop()

حيث البرنامج النصي هو البرنامج الخاص بك.من الواضح أنه يمكنك طباعة الأخطاء بلون مختلف، أو شيء من هذا القبيل.

نصائح أخرى

لعرض مخرجات العملية الفرعية في واجهة المستخدم الرسومية بينما لا يزال قيد التشغيل, ، يجب أن يستخدم الحل المحمول stdlib فقط الذي يعمل على كل من Python 2 و 3 مؤشر ترابط في الخلفية:

#!/usr/bin/python
"""
- read output from a subprocess in a background thread
- show the output in the GUI
"""
import sys
from itertools import islice
from subprocess import Popen, PIPE
from textwrap import dedent
from threading import Thread

try:
    import Tkinter as tk
    from Queue import Queue, Empty
except ImportError:
    import tkinter as tk # Python 3
    from queue import Queue, Empty # Python 3

def iter_except(function, exception):
    """Works like builtin 2-argument `iter()`, but stops on `exception`."""
    try:
        while True:
            yield function()
    except exception:
        return

class DisplaySubprocessOutputDemo:
    def __init__(self, root):
        self.root = root

        # start dummy subprocess to generate some output
        self.process = Popen([sys.executable, "-u", "-c", dedent("""
            import itertools, time

            for i in itertools.count():
                print("%d.%d" % divmod(i, 10))
                time.sleep(0.1)
            """)], stdout=PIPE)

        # launch thread to read the subprocess output
        #   (put the subprocess output into the queue in a background thread,
        #    get output from the queue in the GUI thread.
        #    Output chain: process.readline -> queue -> label)
        q = Queue(maxsize=1024)  # limit output buffering (may stall subprocess)
        t = Thread(target=self.reader_thread, args=[q])
        t.daemon = True # close pipe if GUI process exits
        t.start()

        # show subprocess' stdout in GUI
        self.label = tk.Label(root, text="  ", font=(None, 200))
        self.label.pack(ipadx=4, padx=4, ipady=4, pady=4, fill='both')
        self.update(q) # start update loop

    def reader_thread(self, q):
        """Read subprocess output and put it into the queue."""
        try:
            with self.process.stdout as pipe:
                for line in iter(pipe.readline, b''):
                    q.put(line)
        finally:
            q.put(None)

    def update(self, q):
        """Update GUI with items from the queue."""
        for line in iter_except(q.get_nowait, Empty): # display all content
            if line is None:
                self.quit()
                return
            else:
                self.label['text'] = line # update GUI
                break # display no more than one line per 40 milliseconds
        self.root.after(40, self.update, q) # schedule next update

    def quit(self):
        self.process.kill() # exit subprocess if GUI is closed (zombie!)
        self.root.destroy()


root = tk.Tk()
app = DisplaySubprocessOutputDemo(root)
root.protocol("WM_DELETE_WINDOW", app.quit)
# center window
root.eval('tk::PlaceWindow %s center' % root.winfo_pathname(root.winfo_id()))
root.mainloop()

جوهر الحل هو:

  • ضع مخرجات العملية الفرعية في قائمة الانتظار في سلسلة رسائل الخلفية
  • الحصول على الإخراج من قائمة الانتظار في مؤشر ترابط واجهة المستخدم الرسومية.

أي اتصل process.readline() في موضوع الخلفية -> قائمة الانتظار -> تحديث تسمية واجهة المستخدم الرسومية في الموضوع الرئيسي.متعلق ب kill-process.py (لا يوجد اقتراع - حل أقل قابلية للنقل يستخدم event_generate في موضوع الخلفية).

تعد إعادة توجيه stdout إلى طريقة write()‎ التي تعمل على تحديث واجهة المستخدم الرسومية إحدى الطرق التي يمكنك اتباعها، وربما هي الأسرع - على الرغم من أن تشغيل عملية فرعية ربما يكون حلاً أكثر أناقة.

لا تقم بإعادة توجيه stderr إلا عندما تكون واثقًا من أنه جاهز للعمل، رغم ذلك!

مثال على التنفيذ (ملف واجهة المستخدم الرسومية والبرنامج النصي للاختبار):

test_gui.py:

from Tkinter import *
import sys
sys.path.append("/path/to/script/file/directory/")

class App(Frame):
    def run_script(self):
        sys.stdout = self
        ## sys.stderr = self
        try:
            del(sys.modules["test_script"])
        except:
            ## Yeah, it's a real ugly solution...
            pass
        import test_script
        test_script.HelloWorld()
        sys.stdout = sys.__stdout__
        ## sys.stderr = __stderr__

    def build_widgets(self):
        self.text1 = Text(self)
        self.text1.pack(side=TOP)
        self.button = Button(self)
        self.button["text"] = "Trigger script"
        self.button["command"] = self.run_script
        self.button.pack(side=TOP)

    def write(self, txt):
        self.text1.insert(INSERT, txt)

    def __init__(self, master=None):
        Frame.__init__(self, master)
        self.pack()
        self.build_widgets()

root = Tk()
app = App(master = root)
app.mainloop()

test_script.py:

print "Hello world!"

def HelloWorld():
    print "HelloWorldFromDef!"

اسف على سوء لغتي الانجليزية.لقد استخدمت بالفعل طريقة مختلفة لطباعة إخراج موجه الأوامر في أداة الأتمتة الجديدة الخاصة بي.يرجى العثور على هذه الخطوات أدناه.

1> قم بإنشاء ملف Bat وأعد توجيه مخرجاته إلى ملف LOG.أمر موجه الأوامر: tasklist /svc

2> قم بقراءة هذا الملف باستخدام Python 3.x.`processedFile = open('D:\LOG askLog.txt', 'r')

3> الخطوة النهائية.ttk.Label(Tab4, text=[ProcessFile.read()]).place(x=0, y=27)

** ومن ثم يرجى العلم أنني لم أقم بتضمين شريط التمرير في هذا الرمز حتى الآن.

نشر لقطة الشاشة:

enter image description here

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top