1

我正在使用 Traits 4 构建一个简单的交互式 GUI 应用程序。此应用程序将在 GUI 的专用部分显示带有时间戳的事件日志。此日志当前存储为字符串特征。

String trait 的默认编辑器(或视图?不确定确切的命名法)是一个可滚动的多行显示小部件。当内部字符串值更改时,小部件会更新以显示新值。如果内容的长度超过了小部件的可视大小,则会出现一个滚动条,允许用户在整个值上上下滚动。

似乎当小部件刷新并且垂直滚动条可见(内容超过小部件大小)时,视图重置到值的开头(顶部),并且滚动条也返回到顶部,遮盖了值的最后部分.

在我的应用程序中,我希望日志中的最新事件(在底部)始终在值刷新后显示。但是由于视图重置到值的顶部,它不会跟随最新的条目,并且用户必须在每次刷新后不断滚动到底部。这在这种形式中是不可用的。

有没有一种简单的方法可以将此特征的编辑器/视图配置为从底部滚动?

如果没有,如何为这个 String trait 编写自定义编辑器/视图?是否有必要使用 wx/qt4 原语从头开始编写新视图,或者是否有某种方法可以从现有视图派生新视图并仅覆盖实现所需功能所需的部分?

这是一些演示该问题的示例代码:

# from https://svn.enthought.com/enthought/ticket/1619 - broken SSL cert
from threading import Thread
from time import sleep
from enthought.traits.api import *
from enthought.traits.ui.api import View, Item, ButtonEditor

class TextDisplay(HasTraits):
    string =  String()

    view= View( Item('string',show_label=False, springy=True, style='custom' ))


class CaptureThread(Thread):
    def run(self):
        self.display.string = 'Camera started\n' + self.display.string
        n_img = 0
        while not self.wants_abort:
            sleep(.5)
            n_img += 1
            self.display.string += '%d image captured\n' % n_img
        self.display.string += 'Camera stopped\n'

class Camera(HasTraits):
    start_stop_capture = Button()
    display = Instance(TextDisplay)
    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.capture_thread.wants_abort = False
            self.capture_thread.display = self.display
            self.capture_thread.start()

class MainWindow(HasTraits):
    display = Instance(TextDisplay, ())

    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()

多次单击“开始停止捕获”按钮,直到视图填满,您会观察到后续刷新会将滚动条位置重置到视图顶部。

4

1 回答 1

2

几年前我需要类似的东西。你可以在这里找到我想出的: https ://svn.enthought.com/enthought/wiki/OutputStream

该类OutputStream对字符串有一个类似文件的接口。特别是,您使用它的write方法添加到字符串中。an 的默认视图OutputStream是多行文本字段。它有一个处理程序,该处理程序使用适当的工具包方法将光标移动到字符串的末尾,只要它被更改。wiki 页面中给出了其使用的两个不同演示output_stream_demo.py和。output_stream_demo2.py

您可能希望enthought在导入中删除命名空间。也就是改变

from enthought.traits.api import ...
from enthought.traits.ui.api import ...
from enthought.etsconfig.api import ETSConfig

from traits.api import ...
from traitsui.api import ...
from traits.etsconfig.api import ETSConfig

更新,以解决评论:

TextEditor显然,Qt 后端 a的“只读”样式使用 aQLabel作为文本字段而不是 a QTextEdit,并且 aQLabel不提供该moveCursor方法。对处理程序的以下修改提供了一种在仍使用style="custom".

def _get_editor(uiinfo, name):
    ui = uiinfo.ui
    if ui is None:
        return None
    for ed in ui._editors:
        if ed.name == name:
            return ed
    return None


class _OutputStreamViewHandler(Handler):

    def init(self, uiinfo):
        if ETSConfig.toolkit == 'qt4':
            ed = _get_editor(uiinfo, 'text')
            if ed is not None:
                # Make the text field read-only.
                ed.control.setReadOnly(True)
        return True

    def object_text_changed(self, uiinfo):
        ed = _get_editor(uiinfo, 'text')
        if ed is None:
            return

        if ETSConfig.toolkit == 'wx':
            # With wx, the control is a TextCtrl instance.
            ed.control.SetInsertionPointEnd()
        elif ETSConfig.toolkit == 'qt4':
            # With qt4, the control is a PyQt4.QtGui.QTextEdit instance.
            from PyQt4.QtGui import QTextCursor
            ed.control.moveCursor(QTextCursor.End)
于 2013-11-24T23:59:58.123 回答