2011-02-17 289 views
2

在下面的方法中,我尝试创建一个框架,在其中放置一个标签和文本小部件,并将它们放置在另一个文本小部件中。结果有两个问题。它应该如何更改为:在Python的tkinter中自动调整大小

  1. 根据插入的文本,内部文本对象是否具有正确的高度?
  2. 获取框架和文本的大小调整到外部窗口小部件的当前尺寸?

建议将不胜感激!让消息在代码中按预期显示有点困难。他们应该自动换行并调整大小,因为主小部件会被拉长。

def display(self, name, message): 
    frame = tkinter.ttk.Frame(self.__text, borderwidth=1) 
    frame.grid_rowconfigure(0, weight=1) 
    frame.grid_columnconfigure(1, weight=1) 
    name = tkinter.ttk.Label(frame, text=name) 
    name.grid(row=0, column=0) 
    text = tkinter.Text(frame, wrap=tkinter.WORD, height=1) 
    text.grid(row=0, column=1, sticky=tkinter.EW) 
    text.insert('1.0', message) 
    text.configure(state=tkinter.DISABLED) 
    self.__text.window_create('1.0', window=frame, stretch=tkinter.TRUE) 

该代码应该生成一个带有标签的框架以及旁边的文字包装文本。每个正在显示的新消息都应该位于旧消息的顶部,随着消息列表的增长,应该可以滚动并阅读旧消息(无限期地)。不幸的是,这不能比上面的代码更好。

def display(self, name, message): 
    frame = tkinter.ttk.Frame(self.__text, borderwidth=1, relief='solid') 
    name = tkinter.ttk.Label(frame, text=name) 
    text = tkinter.Text(frame, wrap=tkinter.WORD, height=1) 
    frame.pack(expand=tkinter.TRUE, fill=tkinter.BOTH) 
    name.pack(fill=tkinter.BOTH, side=tkinter.LEFT) 
    text.pack(expand=tkinter.TRUE, fill=tkinter.BOTH) 
    text.insert('1.0', message) 
    text.configure(state=tkinter.DISABLED) 
    self.__text.window_create('1.0', window=frame) 

框架似乎是正确配置,但得到外文本框表现得像一个几何管理器和设置内部文本框的高度属性出现在这里是主要的问题。外部文本框当前没有调整框架的大小,我不确定要编写什么代码来根据内部文本的大小调整内部文本框的高度。这里是程序的完整代码:

import tkinter 
import tkinter.ttk 

import datetime 
import getpass 
import os 
import uuid 

################################################################################ 

class DirectoryMonitor: 

    def __init__(self, path): 
     self.__path = path 
     self.__files = {} 

    def update(self, callback): 
     for name in os.listdir(self.__path): 
      if name not in self.__files: 
       path_name = os.path.join(self.__path, name) 
       self.__files[name] = FileMonitor(path_name) 
     errors = set() 
     for name, monitor in self.__files.items(): 
      try: 
       monitor.update(callback) 
      except OSError: 
       errors.add(name) 
     for name in errors: 
      del self.__files[name] 


################################################################################ 

class FileMonitor: 

    def __init__(self, path): 
     self.__path = path 
     self.__modified = 0 
     self.__position = 0 

    def update(self, callback): 
     modified = os.path.getmtime(self.__path) 
     if modified != self.__modified: 
      self.__modified = modified 
      with open(self.__path, 'r') as file: 
       file.seek(self.__position) 
       text = file.read() 
       self.__position = file.tell() 
      callback(self.__path, text) 

################################################################################ 

class Aggregator: 

    def __init__(self): 
     self.__streams = {} 

    def update(self, path, text): 
     if path not in self.__streams: 
      self.__streams[path] = MessageStream() 
     parts = text.split('\0') 
     assert not parts[-1], 'Text is not properly terminated!' 
     self.__streams[path].update(parts[:-1]) 

    def get_messages(self): 
     all_messages = set() 
     for stream in self.__streams.values(): 
      all_messages.update(stream.get_messages()) 
     return sorted(all_messages, key=lambda message: message.time) 

################################################################################ 

class MessageStream: 

    def __init__(self): 
     self.__name = None 
     self.__buffer = None 
     self.__waiting = set() 

    def update(self, parts): 
     if self.__name is None: 
      self.__name = parts.pop(0) 
     if self.__buffer is not None: 
      parts.insert(0, self.__buffer) 
      self.__buffer = None 
     if len(parts) & 1: 
      self.__buffer = parts.pop() 
     for index in range(0, len(parts), 2): 
      self.__waiting.add(Message(self.__name, *parts[index:index+2])) 

    def get_messages(self): 
     messages = self.__waiting 
     self.__waiting = set() 
     return messages 

################################################################################ 

class Message: 

    def __init__(self, name, timestamp, text): 
     self.name = name 
     self.time = datetime.datetime.strptime(timestamp, '%Y-%m-%dT%H:%M:%SZ') 
     self.text = text 

