Redo Not Working on Windows for Tkinter Text Widget - python

I'm sorry if this is a stupid question, but I'm not sure what I am doing wrong here. The undo and redo worked perfectly fine on Linux but it will not work on Windows. I tried to create a binding manually (as seen below) but that also didn't solve the problem. I try this exact code below and the redo command does not work. I have researched the problem, but I only find the binding solution, which I have already tried.
import tkinter as tk
class TextThing():
def __init__(self, master):
self.text = tk.Text(undo=True, maxundo=-1, autoseparators=True)
self.bindings()
self.text.pack()
def bindings(self):
self.text.bind('<Control-Shift-z>', self.text.edit_redo)
if __name__ == '__main__':
master = tk.Tk()
text_thing = TextThing(master)
master.mainloop()

You need to bind "Control-Shift-Z" (uppercase "Z") because if the shift key is pressed you get an upper case "Z" not a lower case "z".

Related

page GUI - Combobox pass variable

New to GUI. Not quite getting there. I used page and get can get buttons to do something (click on a button and get a response). With Combobox, I can't pass a value. Searched here, tried many things, watched a few hours of youtube tutorials.
What am I doing wrong below? This is the code page generates (basically) then I added what I think I need to do to use the Combobox.
I am just trying to have 1,2,3 in a combo box and print out the value that is chosen. Once I figure that out I think I can actually make a simple GUI that passes variables I can then program what I want to do with these variables being selected.
class New_Toplevel_1:
def __init__(self, top):
self.box_value = StringVar()
self.TCombobox1 = ttk.Combobox(textvariable=self.box_value)
self.TCombobox1.place(relx=0.52, rely=0.38, relheight=0.05, relwidth=0.24)
self.TCombobox1['values']=['1','2','3']
self.TCombobox1.configure(background="#ffff80")
self.TCombobox1.configure(takefocus="")
self.TCombobox1.bind('<<ComboboxSelected>>',func=select_combobox)
def select_combobox(self,top=None):
print 'test combo ' # this prints so the bind works
self.value_of_combo = self.ttk.Combobox.get() # this plus many other attempts does not work
It's hard to know what you're actually asking about, since there is more than one thing wrong with your code. Since you say the print statement is working, I'm assuming the only problem you have with your actual code is with the last line.
To get the value of the combobox, get the value of the associated variable:
self.value_of_combo = self.box_value.get()
Here's a working version where I fixed the other things that were wrong with the program:
from tkinter import *
from tkinter import ttk
class New_Toplevel_1:
def __init__(self, top):
self.box_value = StringVar()
self.TCombobox1 = ttk.Combobox(textvariable=self.box_value)
self.TCombobox1.place(relx=0.52, rely=0.38, relheight=0.05, relwidth=0.24)
self.TCombobox1['values']=['1','2','3']
self.TCombobox1.configure(background="#ffff80")
self.TCombobox1.configure(takefocus="")
self.TCombobox1.bind('<<ComboboxSelected>>',func=self.select_combobox)
def select_combobox(self,top=None):
print('test combo ') # this prints so the bind works
self.value_of_combo = self.box_value.get()
print(self.value_of_combo)
root = Tk()
top = New_Toplevel_1(root)
root.mainloop()
Note: I strongly advise you not to start with place. You should try to learn pack and place first. I know place seems easier, but to get the most responsive and flexible GUI you should leverage the power of pack and grid.

Accessing variable inside method, python tkinter

