2

My program gets a list of messages and displays them on a Tkinter widget. "Previous" and "Next" buttons let me read through the message list.

I can read all the messages and navigate back and forth, but the transitions make the whole window "flash", as if it is redrawn every time. This behaviour doesn't happen if I don't change the Labelframe text, but I would like to see the "Message 2 of 3" kind of information. I could leave the Labelframe text unchanged (for example "Messages") and use a separate widget to display what I want, but isn't there a way to change that Labelframe text without the whole window flashing?

Example of message

This is a minimal reproducible example:

import tkinter as tk
from tkinter import ttk
from tkinter.font import Font


class MessageViewer:
    def __init__(self, msg_list):
        self.msg_list = msg_list
        # Current message shown
        self.pos = 0
        
        self.L = len(msg_list)
        
        self.root = tk.Tk()
        self.root.title("Title here")
        f = Font(family='Courier', size=10)
        
        self.frm_buttons = ttk.Frame(self.root)
        self.frm_buttons.pack()
        
        self.frm_content = ttk.Frame(self.root, height=100, width=100)
        self.frm_content.pack()
        
        self.btn_prev = ttk.Button(self.frm_buttons, text="Previous", command=self.do_previous)
        self.btn_next = ttk.Button(self.frm_buttons, text="Next", command=self.do_next)
        self.btn_prev.pack(side='left')
        self.btn_next.pack(side='left')
        
        self.lf = ttk.Labelframe(self.frm_content, text='Message Id', height=100, width=100)
        self.lf.grid(row=0, column=0, sticky="EWNS", padx=5, pady=5)
        
        self.msg = tk.Text(self.lf, font=f)
        self.msg.insert("1.0", "Message content")
        self.msg['state'] = 'disabled'
        self.msg.grid(row=1, column=0, sticky="EWNS", padx=5, pady=5)
        
        self.update()


    def do_previous(self):
        if self.pos:
            self.pos -= 1
            self.update()


    def do_next(self):
        if self.pos == self.L - 1:
            return
        
        self.pos += 1
        self.update()


    def update(self):
        data = self.msg_list[self.pos]
        # If I comment out next line, message transition is smooth
        # Otherwise, pressing Next or Previous seems to redraw everything
        self.lf.configure(text=f'Message {self.pos + 1} of {self.L}')

        self.msg['state'] = 'normal'
        self.msg.delete("1.0", tk.END)
        self.msg.insert("1.0", f'{data[self.pos]}')
        self.msg['state'] = 'disabled'


# Messages come in this format
msg_list = [{0: "First message"},
{1: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean iaculis velit a lacinia aliquam."}, 
{2: "Last message"}]

viewer = MessageViewer(msg_list)
viewer.root.mainloop()

I am running this program with Python 3.14 on Windows.

3
  • 1
    This must be a platform-specific problem. On my mac the transition looks exactly the same with or without changing the label. Commented Oct 24 at 13:22
  • I see some strange effect on Linux - it looks like it changes size of main window to minimal and later it resize it again to expected size. It disappears when I manually change size of window or when I set some size at start - like self.root.geometry("500x600") Commented Oct 24 at 22:22
  • @furas I've tried what you suggest and it works... however, if I click "Previous" and "Next" randomly, sometimes I think I see some flashing. Not as much as I saw in my original code. The flashing I see is not in "Message X of 3", but in the Text widget. It is minimal, though. Commented Oct 25 at 9:53

2 Answers 2

3

Changing the text of a ttk.Labelframe causes it to redraw completely, which makes the window flash. To avoid that, keep the Labelframe title static and show the “Message X of Y” info in a separate Label inside the frame instead. This removes the flicker.

Sign up to request clarification or add additional context in comments.

4 Comments

Your answer could be improved by demonstrating how to implement this change. That said, it is correct!
The Label can be used directly with the option labelwidget instead of putting it inside the frame.
Can you elaborate, please?
This way, it works without the flashing. I had mentioned this possible solution in my original post. Because of what you mentioned of the complete redraw when changing the text of ttk.LabelFrame, I'll go for this solution. Thanks.
2

Using a Label widget for the text can solve the flicker issue. You can assign the label using labelwidget option of ttk.LabelFrame:

def __init__(self, msg_list):
    ...
    self.label = ttk.Label(self.frm_content)
    self.lf = ttk.LabelFrame(self.frm_content, height=100, width=100, labelwidget=self.label)
    ...

Then update the text of the label instead of the text of LabelFrame:

def update(self):
    ...
    self.label.config(text=f'Message {self.pos+1} of {self.L}')
    ...

You can refer the document on labelwidget option.

1 Comment

It works like a charm, thank you.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.