在Python的tkinter中自动调整大小

时间:2011-02-17 23:05:38

标签: python text tkinter resize

在下面的方法中,我尝试创建一个框架,在其中放置一个标签和文本小部件,并将它们放在另一个文本小部件中。结果有两个问题。应该怎样改为:

  1. 内部文本对象是否具有基于插入文本的正确高度?
  2. 获取框架和文本以调整外部窗口小部件的当前尺寸?
  3. 建议将不胜感激!要使消息在代码中按预期显示有些困难。当主要窗口小部件被拉伸时,它们应该自动换行并调整大小。

    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 :(得分:3)

.grid方法对我来说一直是一个麻烦,让我正确调整大小/拉伸。

对于您的代码,我会将.grid调用更改为以下.pack调用:

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

然后您也可以删除.grid_ {row,column}配置调用。

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

答案 1 :(得分:2)

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

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

相关问题