I'd like to preface by saying I've read the docs and I guess I'm not sure how to use the provided information, or I'm just not capable of seeing the difference between the provided code and my code. I've also searched on google for relevant issues to no avail. I'm currently following this tutorial
I've made a tkinter window for a simple program I'm working on, but now I'm trying to put it in a class and everything's going downhill. The basic structure of the code in question is as follows:
from tkinter import *
class WindowManager(Tk):
def __init__(self, parent):
Tk.__init__(self, parent)
self.parent = parent
# Place items for console log
self.initialize()
def initialize(self):
self.grid()
self.text_item = Text(self, bd=0, bg="black", fg="white", height="15", width="56", )
self.text_item.grid(column=0, row=0)
def access_text(app):
app.initialize.text_item.delete('1.0', END)
def main():
app = WindowManager(None)
app.title("Main Window")
app.geometry("400x250")
app.resizable(0, 0)
app.mainloop()
access_text(app)
if __name__ == "__main__":
main()
Now this is just the basic structure of the code in question, and everything that's relevant.
An error is being thrown when the window closes saying "'function' object has no attribute 'text_item'"
My best guess is that it's trying to do something with functions in general and not accessing the code found within the function in question, but I'm not sure what the proper way to access this variable is.
Any help or clarification would be much appreciated. Thank you for your time.
My issue was I was trying to access the variable from the method when it was a local variable in the class
Instead of app.initialize.text_item I should have just had app.text_item.
Credit to #Gribouillis

How to auto-activate a tkinter simpledialog pop-up window?

I have this function inside one of my python scripts which throws up a Tkinter simple dialog screen to ask for some simple user-input. The function works. However, there are 2 problems with it.
It opens up two windows, while all I need is one. But if I remove the master = Tk() I get the error:
AttributeError: 'NoneType' object has no attribute 'winfo_viewable'
It would be nice to at one point figure that one out, but my main problem however is the second one:
Whenever the simple dialog screen turns up, I have to click it first before it gets activated, which is annoying. To fix it I tried the solutions offered here and here but they do not work. The first link didn't do anything for me at all, the second link helped me to lift the master.Tk() window to the front, but that is not what I need. I need the simple dialog window to become the topmost window and I need it to be auto-activated so that when I run my code and the screen pops-up I can automatically type in it without having to click on it first.
Any help would be greatly appreciated!
My code:
def getUser():
master = Tk()
newList2=str(newList).replace(", ","\n")
for ch in ['[',']',"'"]:
if ch in newList2:
newList5=newList2.replace(ch,"")
userNr=simpledialog.askinteger("Enter user number", newList2)
chosenUsernr= userNr - 1
global chosenUsernrdef
chosenUsernrdef = chosenUsernr
master.destroy()
I don't think there is a way to lift/give focus to it but askinteger is merely a combination of couple widgets so you can easily recreate it yourself.
import tkinter as tk
from tkinter import messagebox
class CustomAskInteger(tk.Tk):
def __init__(self, numbers):
tk.Tk.__init__(self)
self.value = None
self.label = tk.Label(self, text=", ".join(map(str, numbers))).pack(fill="both", expand=True)
self.entry = tk.Entry(self)
self.button = tk.Button(self, text="Ok", command=self.get_number)
self.entry.pack()
self.button.pack()
def get_number(self):
"""
You can customize these error handlings as you like to
"""
if self.entry.get():
try:
int(self.entry.get())
self.value = self.entry.get()
self.destroy()
except ValueError:
messagebox.showwarning("Illegal Value", "Not an integer.\nPlease try again.")
else:
messagebox.showwarning("Illegal Value", "Not an integer.\nPlease try again.")
To use this in your code, you can do
def getUser():
newList2=str(newList).replace(", ","\n")
askInteger = CustomAskInteger("Enter user number", newList2)
#since it is a Tk() instance, you can do lift/focus/grab_set etc. on this
askInteger.lift()
askInteger.mainloop()
userNr = askInteger.value
First, credits to Lafexlos for showing a solution of how to apply .lift() and similar commands on a Tkinter simpledialog.askinteger() window by recreating such a window as a Tk() instance.
For those however looking how to automatically activate a Tk-window (so you do not have to click on it before being able to type in it), there appear to be multiple options.
The most common solution seems to be to use .focus() or .force_focus() as seen implemented here and here. However over here it seems those options may not work on (at least some versions of) Windows OS. This question shows a possible solution for those systems. Also, the previous solutions appear not to work on multiple versions of OS X. Based on the solution offered here, using Apple's osascript, I was able to solve my problem.
The working code eventually looks like this:
def getUser():
master = Tk()
newList2=str(newList).replace(", ","\n")
for ch in ['[',']',"'"]:
if ch in newList2:
newList2=newList2.replace(ch,"")
cmd = """osascript -e 'tell app "Finder" to set frontmost of process "Python" to true'"""
def stupidtrick():
os.system(cmd)
master.withdraw()
userNr=simpledialog.askinteger("Enter user number", newList2)
global chosenUsernrdef
chosenUsernr= userNr - 1
chosenUsernrdef = chosenUsernr
stupidtrick()
master.destroy()
Simplified / general solution:
import os
from tkinter import Tk
from tkinter import simpledialog
def get_user():
root = Tk()
cmd = """osascript -e 'tell app "Finder" to set frontmost of process "Python" to true'"""
def stupid_trick():
os.system(cmd)
root.withdraw()
new_window=simpledialog.askinteger("Title of window", "Text to show above entry field")
stupid_trick()
root.destroy()
get_user()
EDIT: Now I am figuring out what to look for the solution appears to be found already by multiple posts. For those on OS X wanting to activate a specific Tkinter window when multiple instances of Tkinter and/or python are running simultaneously, you might want to look here.

