Запись в текстовое поле easygui во время работы функции?

В настоящее время я запускаю скрипт, используя easygui для получения пользовательского ввода. Старый сценарий, который запускался в командной строке, просто печатал все, что пользователю нужно было знать в командной строке, но я изменил его, чтобы выводить уведомления в новых окнах easygui, когда есть необходимые входные данные.

Я хочу, чтобы прогресс, каждое действие внутри запущенных функций выводилось в текстовое поле по мере их завершения. В командной строке я мог бы просто использовать print "text", но я не могу заставить это происходить в реальном времени в easygui. В настоящее время я добавляю список, поэтому у меня есть текстовое поле, в котором отображаются результаты функции после того, как все будет завершено, но я бы хотел, чтобы большое окно текстового поля появлялось и печатало строку всякий раз, когда процесс заметки завершается. Это выполнимо?

Вот как я добавляю список:

result_list = []
record_str = "\n Polling has completed for 502."
result_list.append(record_str)
eg.textbox("Polling Status", "Daily polling completion status:", result_list)

person Benjooster    schedule 06.08.2013    source источник


Ответы (1)


Я не думаю, что есть простой способ заставить функцию EasyGUI textbox делать то, что вы хотите, за исключением изменения модуля. Поскольку это функция, а не класс, вы даже не можете получить от нее подкласс, чтобы легко повторно использовать его код.

Однако вполне возможно создать отдельное Tkinter окно, которое просто отображает строки текста по мере того, как они отправляются в него, используя расширенную версию некоторого кода, который я однажды нашел в ветке на comp.lang.python группы новостей.

Исходный код был разработан для захвата и отображения только stderr вывода из приложения с графическим интерфейсом, которое обычно не имеет stderr дескриптора вывода, поэтому модуль был назван errorwindow. Однако я изменил его, чтобы иметь возможность перенаправлять как stderr, так и stdout в такие окна в одном приложении на основе easygui, которое я разработал, но я так и не удосужился переименовать его или обновить комментарии в нем, чтобы также упомянуть перенаправление stdout. ;¬)

В любом случае, модуль работает, определяя и создавая два экземпляра файлового класса с именем OutputPipe, когда он imported, и назначает их файловым объектам потока ввода-вывода sys.stdout и sys.stderr, которые обычно None в приложениях с графическим интерфейсом Python .pyw (в Windows) . Когда выходные данные впервые отправляются в любой из них, тот же модуль запускается как отдельный процесс Python с его обработчиками ввода-вывода stdin, stdout и stderr, связанными с исходным процессом.

Происходит много всего, но, по крайней мере, небольшое изучение этого может дать вам некоторые идеи о том, как заставить textbox easygui делать то, что вы хотите. Надеюсь это поможет.

Примечание. Опубликованный код предназначен для Python 2.x, есть модифицированная версия, которая будет работать как в Python 2, так и в 3, в мой ответ на еще один вопрос, если кому интересно.

Файл errorwindow.py:

# Code derived from Bryan Olson's source posted in this related Usenet discussion:
#   https://groups.google.com/d/msg/comp.lang.python/HWPhLhXKUos/TpFeWxEE9nsJ
#   https://groups.google.com/d/msg/comp.lang.python/HWPhLhXKUos/eEHYAl4dH9YJ
#
#   Here's a module to show stderr output from console-less Python
#   apps, and stay out of the way otherwise. I plan to make a ASPN
#   recipe of it, but I thought I'd run it by this group first.
#
#   To use it, import the module. That's it. Upon import it will
#   assign sys.stderr.
#
#   In the normal case, your code is perfect so nothing ever gets
#   written to stderr, and the module won't do much of anything.
#   Upon the first write to stderr, if any, the module will launch a
#   new process, and that process will show the stderr output in a
#   window. The window will live until dismissed; I hate, hate, hate
#   those vanishing-consoles-with-critical-information.
#
#   The code shows some arguably-cool tricks. To fit everthing in
#   one file, the module runs the Python interpreter on itself; it
#   uses the "if __name__ == '__main__'" idiom to behave radically
#   differently upon import versus direct execution. It uses TkInter
#   for the window, but that's in a new process; it does not import
#   TkInter into your application.
#
#   To try it out, save it to a file -- I call it "errorwindow.py" -
#   - and import it into some subsequently-incorrect code. For
#   example:
#
#        import errorwindow
#
#        a = 3 + 1 + nonesuchdefined
#
#   should cause a window to appear, showing the traceback of a
#   Python NameError.
#
#   --
#   --Bryan
#   ----------------------------------------------------------------
#
#   martineau - Modified to use subprocess.Popen instead of the os.popen
#               which has been deprecated since Py 2.6. Changed so it
#               redirects both stdout and stderr. Added numerous
#               comments, and also inserted double quotes around paths
#               in case they have embedded space characters in them, as
#               they did on my Windows system.

