Tkinter Canvas Flicker - python

I'm drawing small black and white video frames to a Tkinter canvas using this code (at 10Hz)
self.image.buf = bytearray(header.width * header.height);
self.image.buf[:] = image
self.image.im = Image.frombuffer("L", (header.width, header.height), self.image.buf).resize((320, 240)).transpose(Image.ROTATE_180)
self.image.tkimage = ImageTk.PhotoImage(self.image.im)
if (self.image.id): self.image.delete(self.image.id);
self.image.id = self.image.create_image((0, 0), image=self.image.tkimage, anchor=NW)
Everytime a frame gets drawn, the widget flickers. Isn't the Tk canvas supposed to be double buffered? what can I do to avoid this?

So i figured out the problem -- it seems you have to create your tkimage from the same thread that tk is running in or bad things happen. Thanks to anyone who looked at this!

I don't think there's enough detail in your question to say for certain what the problem is. It's possible to swap images in and out without flicker and your code doesn't look too unusual, so there may be something else in your code that is causing the problem.
Here's one thing to try: instead of deleting and re-creating the canvas item each iteration, try using one canvas item that you reconfigure to use the new image using the itemconfig method.
For example:
if self.image.id is None:
self.image.id = self.image.create_image(...)
else:
self.image.itemconfig(self.image.id, image=self.image.tkimage)
Also, if you're not using the canvas for anything else you might want to consider using a label widget rather than a canvas and an image item.

Related

Tkinter in Python - Remove widget from active window

I'm trying to remove a Tkinter progress bar widget from an active window (after the GUI window using Tkinter has been initialized). I'm using a Tkinter Frame for my window. I've initialized the progress bar as pb, as below.
pb = ttk.Progressbar(root,orient ="horizontal",length = 540, mode ="determinate")
And then I've tried two different methods to get rid of the progress bar. The line below causes the window to freeze and stop responding when I try to use it after the GUI is initialized.
pb.pack_forget()
The line below causes only a middle section of the progress bar to disappear, but you can still see the two sides of it.
pb.destroy()
Is there any way I could get this widget to disappear after the Frame has been initialized?
The specific answer to your question is that pack_forget, grid_forget or grid_remove are what you want if you want to make a widget temporarily invisible. Which one you choose depends on if you're using grid or pack, and whether or not you want grid to remember where it was so you can later put it back in the same spot.
destroy is what you want to call if you want to literally destroy the widget.
When used properly, none of those methods will cause your program to freeze. Without seeing your code, it's impossible to know what the root cause of the problem is.
Sorry for my bad English.
This code worked for me. I simply follow Oakley's instruction.
def progressBar(*args, **kwargs):
def progress(currentValue):
progressbar["value"] = currentValue
maxValue = 100
progressbar = ttk.Progressbar((kwargs), orient="horizontal", length=150, mode="determinate", takefocus=True)
progressbar.pack(side=tk.BOTTOM)
currentValue = 0
progressbar["value"] = currentValue
progressbar["maximum"] = maxValue
divisions = 10
for i in range(divisions):
currentValue = currentValue + 10
progressbar.after(500, progress(currentValue))
progressbar.update() # Force an update of the GUI
progressbar.destroy()
Hence I simply tried progressbar.destroy() outside of the loader loop. So after
complete the loading, it will disappear from the main App window.
Thank you, Bryan Oakley sir.

Python: Tkinter: How to change the windows border color?

