不必使用gobject.timeout_add();您可以只使用threading 模块,它专门用于线程,并且比尝试使用 GTK 更简单。
使用`threading`调用函数
使用threading 的好处在于,即使它不是 Gtk 模块组的一部分,它仍然不会阻塞 Gtk 的主循环。这是一个使用threading的简单超时线程的简单示例:
import threading
def function():
print("Hello!")
# Create a timer
timer = threading.Timer(
1, # Interval (in seconds; can be a float) after which to call the function
function # Function to call after time interval
)
timer.start() # Start the timer
这会在 1 秒后调用一次 function(),然后退出。
但是,在您的情况下,您希望多次调用该函数,以反复检查日志的状态。为此,您可以在 function() 中重新创建计时器,然后再次运行它:
import threading
def function():
global timer
print("Hello!")
# Recreate and start the timer
timer = threading.Timer(1, function)
timer.start()
timer = threading.Timer(1, function)
timer.start()
现在要检查日志中是否添加了新行,程序需要读取日志,然后将最近读取的行与之前读取的行进行比较。
读取行,并使用 `difflib` 比较它们
首先,要比较这些行,它们需要存储在两个列表中:一个包含最近读取的行集,另一个包含之前的行集。下面是一个读取日志输出的代码示例,将行存储在一个列表中,然后打印该列表:
from subprocess import PIPE, Popen
# The list storing the lines of the log
current_lines = []
# Read the log
p = Popen(["journalctl", "--user-unit=appToMonitor"], stdout=PIPE)
with p.stdout:
for line in iter(p.stdout.readline, b''):
current_lines.append(line.decode("utf-8")) # Add the lines of the log to the list
p.wait()
print(current_lines)
注意必须使用line.decode("uft-8"),因为p.stdout.readline的输出是以字节为单位的。使用您的程序使用的任何编码; utf-8 只是一种常见的编码方式。
然后您可以使用difflib 模块来比较这两个列表。这是一个示例程序,它比较两个行列表,并打印第二个列表中而不是第一个列表中的任何行:
import difflib
# Two lists to compare
last_lines = ["Here is the first line\n"]
current_lines = ["Here is the first line\n", "and here is the second line"]
# Iterate through all the lines, and check for new ones
for line in difflib.ndiff(last_lines, current_lines):
# Print the line only if it was not in last_lines
if line[0] == "+": # difflib inserts a "+" for every addition
print("Line added:", line.replace("+ ", "")) # Remove the "+" from the line and print it
很好,但是我们如何将所有这些都放入一个程序中?
结局:将所有这些概念放入一个程序中,该程序每秒读取日志中的输出,并将任何新行添加到窗口中的文本小部件中。下面是一个简单的 Gtk 应用程序:
import difflib
import gi
import threading
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
from subprocess import PIPE, Popen
class App(Gtk.Window):
"""The application window."""
def __init__(self):
Gtk.Window.__init__(self)
self.connect("delete-event", self.quit)
# The list of lines read from the log file
self.current_lines = []
# The list of previously read lines to compare to the current one
self.last_lines = []
# The box to hold the widgets in the window
self.box = Gtk.VBox()
self.add(self.box)
# The text widget to output the log file to
self.text = Gtk.TextView()
self.text.set_editable(False)
self.box.pack_start(self.text, True, True, 0)
# A button to demonstrate non-blocking
self.button = Gtk.Button.new_with_label("Click")
self.box.pack_end(self.button, True, True, 0)
# Add a timer thread
self.timer = threading.Timer(0.1, self.read_log)
self.timer.start()
self.show_all()
def quit(self, *args):
"""Quit."""
# Stop the timer, in case it is still waiting when the window is closed
self.timer.cancel()
Gtk.main_quit()
def read_log(self):
"""Read the log."""
# Read the log
self.current_lines = []
p = Popen(["journalctl", "--user-unit=appToMonitor"], stdout=PIPE)
with p.stdout:
for line in iter(p.stdout.readline, b''):
self.current_lines.append(line.decode("utf-8"))
p.wait()
# Compare the log with the previous reading
for d in difflib.ndiff(self.last_lines, self.current_lines):
# Check if this is a new line, and if so, add it to the TextView
if d[0] == "+":
self.text.set_editable(True)
self.text.get_buffer().insert_at_cursor(d.replace("+ ", ""))
self.text.set_editable(False)
self.last_lines = self.current_lines
# Reset the timer
self.timer = threading.Timer(1, self.read_log)
self.timer.start()
if __name__ == "__main__":
app = App()
Gtk.main()
按钮表明threading在等待时不会阻塞执行;即使程序正在读取日志,您也可以随意点击它!