Python Tkinter Flashing Standard Window

I am building an Interface with TKinter and I have the problem, that whenever create a new window the standardized tkinter window of 200x200 pixel with nothing in it flashes up for a fraction of a second and AFTER that all my modifications (widgets ect.) are made. This happens before AND after calling the mainloop.
The Main-Interface is created.
Mainloop stats
Flashing window
Main-Interface appears
Also after Mainloop was called this happens with newly created windows.
Main-Interface appears--> Push a button, that creates a new window
Flashing window
new window appears
Sadly I cannot give you a sample code... If I try to do a minimal example, this doesn't happen. Maybe the standard window is created, but it is changed so fast, that it doesn't appear on screen. I don't even know what to look up in this case... searching "tkinter flashing window" yields nothing.
EDIT: I found the source of the problem. It seems to be caused by wm_iconbitmap, FigureCanvasTkAgg and tkinter.Toplevel. If you remove the the icon out of the code, it works fine, no flashing. But if I use it together with one of the other, the window flashes when created. Try it out with the code below. You have to put the icon in the working directory of course.
Here is a code sample and the link to the icon I am using, but I suppose any icon will do.
# coding=utf-8
import numpy as np
import matplotlib as mpl
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import tkinter as tk
import os
class INTERFACE(object):
def __init__(self):
self.root = tk.Tk()
self.root.protocol("WM_DELETE_WINDOW", self.EXIT)
self.root.wm_iconbitmap( os.path.abspath("icon.ico")) #<---- !!!!!!
self.root.geometry("1024x768")
canvas = FigureCanvasTkAgg(self.testfigure(), master=self.root) #<---- !!!!!!
canvas.get_tk_widget().grid(sticky=tk.N+tk.W+tk.E+tk.S)
self.root.rowconfigure(0, weight=1)
self.root.columnconfigure(0, weight=1)
def testfigure(self):
x=np.linspace(0, 2*np.pi,100)
y=np.sin(x)
fig = mpl.figure.Figure()
sub = fig.add_subplot(111)
sub.plot(x,y)
return fig
def EXIT(self):
Top = tk.Toplevel(master=self.root)
Top.wm_iconbitmap( os.path.abspath("icon.ico")) #<---- !!!!!!
Top.transient(self.root)
Top.resizable(width=False, height=False)
Top.title("Exit")
tk.Message(Top,text="Do you really want to quit?", justify=tk.CENTER, width=300).grid(row=0,columnspan=3)
tk.Button(Top,text="YES",command=self.root.destroy).grid(row=1,column=0)
tk.Button(Top,text="No",command=self.root.destroy).grid(row=1,column=1)
tk.Button(Top,text="Maybe",command=self.root.destroy).grid(row=1,column=2)
def start(self):
self.root.mainloop()
if __name__ == '__main__':
INTERFACE().start()
I know this is an old question, but I've experienced a similar situation and have found a solution.
In my case, I've isolated the issue to the use of iconbitmap. I've managed to solve it by calling iconbitmap with the after method just before calling root.mainloop().
Example:
from tkinter import *
root = Tk()
w = Label(root, text="Hello, world!")
w.pack()
root.geometry('300x300+500+500')
root.after(50, root.iconbitmap('icon.ico'))
root.mainloop()
This method has worked on Toplevel() windows with icons as well.
Tested on Win 8.1 with Python 3.5.0.
Edit: Upon further inspection I've noticed that the behavior changes relative to root.geometry's presence as well. My initial example didn't have it and I only noticed after a few tries that it still had the same issue. The time delay in the after method doesn't seem to change anything.
Moving root.geometry below the after method yields the same issue for some reason.
Most likely, somewhere in your initialization code you're calling update or update_idletasks, which causes the current state of the GUI to be drawn on the screen.
Another possible source of the problem is if you're creating multiple instances of Tk rather than Toplevel.
Without seeing your code, though, all we can do is guess.
Your best route to solving this problem is to create a small example that has the same behavior. Not because we need it to help you, but because the effort you put into recreating the bug will likely teach you what is causing the bug.
This should work but it requires win32gui
import win32gui
def FlashMyWindow(title):
ID = win32gui.FindWindow(None, title)
win32gui.FlashWindow(ID,True)

