在使用Traits 4构建简单的交互式GUI应用程序时,会出现这样的一个问题:该应用程序会在GUI的专用部分显示带时间戳的事件日志,该日志目前存储为字符串属性。字符串属性的默认编辑器(或视图)是一个可滚动多行显示控件。当内部字符串值发生更改时,控件会更新以显示新值。如果内容的长度超过控件的可视大小,那么就会出现一个滚动条允许用户在整个值中上下滚动。
当控件刷新时,如果垂直滚动条可见(内容超出了控件的大小),则视图重置为值(顶部)的开头,并且滚动条也返回顶部,从而隐藏了值的最后部分。
而应用中希望始终在值刷新后显示日志中的最新事件(在底部)。但是,由于视图重置为值的顶部,因此不会遵循最新条目,并且每次刷新后,用户都必须不断滚动到底部。这种形式是不可用的。
所以需要找到一种简单的方法来配置此属性的编辑器/视图,使其从底部滚动,或者了解如何编写自定义编辑器/视图。
2、解决方案
方法一:
可以使用OutputStream类,它具有文件类接口到字符串。使用其write方法可以将内容添加到字符串中。OutputStream的默认视图是多行文本字段,它具有一个处理程序,该处理程序使用适当的工具包方法在字符串更改时将光标移动到字符串的末尾。
方法二:
可以修改处理程序来强制文本字段只读,同时仍然使用样式=“custom”。修改后,可以在Qt后端强制文本字段只读,同时仍然使用样式=“custom”。
方法三:
自己编写一个自定义的编辑器/视图。可以从头开始使用wx / qt4基元编写一个新视图,也可以从现有视图派生一个新视图,并仅覆盖实现所需功能的必要部分。
以下是一些示例代码,演示了如何使用OutputStream类来解决此问题:
from traits.api import HasTraits, Instance, String
from traitsui.api import View, Item
import threading
from time import sleep
class OutputStream(HasTraits):
text = String()
view = View(Item('text', show_label=False, springy=True, style='custom'))
class CaptureThread(threading.Thread):
def __init__(self, display):
super().__init__()
self.display = display
self.wants_abort = False
def run(self):
self.display.text = 'Camera started\n' + self.display.text
n_img = 0
while not self.wants_abort:
sleep(.5)
n_img += 1
self.display.text += '%d image captured\n' % n_img
self.display.text += 'Camera stopped\n'
class Camera(HasTraits):
start_stop_capture = Instance(ButtonEditor)
display = Instance(OutputStream)
capture_thread = Instance(CaptureThread)
view = View(Item('start_stop_capture', show_label=False))
def _start_stop_capture_fired(self):
if self.capture_thread and self.capture_thread.isAlive():
self.capture_thread.wants_abort = True
else:
self.capture_thread = CaptureThread(self.display)
self.capture_thread.start()
class MainWindow(HasTraits):
display = Instance(OutputStream, ())
camera = Instance(Camera)
def _camera_default(self):
return Camera(display=self.display)
view = View('display', 'camera', style="custom", resizable=True)
if __name__ == '__main__':
MainWindow().configure_traits()
以上代码演示了一个使用OutputStream类的简单应用程序。在应用程序中,可以单击“开始/停止捕获”按钮多次,直到视图被填满,此时观察到后续刷新将滚动条位置重置到视图的顶部。但使用OutputStream类可以解决此问题,使最新事件始终显示在底部。