################################################################################ 

class MessageWriter: 

    def __init__(self, path, name): 
     assert '\0' not in name, 'Name may not have null characters!' 
     self.__name = str(uuid.uuid1()) 
     self.__path = os.path.join(path, self.__name) 
     with open(self.__path, 'w') as file: 
      file.write(name + '\0') 

    @property 
    def name(self): 
     return self.__name 

    def write(self, text): 
     assert '\0' not in text, 'Text may not have null characters!' 
     timestamp = datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%SZ') 
     with open(self.__path, 'a') as file: 
      file.write(timestamp + '\0' + text + '\0') 

################################################################################ 

class Logos(tkinter.ttk.Frame): 

    @classmethod 
    def main(cls, path): 
     tkinter.NoDefaultRoot() 
     root = tkinter.Tk() 
     root.title('Logos 2.0') 
     root.minsize(320, 240) # QVGA 
     view = cls(root, path) 
     view.grid(row=0, column=0, sticky=tkinter.NSEW) 
     root.grid_rowconfigure(0, weight=1) 
     root.grid_columnconfigure(0, weight=1) 
     root.mainloop() 

    def __init__(self, master, path, **kw): 
     super().__init__(master, **kw) 
     self.configure_widgets() 
     self.__writer = MessageWriter(path, getpass.getuser()) 
     self.__monitor = DirectoryMonitor(path) 
     self.__messages = Aggregator() 
     self.after_idle(self.update) 

    def configure_widgets(self): 
     # Create widgets. 
     self.__text = tkinter.Text(self, state=tkinter.DISABLED) 
     self.__scroll = tkinter.ttk.Scrollbar(self, orient=tkinter.VERTICAL, 
               command=self.__text.yview) 
     self.__entry = tkinter.ttk.Entry(self, cursor='xterm') 
     # Alter their settings. 
     self.__text.configure(yscrollcommand=self.__scroll.set) 
     # Place everything on the grid. 
     self.__text.grid(row=0, column=0, sticky=tkinter.NSEW) 
     self.__scroll.grid(row=0, column=1, sticky=tkinter.NS) 
     self.__entry.grid(row=1, column=0, columnspan=2, sticky=tkinter.EW) 
     self.grid_rowconfigure(0, weight=1) 
     self.grid_columnconfigure(0, weight=1) 
     # Setup box for typing. 
     self.__entry.bind('<Control-Key-a>', self.select_all) 
     self.__entry.bind('<Control-Key-/>', lambda event: 'break') 
     self.__entry.bind('<Return>', self.send_message) 
     self.__entry.focus_set() 

    def select_all(self, event): 
     event.widget.selection_range(0, tkinter.END) 
     return 'break' 

    def send_message(self, event): 
     text = self.__entry.get() 
     self.__entry.delete(0, tkinter.END) 
     self.__writer.write(text) 

    def update(self): 
     self.after(1000, self.update) 
     self.__monitor.update(self.__messages.update) 
     for message in self.__messages.get_messages(): 
      self.display(message.name, message.text) 

    def display(self, name, message): 
     frame = tkinter.ttk.Frame(self.__text, borderwidth=1, relief='solid') 
     name = tkinter.ttk.Label(frame, text=name) 
     text = tkinter.Text(frame, wrap=tkinter.WORD, height=1) 
     name.grid(row=0, column=0) 
     text.grid(row=0, column=1, sticky=tkinter.EW) 
     frame.grid_rowconfigure(0, weight=1) 
     frame.grid_columnconfigure(1, weight=1) 
     text.insert('1.0', message) 
     text.configure(state=tkinter.DISABLED) 
     self.__text.window_create('1.0', window=frame) 

################################################################################ 

if __name__ == '__main__': 
    Logos.main('Feeds') 

回答

2

你的描述很难理解。你是说即使你将框架/文本组合放置在另一个文本小部件中,你希望框架/文本小部件组合可以增长和缩小以适应外部文本小部件吗?如果是这样,你为什么使用文本小部件?

您可能正在使用错误类型的小部件来实现您要实现的效果。你究竟想要做什么,需要嵌套在其他文本小部件中的文本小部件?

+0

感谢您的要求!它们已经被添加到上面。 – 2011-02-18 00:30:28

+0

很明显,需要重新提出这个问题,即请求帮助处理较小的问题。 – 2011-02-18 16:28:43

3

.grid方法对我来说正确调整/拉伸一直有点麻烦。

为您的代码,我会改变.grid调用以下.pack电话:

frame.pack(expand=1, fill='both') 
name.pack(fill='both', side='left') 
text.pack(expand=1, fill='both') 

然后,您可以减少您的.grid_ {行,列}配置要求也是如此。

您的__text小部件是否正确调整大小?如果它没有调整大小,它将不允许这个框架小部件调整大小。