Every result I've gotten from searching this question up has to do with changing the border color for Tkinter widget's WITHIN the main app window, or changing the background color and stuff.
What I'm looking to do is actually change the windows border color itself. Which I have marked in this picture here
I read some stuff concerning 'Styles' but it didn't seem to be what I was looking for.
Can anyone help point me to a tutorial for changing this, or explain how it can be done?
Or if there is a question that asked exactly the same question I was asking that is answered point me to it.
As I see you are using windows.
This color is set by the theme you are currently using. It is the same for every window.
So I cross out the possibility of only using the Tkinter module for this.
Tkinter is responsible for what is in the window but the window manager decides about the border. For example in Ubuntu the window would look totally different.
I guess, you would need some windows specific calls for that.
You can remove the border with root.overrideredirect(1) if I remember correctly.
PS: put "windows" into the tags of this question.
Hi I was looking for this answer too, found it after like 80 minutes searching, Only work, to activate DWMWA_USE_IMMERSIVE_DARK_MODE
found it here: Can I change the title bar in Tkinter?
I didnt found dwmwindowattribute in dwmwindowattribute that affect Top bar color sadly :(.
For border colour:
It should be possible to call DWMWA_BORDER_COLOR, but honestly I dont know how, there is some article calling it in C++ here:
change-the-color-of-the-title-bar-caption-of-a-win32-application
Tried this but doesnt work:
set_window_attribute(hwnd, 22, '0x000000FF', 4)
Here is working code for pure black top:
import tkinter as tk
import ctypes as ct
def dark_title_bar(window):
"""
MORE INFO:
https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute
"""
window.update()
set_window_attribute = ct.windll.dwmapi.DwmSetWindowAttribute
get_parent = ct.windll.user32.GetParent
hwnd = get_parent(window.winfo_id())
value = 2
value = ct.c_int(value)
set_window_attribute(hwnd, 20, ct.byref(value),
4)
root = tk.Tk()
root.title("Crystaly Ball")
root.geometry("1400x900")
root.configure(background="#222246")
dark_title_bar(root)
root.mainloop()

wxPython: Is there a way to change the colour of the Scrollbar in a wx.ScrolledWindow?

I've found that if I make a wx.ScrollBar directly, I can modify its background color via the SetBackgroundColour() function. However, I'm unsure of how to reference to Scrollbar when it's part of a wx.ScrolledWindow.
How do I get reference to the Scrollbar in a ScrolledWindow?
A second question is this, is there a way to change more than just the Background color on a Scrollbar? I'm attempting to implement a "nighttime" mode, and thus want to mute all the colors a bit. Doing this in the rest of the program was fairly straight forward, but the scrollbars remain a problem.
You can get the reference to each ScrollBar (one horizontal and one vertical) by querying scrolledwindow.GetChildren() like:
sw = wx.ScrolledWindow(self)
for child in sw.GetChildren():
if isinstance(child, wx.ScrollBar):
print child
child.SetBackgroundColour(...)
Regarding your second question, there's SetForegroundColour(). You can always look through the documentation by doing:
python
>>> import wx
>>> help(wx.ScrollBar)

python tkinter: how to work with pixels?

using google (and this site) i have seen some similar questions but my problem is still here:
"i want to draw an image (without reading a file) , being able to manipulate every single pixel's colour in that image."
i have seen another question where was suggested to do something like this:
from tkinter import *
A=Tk()
B=Canvas(A)
B.place(x=0,y=0,height=256,width=256)
for a in range(256):
for b in range(256):
B.create_line(a,b,a+1,b+1,fill=pyList[a][b])#where pyList is a matrix of hexadecimal strings
A.geometry("256x256")
mainloop()
in fact this answers my question but... it is extremely slow.
what should i do with a 1920x1080 image ? wait for my death?
so i am asking something to perform the same as the above code but in a faster way
i have found a way to improve the method suggested by jsbueno , it is explained in the page linked :
Why is Photoimage put slow?
It is indeed tricky --
I thought you had to use a Canvas widget, but that has no access to Pixels either.
Image items embedded in the Canvas do have, though. The Tkinter.PhotoImage class
does have a "put" method that accepts a color in hex format and pixel coordinates:
from tkinter import Tk, Canvas, PhotoImage, mainloop
from math import sin
WIDTH, HEIGHT = 640, 480
window = Tk()
canvas = Canvas(window, width=WIDTH, height=HEIGHT, bg="#000000")
canvas.pack()
img = PhotoImage(width=WIDTH, height=HEIGHT)
canvas.create_image((WIDTH/2, HEIGHT/2), image=img, state="normal")
for x in range(4 * WIDTH):
y = int(HEIGHT/2 + HEIGHT/4 * sin(x/80.0))
img.put("#ffffff", (x//4,y))
mainloop()
The good news is that even it being done this way, the updates are "live":
you set pixels on the image, and see them showing up on screen.
This should be much faster than the way drawing higher level lines on screen -
but for lots of pixels it still will be slow, due to a Python function call needed for
every pixel. Any other pure python way of manipulating pixels directly will suffer from that - the only way out is calling primitives that manipulate several pixels at a time in native code from your Python code.
A nice cross-platform library for getting 2d drawing, however poorly documented as well
is Cairo - it would should have much better primitives than Tkinter's Canvas or PhotoImage.
Don't forget to save a reference after canvas.create_image. In some cases, especially when working with the PIL module, python will garbage-collect the image, even though it is being displayed!
Syntax is something like
canvas.create_image((WIDTH/2, HEIGHT/2), image=img)
canvas.image = img

tkinter LabelFrame not attatching widgets

I am running into a problem with a tkinter program, I have the LabelFrame grouping a set of labels and entries, however, it is not grouping my widgets. My code for the LabelFrame is as follows:
(edit: i managed to get the Label to display, however, it is not grouping my widgets.)
root=Tk()
message_frame=LabelFrame(root,text="testing",padx=0,pady=0,width=100,height=100).grid(padx=5,pady=10)
message_label=Label(message_frame,text="Message").grid(row=1,column=0,sticky=W)
pub_label=Label(message_frame,text="Public Key").grid(row=2,column=0,sticky=W)
priv_label=Label(message_frame,text="Public Key").grid(row=3,column=0,sticky=W)
message_entry=Entry(message_frame,textvariable=message,width=50).grid(row=1,column=1,sticky=W)
pub_entry=Entry(message_frame,textvariable=pub_key,width=50).grid(row=2,column=1,sticky=W)
priv_entry=Entry(message_frame,textvariable=private_key,width=50).grid(row=3,column=1,sticky=W)
In Tkinter, the typical workflow is to create a widget and then place it using some geometry manager on two separate lines.
If I'm not mistaken, the .grid method on Tkinter widgets returns None. So if you print message_frame right after you create it, you will probably see that it is None. When you use that passed to the next widgets, they assume you want to put it on the root widget...
The easy fix is to do something like:
message_frame=LabelFrame(root,text="testing",padx=0,pady=0,width=100,height=100)
message_frame.grid(row=0,column=0)
And you probably want to do the same with all the widgets since I doubt you actually want pub_label = priv_label = None ...
Give the frame some size attributes:
from Tkinter import *
root = Tk()
message_frame = LabelFrame(root,text="testing",padx=0,pady=0,width=100,height=100).grid(row=0,column=0,padx=5,pady=10)
Once the width and height are defined, the frame shows up fine.
If you post some of your frame's contents, it might make it clearer if this is not the issue.

Categories

Resources