Detect key press combination in Linux with Python?

I'm trying to capture key presses so that when a given combination is pressed I trigger an event.
I've searched around for tips on how to get started and the simplest code snippet I can find is in Python - I grabbed the code below for it from here. However, when I run this from a terminal and hit some keys, after the "Press a key..." statement nothing happens.
Am I being stupid? Can anyone explain why nothing happens, or suggest a better way of achieving this on Linux (any language considered!)?
import Tkinter as tk
def key(event):
if event.keysym == 'Escape':
root.destroy()
print event.char
root = tk.Tk()
print "Press a key (Escape key to exit):"
root.bind_all('<Key>', key)
# don't show the tk window
root.withdraw()
root.mainloop()
Tk does not seem to get it if you do not display the window. Try:
import Tkinter as tk
def key(event):
if event.keysym == 'Escape':
root.destroy()
print event.char
root = tk.Tk()
print "Press a key (Escape key to exit):"
root.bind_all('<Key>', key)
# don't show the tk window
# root.withdraw()
root.mainloop()
works for me...
What you're doing is reading /dev/tty in "raw" mode.
Normal Linux input is "cooked" -- backspaces and line endings have been handled for you.
To read a device like your keyboard in "raw" mode, you need to make direct Linux API calls to IOCTL.
Look at http://code.activestate.com/recipes/68397/ for some guidance on this. Yes, the recipe is in tcl, but it gives you a hint as to how to proceed.
Alternatively (a non-Python option) use XBindKeys.
Well, turns out there is a much simpler answer when using GNOME which doesn't involve any programming at all...
http://www.captain.at/howto-gnome-custom-hotkey-keyboard-shortcut.php
Archived on Wayback
Just create the script/executable to be triggered by the key combination and point the 'keybinding_commands' entry you create in gconf-editor at it.
Why didn't I think of that earlier?
tkinter 'bind' method only works when tkinter window is active.
If you want binding keystrokes combinations that works in all desktop (global key/mouse binding) you can use bindglobal (install with pip install bindglobal). It works exactly like standard tkinter 'bind'.
Example code:
import bindglobal
def callback(e):
print("CALLBACK event=" + str(e))
bg = bindglobal.BindGlobal()
bg.gbind("<Menu-1>",callback)
bg.start()

Categories

Resources