"""
    Import this module into graphical Python apps to provide a
    sys.stderr. No functions to call, just import it. It uses
    only facilities in the Python standard distribution.

    If nothing is ever written to stderr, then the module just
    sits there and stays out of your face. Upon write to stderr,
    it launches a new process, piping it error stream. The new
    process throws up a window showing the error messages.
"""
import subprocess
import sys
import thread
import os

if __name__ == '__main__':  # when spawned as separate process
    # create window in which to display output
    # then copy stdin to the window until EOF
    # will happen when output is sent to each OutputPipe created
    from Tkinter import BOTH, END, Frame, Text, TOP, YES
    import tkFont
    import Queue

    queue = Queue.Queue(100)

    def read_stdin(app, bufsize=4096):
        fd = sys.stdin.fileno()  # gets file descriptor
        read = os.read
        put = queue.put
        while True:
            put(read(fd, bufsize))

    class Application(Frame):
        def __init__(self, master=None, font_size=8, text_color='#0000AA', rows=25, cols=100):
            Frame.__init__(self, master)
            # argv[0]: name of this script (not used)
            # argv[1]: name of script that imported this module
            # argv[2]: name of redirected stream (optional)
            if len(sys.argv) < 3:
                title = "Output Stream from %s" % (sys.argv[1],)
            else:
                title = "Output Stream '%s' from %s" % (sys.argv[2], sys.argv[1])
            self.master.title(title)
            self.pack(fill=BOTH, expand=YES)
            font = tkFont.Font(family='Courier', size=font_size)
            width = font.measure(' '*(cols+1))
            height = font.metrics('linespace')*(rows+1)
            self.configure(width=width, height=height)
            self.pack_propagate(0)  # force frame to be configured size
            self.logwidget = Text(self, font=font)
            self.logwidget.pack(side=TOP, fill=BOTH, expand=YES)
            # Disallow key entry, but allow copy with <Control-c>
            self.logwidget.bind('<Key>', lambda x: 'break')
            self.logwidget.bind('<Control-c>', lambda x: None)
            self.logwidget.configure(foreground=text_color)
            #self.logwidget.insert(END, '==== Start of Output Stream ====\n\n')
            #self.logwidget.see(END)
            self.after(200, self.start_thread, ())

        def start_thread(self, _):
            thread.start_new_thread(read_stdin, (self,))
            self.after(200, self.check_q, ())

        def check_q(self, _):
            log = self.logwidget
            log_insert = log.insert
            log_see = log.see
            queue_get_nowait = queue.get_nowait
            go = True
            while go:
                try:
                    data = queue_get_nowait()
                    if not data:
                        data = '[EOF]'
                        go = False
                    log_insert(END, data)
                    log_see(END)
                except Queue.Empty:
                    self.after(200, self.check_q, ())
                    go = False

    app = Application()
    app.mainloop()

else: # when module is first imported
    import traceback
    class OutputPipe(object):
        def __init__(self, name=''):
            self.lock = thread.allocate_lock()
            self.name = name

        def __getattr__(self, attr):
            if attr == 'pipe':  # pipe attribute hasn't been created yet
                # launch this module as a separate process to display any output
                # it receives.
                # Note: It's important to put double quotes around everything in case
                # they have embedded space characters.
                command = '"%s" "%s" "%s" "%s"' % (sys.executable,                # command
                                                   __file__,                      # argv[0]
                                                   os.path.basename(sys.argv[0]), # argv[1]
                                                   self.name)                     # argv[2]

                # sample command and arg values on receiving end:
                #   E:\Program Files\Python\python[w].exe                         # command
                #   H:\PythonLib\TestScripts\PyRemindWrk\errorwindow.py           # argv[0]
                #   errorwindow.py                                                # argv[1]
                #   stderr                                                        # argv[2]

                # execute this script as __main__ with a stdin PIPE for sending output to it
                try:
                    # had to make stdout and stderr PIPEs too, to make it work with pythonw.exe
                    self.pipe = subprocess.Popen(command, bufsize=0,
                                                 stdin=subprocess.PIPE,
                                                 stdout=subprocess.PIPE,
                                                 stderr=subprocess.PIPE).stdin
                except Exception:
                    # output exception info to a file since this module isn't working
                    exc_type, exc_value, exc_traceback = sys.exc_info()
                    msg = ('%r exception in %s\n' %
                            (exc_type.__name__, os.path.basename(__file__)))
                    with open('exc_info.txt', 'wt') as info:
                        info.write('msg:' + msg)
                        traceback.print_exc(file=info)
                    sys.exit('fatal error occurred spawning output process')

            return super(OutputPipe, self).__getattribute__(attr)

        def write(self, data):
            with self.lock:
                self.pipe.write(data)  # 1st reference to pipe attr will cause it to be created

    # redirect standard output streams in the process importing the module
    sys.stderr = OutputPipe('stderr')
    sys.stdout = OutputPipe('stdout')
person martineau    schedule 06.08.2013