2011-02-17 8 views
2

Dans la méthode suivante, j'essaie de créer un cadre, y mettre une étiquette et un widget de texte, et les placer à l'intérieur d'un autre widget de texte. Il y a deux problèmes avec les résultats. Comment devrait-il être changé en:Redimensionnement automatique dans le tkinter de Python

  1. Les objets de texte interne ont-ils la bonne hauteur en fonction du texte inséré?
  2. Obtenez le cadre et le texte à redimensionner aux dimensions actuelles du widget externe?

Des suggestions seraient appréciées! Il est quelque peu difficile d'afficher les messages comme prévu dans le code. Ils sont censés être automatiquement enveloppés et redimensionnés lorsque le widget principal est étiré.

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) 

Le code est censé générer une trame avec une étiquette à côté de celui-ci. Chaque nouveau message affiché doit être placé au-dessus des messages les plus anciens et, à mesure que la liste des messages augmente, il devrait être possible de faire défiler et de lire les messages plus anciens (indéfiniment). Malheureusement, cela ne fonctionne pas mieux que le code ci-dessus.

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) 

Le cadre semble être correctement configuré, mais obtenir la zone de texte externe pour agir comme un gestionnaire de géométrie et définissant la propriété de hauteur de la zone de texte intérieur semblent être les principaux problèmes ici. La zone de texte externe ne redimensionne pas actuellement le cadre, et je ne suis pas sûr du code à écrire pour redimensionner la hauteur de la zone de texte interne en fonction de la quantité de texte à l'intérieur de celle-ci. Voici le code complet du programme:

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

Répondre

2

Votre description est difficile à comprendre. Êtes-vous en train de dire que même si vous placez le combo frame/text dans un autre widget de texte, vous voulez que le combo de widget frame/text se développe et se rétrécisse pour s'adapter au widget de texte externe? Si oui, pourquoi utilisez-vous un widget texte?

Il est possible que vous utilisiez le mauvais type de widget pour l'effet que vous essayez d'obtenir. Qu'est-ce que vous essayez exactement de faire qui nécessite des widgets texte imbriqués dans d'autres widgets de texte?

+0

Merci de demander les exigences! Ils ont été ajoutés ci-dessus. –

+0

Il est clair que la question doit être re-posée que les demandes d'aide avec des problèmes plus petits. –

3

Les méthodes .grid ont toujours été un peu compliquées pour que je puisse redimensionner/étirer correctement.

Pour votre code, je changerais les appels .grid aux appels .pack suivants:

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

Vous pouvez alors déposer votre .grid_ {ligne, colonne} configure appelle ainsi.

Votre widget __text est-il correctement redimensionné? S'il ne se redimensionne pas, il ne permettra pas non plus à ce widget d'être redimensionné.

+0

Y at-il un meilleur titre pour cette question que vous pourriez penser? Et oui: le widget texte est en train de se redimensionner très bien. Voulez-vous le code entier? –

+0

J'irais avec quelque chose de spécifique au redimensionnement ou à l'expansion des widgets dans votre titre, car cela semble être le plus gros problème. Si vous pouvez poster le code avec le reste de la création de widget et la gestion de la disposition, ce serait génial. – korhadris

Questions connexes