I want to highlight a given token inside a input text field (We can have several token highlighted) and when the user has the mouse over this token we get up dialogox.
I tried the following:
import tkinter as tk
from tkinter import *
class Example(tk.Frame):
def __init__(self, *args, **kwargs):
tk.Frame.__init__(self, *args, **kwargs)
self.l1 = tk.Label(self, text="Hover over me")
self.l2 = tk.Label(self, text="", width=40)
self.l1.pack(side="top")
self.l2.pack(side="top", fill="x")
self.inputText = tk.Text(root, height = 10, width = 70, bg = "light yellow")
self.inputText.insert('1.0', "token1 token2 token3 etc.")
self.inputText.pack()
self.display_annotate = tk.Button(self, height = 2, width = 20, text ="Annotate text", command = lambda: self.add_highlighter())
self.display_annotate.place(x = 750, y = 20)
print(self.__dict__.keys())
self.l1.bind("<Enter>", lambda event, text="text": self.on_enter(text=text))
self.l1.bind("<Leave>", self.on_leave)
def take_input(self,):
text_to_annotate = self.inputText.get("1.0", "end-1c")
print(text_to_annotate)
return text_to_annotate
def on_enter(self, text):
self.l2.configure(text=text)
def on_leave(self, event):
self.l2.configure(text="")
def add_highlighter(self):
self.inputText.tag_add("start", "1.0", "1.5")
self.inputText.bind("<Enter>", lambda event, text="ali": self.on_enter(text=text))
self.inputText.tag_config("start", background= "black", foreground= "white")
if __name__ == "__main__":
root = tk.Tk()
scrollb = tk.Scrollbar(root)
scrollb.pack(side = tk.RIGHT, fill=tk.Y)
var1 = tk.IntVar()
var2 = tk.IntVar()
root.geometry("900x500+10+10")
root.title('Annotation page')
Example(root).pack(side="top", fill="both", expand="true")
root.mainloop()
It works to highlight the concerned token 1 between the caracters 0 and 5. But it is not working when I haveover the mouse over token 1. noting that it is working for the label.
Any suggestion?
If I got you right, I've added a label when you hover over a token (together with rotating tokens)
import tkinter as tk
from tkinter import *
def on_enter(text, obj, e=None):
obj.float_lbl = Label(root, text=text)
obj.float_lbl.place(x=e.x + obj.winfo_x(), y=e.y + obj.winfo_y())
def on_leave(obj, event=None):
obj.float_lbl.destroy()
class Example(tk.Frame):
def __init__(self, *args, **kwargs):
tk.Frame.__init__(self, *args, **kwargs)
self.l1 = tk.Label(self, text="Hover over me")
self.l1.pack(side="top")
# creating new Text object with new methods and properties
self.inputText = InputText()
# creating 15 new tokens
for i in range(15):
self.inputText.add_token(f"token{i + 1}")
self.inputText.pack()
# binding rotate_tag method to button
self.display_annotate = tk.Button(self, height=2, width=20, text="Annotate text",
command=self.inputText.rotate_tag)
self.display_annotate.place(x=750, y=20)
self.l1.bind("<Enter>", lambda event, text="text": on_enter(text=text, e=event, obj=self.l1))
self.l1.bind("<Leave>", lambda event: on_leave(event=event, obj=self.l1))
class Token:
"""
Token object for InputText
"""
def __init__(self, name: str, pos: int, tag_parent: Text):
self.token_name = name
self.begin = "1." + str(pos)
self.end = "1." + str(pos + len(name))
self.parent = tag_parent
self.add_tag()
self.bind_tag()
def add_tag(self):
self.parent.tag_add(self.token_name, self.begin, self.end)
def bind_tag(self):
self.parent.tag_bind(self.token_name, "<Enter>",
lambda event, text="text": on_enter(obj=self.parent, e=event, text=self.token_name))
self.parent.tag_bind(self.token_name, "<Leave>",
lambda event, text="text": on_leave(obj=self.parent, event=event))
def highlight_tag(self):
self.parent.tag_config(self.token_name, background='red', foreground='white')
def disable_tag(self):
self.parent.tag_config(self.token_name, background='gray', foreground='black')
class InputText(Text):
"""
Text object with methods to add tokens and rotate tags
"""
def __init__(self):
super(InputText, self).__init__(root, height=10, width=70, bg="light yellow")
self.tokens = []
self.last = 0
self.current_tag = None
def add_token(self, name):
self.insert(END, name + " ")
self.tokens.append(Token(name, self.last, self))
self.last += (1 + len(name))
def rotate_tag(self):
if self.current_tag is None:
self.current_tag = 0
else:
self.tokens[self.current_tag - 1].disable_tag()
self.tokens[self.current_tag].highlight_tag()
self.current_tag = min(self.current_tag + 1, len(self.tokens) - 1)
if __name__ == "__main__":
root = tk.Tk()
scrollb = tk.Scrollbar(root)
scrollb.pack(side=tk.RIGHT, fill=tk.Y)
var1 = tk.IntVar()
var2 = tk.IntVar()
root.geometry("900x500+10+10")
root.title('Annotation page')
Example().pack(side="top", fill="both", expand="true")
root.mainloop()
Related
I have text box my_text on gui at location
my_text = Text(root, width=98, height=35, font=("Helvetica", 10), bg="cyan", fg="black")
my_text.grid(row=4, column=0, padx=(20, 50), pady=(20, 0), rowspan=3, sticky="e")
I want to add lines numbers to this text box using Bryan Oakley code here.
My code:
import tkinter as tk
from tkinter import *
root = Tk()
my_text = Text(root, width=98, height=35, font=("Helvetica", 10), bg="cyan", fg="black")
my_text.grid(row=4, column=0, padx=(20, 50), pady=(20, 0), rowspan=3, sticky="e")
text_file = open("sample.xml", 'r')
s = text_file.read()
my_text.delete("1.0", "end")
my_text.insert(END, s)
class TextLineNumbers(tk.Canvas):
def __init__(self, *args, **kwargs):
tk.Canvas.__init__(self, *args, **kwargs)
self.root = Tk()
self.textwidget = None
def attach(self, text_widget):
self.textwidget = text_widget
def redraw(self, *args):
'''redraw line numbers'''
self.delete("all")
i = self.textwidget.index("#0,0")
while True :
dline= self.textwidget.dlineinfo(i)
if dline is None: break
y = dline[1]
linenum = str(i).split(".")[0]
self.create_text(2,y,anchor="nw", text=linenum)
i = self.textwidget.index("%s+1line" % i)
def run(self):
self.root.mainloop()
linenos = TextLineNumbers()
linenos.attach(my_text)
linenos.redraw()
root.mainloop()
Code is not displaying line number. It is displaying just text. How to display linenumbers in text box? Thanks in advance.
You mentioned this great example, why not just modify it to suit your needs? I was curious and modified the Example class from given link by adding a button to call a function load_xml which loads files via filechooser, deletes the previous data in the CustomText widget and inserts the new data:
import tkinter as tk
from tkinter import filedialog
import os
class TextLineNumbers(tk.Canvas):
def __init__(self, *args, **kwargs):
tk.Canvas.__init__(self, *args, **kwargs)
self.textwidget = None
def attach(self, text_widget):
self.textwidget = text_widget
def redraw(self, *args):
'''redraw line numbers'''
self.delete("all")
i = self.textwidget.index("#0,0")
while True:
dline = self.textwidget.dlineinfo(i)
if dline is None:
break
y = dline[1]
linenum = str(i).split(".")[0]
self.create_text(2, y, anchor="nw", text=linenum)
i = self.textwidget.index("%s+1line" % i)
class CustomText(tk.Text):
def __init__(self, *args, **kwargs):
tk.Text.__init__(self, *args, **kwargs)
# create a proxy for the underlying widget
self._orig = self._w + "_orig"
self.tk.call("rename", self._w, self._orig)
self.tk.createcommand(self._w, self._proxy)
def _proxy(self, *args):
# let the actual widget perform the requested action
cmd = (self._orig,) + args
result = self.tk.call(cmd)
# generate an event if something was added or deleted,
# or the cursor position changed
if (args[0] in ("insert", "replace", "delete") or
args[0:3] == ("mark", "set", "insert") or
args[0:2] == ("xview", "moveto") or
args[0:2] == ("xview", "scroll") or
args[0:2] == ("yview", "moveto") or
args[0:2] == ("yview", "scroll")):
self.event_generate("<<Change>>", when="tail")
# return what the actual widget returned
return result
class Example(tk.Frame):
def __init__(self, *args, **kwargs):
tk.Frame.__init__(self, *args, **kwargs)
self.text = CustomText(self)
self.vsb = tk.Scrollbar(self, orient="vertical", command=self.text.yview)
self.text.configure(yscrollcommand=self.vsb.set)
self.text.tag_configure("bigfont", font=("Helvetica", "24", "bold"))
self.linenumbers = TextLineNumbers(self, width=30)
self.linenumbers.attach(self.text)
self.vsb.pack(side="right", fill="y")
self.linenumbers.pack(side="left", fill="y")
self.text.pack(side="right", fill="both", expand=True)
# new button to call load_xml and show status
self.load_button = tk.Button(root, text="Load file", command=self.load_xml)
self.load_button.pack(side="top")
self.text.bind("<<Change>>", self._on_change)
self.text.bind("<Configure>", self._on_change)
self.text.insert("end", "one\ntwo\nthree\n")
self.text.insert("end", "four\n", ("bigfont",))
self.text.insert("end", "five\n")
def _on_change(self, event):
self.linenumbers.redraw()
def load_xml(self):
"""Load any file, delete current text and insert new data"""
input_file = filedialog.askopenfilename(title="Load a textfile",
filetypes=(("XML", "*.xml"),
("Text", "*.txt"),
("All files", "*.*")),
initialdir=os.getcwd())
if input_file:
self.text.delete("1.0", "end")
self.load_button.config(text=f"Currently loaded: {input_file.split(os.sep)[-1]}")
with open(input_file, 'r') as f:
self.text.insert("end", f.read())
if __name__ == "__main__":
root = tk.Tk()
Example(root).pack(side="top", fill="both", expand=True)
root.mainloop()
Hello, please i'm making a tkinter window with an image and two
buttons, and using those buttons previous
and next to switch between images whenever i click the buttons
the problem is that i dont know how to change the image everytime i
click one of the buttons
import tkinter as tk
from tkinter import ttk
from PIL import ImageTk, Image
import os
BASE_DIR = os.path.dirname(os.path.relpath(__file__))
image_dir = os.path.join(BASE_DIR, "my_training_face")
class MainUi(tk.Tk):
def listDir(dir):
global names
global dict
names = []
dict = {}
fileNames = os.listdir(dir)
for fileName in fileNames:
names.append(fileName)
i = 0
while i < len(names):
dict[i] = (names[i])
i = i + 1
return dict
listDir(image_dir)
def get_name(self, cmpt):
try:
self.name = names[cmpt]
return self.name
except:
return "Empty Case"
def get_nbrHab(self):
try:
self.nbr = len(names)
return self.nbr
except:
pass
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.geometry("300x400")
self.geometry("+500+100")
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
self.frames = {}
self.frames = {}
frame = StartPage(container, self)
self.frames[StartPage] = frame
frame.place(relx=0, rely=0, relwidth=1, relheight=1)
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.configure(background='white')
# self.master.configure(background='black')
self.label = tk.Label(self, text=MainUi.get_name(self,0))
self.label.pack(pady=10, padx=10)
button1 = ttk.Button(self, text="Next", width=7, command = lambda: self.next_clicked())
button1.place(relx=0.8, rely=0.9)
button2 = ttk.Button(self, text="Back", width=7, command = lambda: self.back_clicked())
button2.place(relx=0.1, rely=0.9)
self.imgs = []
self.resizing = []
self.finals = []
try:
for i in range(MainUi.get_nbrHab(self)):
self.imgs.append(Image.open(image_dir + "/" + MainUi.get_name(self, self.compteur) + "/1.jpg"))
self.resizing.append(self.imgs[i].resize((160,120), Image.ANTIALIAS))
self.finals.append(ImageTk.PhotoImage(self.resizing[i]))
except:
return
self.label_img = tk.Label(self, bg='white', image= self.finals[0])
self.label_img.image = self.finals[0]
self.label_img.place(relx=0.21, rely=0.15)
compteur = 0
def next_clicked(self):
self.label.config(text=MainUi.get_name(self,self.compteur))
print(self.compteur)
self.label_img.config(image=self.finals[self.compteur])
self.label_img.image=self.finals[self.compteur]
self.compteur += 1
def back_clicked(self):
self.compteur-=1
app = MainUi()
app.mainloop()
I usually use .config() to update the widgets for example ->
from tkinter import *
root = Tk()
label = Label(root, text='This will be updated!')
btn = Button(root, text="Update It", command=lambda : label.config(text='Its Updated!'))
btn.pack()
root.mainloop()
Same thing can be done using Images and Other Widgets Like Buttons
There is issue in your code:
class StartPage(tk.Frame):
def __init__(self, parent, controller):
...
try:
for i in range(MainUi.get_nbrHab(self)):
# "self.compteur" is not yet defined here
# also you should use "i" instead of "self.compteur"
self.imgs.append(Image.open(image_dir + "/" + MainUi.get_name(self, self.compteur) + "/1.jpg"))
...
So the correct code should be:
class StartPage(tk.Frame):
def __init__(self, parent, controller):
...
self.compteur = 0
try:
for i in range(MainUi.get_nbrHab(self)):
self.imgs.append(Image.open(os.path.join(image_dir, MainUi.get_name(self, i), "1.jpg")))
...
Note that I don't know the last argument 1.jpg is correct or not, but it seems not required.
Then the functions for Back and Next buttons should be something like below:
def next_clicked(self):
if self.compteur+1 < len(self.finals):
self.compteur += 1
self.label.config(text=MainUi.get_name(self, self.compteur))
self.label_img.config(image=self.finals[self.compteur])
#self.label_img.image=self.finals[self.compteur] # this line is not necessary actually
def back_clicked(self):
if self.compteur > 0:
self.compteur -= 1
self.label.config(text=MainUi.get_name(self,self.compteur))
self.label_img.config(image=self.finals[self.compteur])
Here is the code. This is just for reference. This is root15.png, root14.png,rot13.png respectively.
from tkinter import *
class ChangeImage:
def __init__(self,root):
self.root = root
self.root.geometry("700x600")
self.list1=[r"C:\Users\91996\Documents\Visual studio code\Visual Studio\rot13.png",r"C:\Users\91996\Documents\Visual studio code\Visual Studio\root15.png",r'C:\Users\91996\Documents\Visual studio code\Visual Studio\root14.png']
self.compteur=0
self.im=PhotoImage(file=self.list1[self.compteur])
self.im_lbl=Label(self.root,image=self.im)
self.im_lbl.pack()
Button(self.root,text="Next Picture",command=self.next_picture).pack(pady=5)
Button(self.root,text="Back Picture",command=self.back_picture).pack(pady=5)
def next_picture(self):
if self.compteur>=len(self.list1)-1:
pass
else:
self.compteur+=1
self.im=PhotoImage(file=self.list1[self.compteur])
self.im_lbl.config(image=self.im)
def back_picture(self):
if self.compteur<=0:
pass
else:
self.compteur-=1
self.im=PhotoImage(file=self.list1[self.compteur])
self.im_lbl.config(image=self.im)
root=Tk()
ob=ChangeImage(root)
root.mainloop()
This code is fine, but I would like to scroll the page using the mousewheel just like we do with Chrome, when the pointer of the mouse is in the middle of the page.
import tkinter as tk
from random import randint
class ScrolledFrame(tk.Frame):
def __init__(self, parent, vertical=True, horizontal=False):
super().__init__(parent)
self._canvas = tk.Canvas(self)
self._canvas.grid(row=0, column=0, sticky='news') # changed
self._vertical_bar = tk.Scrollbar(self, orient='vertical', command=self._canvas.yview)
if vertical:
self._vertical_bar.grid(row=0, column=1, sticky='ns')
self._canvas.configure(yscrollcommand=self._vertical_bar.set)
self._horizontal_bar = tk.Scrollbar(self, orient='horizontal', command=self._canvas.xview)
if horizontal:
self._horizontal_bar.grid(row=1, column=0, sticky='we')
self._canvas.configure(xscrollcommand=self._horizontal_bar.set)
self._vertical_bar.config(command=self._canvas.yview)
self.inner = tk.Frame(self._canvas, bg='red')
self._window = self._canvas.create_window((0, 0), window=self.inner, anchor='nw')
self.columnconfigure(0, weight=1) # changed
self.rowconfigure(0, weight=1) # changed
self.inner.bind('<Configure>', self.resize)
self._canvas.bind('<Configure>', self.frame_width)
def frame_width(self, event):
canvas_width = event.width
self._canvas.itemconfig(self._window, width=canvas_width)
def resize(self, event=None):
self._canvas.configure(scrollregion=self._canvas.bbox('all'))
class Question:
def __init__(self, parent, question, answer):
self.parent = parent
self.question = question
self.answer = answer
self.create_widgets()
def get_input(self):
value = self.entry.get()
print('value:', value)
if value == self.answer:
print("Right it's " + self.answer)
self.label['text'] = self.question + "Right it's " + self.answer
else:
self.label['text'] = "Sorry, it was " + self.answer
def create_widgets(self):
self.labelframe = tk.LabelFrame(self.parent, text="Domanda:")
self.labelframe.pack(fill="both", expand=True)
self.label = tk.Label(self.labelframe, text=self.question)
self.label.pack(expand=True, fill='both')
self.entry = tk.Entry(self.labelframe)
self.entry.pack()
self.entry.bind("<Return>", lambda x: self.get_input())
root = tk.Tk()
root.title("Quiz")
root.geometry("400x300")
window = ScrolledFrame(root)
window.pack(expand=True, fill='both')
for i in range(10):
one = randint(1, 10)
two = randint(1, 10)
Question(window.inner, "How is the result of {} + {} ?".format(one, two), str(one + two))
root.mainloop()
I found how to make it
Ok, I had some problems asking how to make it, but thanks to https://code.activestate.com/recipes/580640-scrolling-frame-with-mouse-wheel-support/ I succeded in making the mousewheel work as I intended. If it can help, the example is here.
import tkinter as tk
from random import randint
# --- classes ---
try:
from Tkinter import Canvas, Frame
from ttk import Scrollbar
from Tkconstants import *
except ImportError:
from tkinter import Canvas, Frame
from tkinter.ttk import Scrollbar
from tkinter.constants import *
import platform
OS = platform.system()
class Mousewheel_Support(object):
# implemetation of singleton pattern
_instance = None
def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = object.__new__(cls)
return cls._instance
def __init__(self, root, horizontal_factor=2, vertical_factor=2):
self._active_area = None
if isinstance(horizontal_factor, int):
self.horizontal_factor = horizontal_factor
else:
raise Exception("Vertical factor must be an integer.")
if isinstance(vertical_factor, int):
self.vertical_factor = vertical_factor
else:
raise Exception("Horizontal factor must be an integer.")
if OS == "Linux":
root.bind_all('<4>', self._on_mousewheel, add='+')
root.bind_all('<5>', self._on_mousewheel, add='+')
else:
# Windows and MacOS
root.bind_all("<MouseWheel>", self._on_mousewheel, add='+')
def _on_mousewheel(self, event):
if self._active_area:
self._active_area.onMouseWheel(event)
def _mousewheel_bind(self, widget):
self._active_area = widget
def _mousewheel_unbind(self):
self._active_area = None
def add_support_to(self, widget=None, xscrollbar=None, yscrollbar=None, what="units", horizontal_factor=None, vertical_factor=None):
if xscrollbar is None and yscrollbar is None:
return
if xscrollbar is not None:
horizontal_factor = horizontal_factor or self.horizontal_factor
xscrollbar.onMouseWheel = self._make_mouse_wheel_handler(widget, 'x', self.horizontal_factor, what)
xscrollbar.bind('<Enter>', lambda event, scrollbar=xscrollbar: self._mousewheel_bind(scrollbar))
xscrollbar.bind('<Leave>', lambda event: self._mousewheel_unbind())
if yscrollbar is not None:
vertical_factor = vertical_factor or self.vertical_factor
yscrollbar.onMouseWheel = self._make_mouse_wheel_handler(widget, 'y', self.vertical_factor, what)
yscrollbar.bind('<Enter>', lambda event, scrollbar=yscrollbar: self._mousewheel_bind(scrollbar))
yscrollbar.bind('<Leave>', lambda event: self._mousewheel_unbind())
main_scrollbar = yscrollbar if yscrollbar is not None else xscrollbar
if widget is not None:
if isinstance(widget, list) or isinstance(widget, tuple):
list_of_widgets = widget
for widget in list_of_widgets:
widget.bind('<Enter>', lambda event: self._mousewheel_bind(widget))
widget.bind('<Leave>', lambda event: self._mousewheel_unbind())
widget.onMouseWheel = main_scrollbar.onMouseWheel
else:
widget.bind('<Enter>', lambda event: self._mousewheel_bind(widget))
widget.bind('<Leave>', lambda event: self._mousewheel_unbind())
widget.onMouseWheel = main_scrollbar.onMouseWheel
#staticmethod
def _make_mouse_wheel_handler(widget, orient, factor=1, what="units"):
view_command = getattr(widget, orient + 'view')
if OS == 'Linux':
def onMouseWheel(event):
if event.num == 4:
view_command("scroll", (-1) * factor, what)
elif event.num == 5:
view_command("scroll", factor, what)
elif OS == 'Windows':
def onMouseWheel(event):
view_command("scroll", (-1) * int((event.delta / 120) * factor), what)
elif OS == 'Darwin':
def onMouseWheel(event):
view_command("scroll", event.delta, what)
return onMouseWheel
class Scrolling_Area(Frame, object):
def __init__(self, master, width=None, anchor=N, height=None, mousewheel_speed=2, scroll_horizontally=True, xscrollbar=None, scroll_vertically=True, yscrollbar=None, background=None, inner_frame=Frame, **kw):
Frame.__init__(self, master, class_="Scrolling_Area", background=background)
self.grid_columnconfigure(0, weight=1)
self.grid_rowconfigure(0, weight=1)
self._width = width
self._height = height
self.canvas = Canvas(self, background=background, highlightthickness=0, width=width, height=height)
self.canvas.grid(row=0, column=0, sticky=N + E + W + S)
if scroll_vertically:
if yscrollbar is not None:
self.yscrollbar = yscrollbar
else:
self.yscrollbar = Scrollbar(self, orient=VERTICAL)
self.yscrollbar.grid(row=0, column=1, sticky=N + S)
self.canvas.configure(yscrollcommand=self.yscrollbar.set)
self.yscrollbar['command'] = self.canvas.yview
else:
self.yscrollbar = None
if scroll_horizontally:
if xscrollbar is not None:
self.xscrollbar = xscrollbar
else:
self.xscrollbar = Scrollbar(self, orient=HORIZONTAL)
self.xscrollbar.grid(row=1, column=0, sticky=E + W)
self.canvas.configure(xscrollcommand=self.xscrollbar.set)
self.xscrollbar['command'] = self.canvas.xview
else:
self.xscrollbar = None
self.rowconfigure(0, weight=1)
self.columnconfigure(0, weight=1)
self.innerframe = inner_frame(self.canvas, **kw)
self.innerframe.pack(anchor=anchor)
self.canvas.create_window(0, 0, window=self.innerframe, anchor='nw', tags="inner_frame")
self.canvas.bind('<Configure>', self._on_canvas_configure)
Mousewheel_Support(self).add_support_to(self.canvas, xscrollbar=self.xscrollbar, yscrollbar=self.yscrollbar)
#property
def width(self):
return self.canvas.winfo_width()
#width.setter
def width(self, width):
self.canvas.configure(width=width)
#property
def height(self):
return self.canvas.winfo_height()
#height.setter
def height(self, height):
self.canvas.configure(height=height)
def set_size(self, width, height):
self.canvas.configure(width=width, height=height)
def _on_canvas_configure(self, event):
width = max(self.innerframe.winfo_reqwidth(), event.width)
height = max(self.innerframe.winfo_reqheight(), event.height)
self.canvas.configure(scrollregion="0 0 %s %s" % (width, height))
self.canvas.itemconfigure("inner_frame", width=width, height=height)
def update_viewport(self):
self.update()
window_width = self.innerframe.winfo_reqwidth()
window_height = self.innerframe.winfo_reqheight()
if self._width is None:
canvas_width = window_width
else:
canvas_width = min(self._width, window_width)
if self._height is None:
canvas_height = window_height
else:
canvas_height = min(self._height, window_height)
self.canvas.configure(scrollregion="0 0 %s %s" % (window_width, window_height), width=canvas_width, height=canvas_height)
self.canvas.itemconfigure("inner_frame", width=window_width, height=window_height)
class Question:
def __init__(self, parent, question, answer):
self.parent = parent
self.question = question
self.answer = answer
self.create_widgets()
def get_input(self):
value = self.entry.get()
print('value:', value)
if value == self.answer:
print("Right it's " + self.answer)
self.label['text'] = self.question + "Right it's " + self.answer
else:
self.label['text'] = "Sorry, it was " + self.answer
def create_widgets(self):
self.labelframe = tk.LabelFrame(self.parent, text="Domanda:")
self.labelframe.pack(fill="both", expand=True)
self.label = tk.Label(self.labelframe, text=self.question)
self.label.pack(expand=True, fill='both')
self.entry = tk.Entry(self.labelframe)
self.entry.pack()
self.entry.bind("<Return>", lambda x: self.get_input())
# self.button = tk.Button(self.labelframe, text="Click", command=self.get_input)
# self.button.pack()
# --- main ---
root = tk.Tk()
root.title("Quiz")
root.geometry("400x300")
window = Scrolling_Area(root)
window.pack(expand=True, fill='both')
for i in range(10):
one = randint(1, 10)
two = randint(1, 10)
Question(window.innerframe, "How is the result of {} + {} ?".format(one, two), str(one + two))
domande = [("Qual è la prima leva del marketing mix? (prodotto o prezzo?", "prodotto")]
for d, r in domande:
Question(window.innerframe, d, r)
root.mainloop()
A simple quiz game
I got this code and I need scrollbars, I tried to search how to add it on stackoverflow (ScrolledWindow with tix...) but I still can't get something that works properly. Could someone help me?
from tkinter import *
from random import randint
root = Tk()
root.title("Quiz")
root.geometry("400x300")
class Window:
def __init__(self, question, answer):
self.text = [question, answer]
self.createLabel()
# self.createText()
self.createEntry()
self.createButton()
def retrieve_input(self):
# inputValue = self.textBox.get("1.0", "end-1c")
# print(inputValue)
if self.mystring.get() == self.text[1]:
print("Esatto. è " + self.text[1])
self.left['text'] = "Esatto"
def createLabel(self):
self.labelframe = LabelFrame(root, text="Domanda:")
self.labelframe.pack(fill="both", expand="yes")
self.left = Label(self.labelframe, text=self.text[0])
self.left.pack()
def createText(self):
self.textBox = Text(height=1)
self.textBox.pack()
def createEntry(self):
self.mystring = StringVar()
self.myentry = Entry(root, textvariable=self.mystring).pack()
def createButton(self):
self.but = Button(text="Click", command=self.retrieve_input)
self.but.pack()
for i in range(10):
one = randint(1, 10)
two = randint(1, 10)
Window("Quanto fa " + str(one) + "+" + str(two) + "?", str(one + two))
root.mainloop()
output
With ScrolledFrame it can look like this
I renamed Window into Question because it makes more sense
I use self.question and self.answer instead of self.text = [question, answer] to make it more readable.
I put classes and functions before root = tk.Tk() to make it more readable.
I use import tkinter as tk instead of from tkinter import * to make it more readable.
Question gets inner frame from ScrolledFrame and use as parent for LabelFrame. Other widgets use labelframe as parent.
BTW: you had entry = Entry(..).pack() which assign None to entry because pack()/grid()/place() returns None. I put pack() in next line and now I can get text directly from Entry (without StringVar)
Code
import tkinter as tk
from random import randint
# --- classes ---
class ScrolledFrame(tk.Frame):
def __init__(self, parent, vertical=True, horizontal=False):
super().__init__(parent)
# canvas for inner frame
self._canvas = tk.Canvas(self)
self._canvas.grid(row=0, column=0, sticky='news') # changed
# create right scrollbar and connect to canvas Y
self._vertical_bar = tk.Scrollbar(self, orient='vertical', command=self._canvas.yview)
if vertical:
self._vertical_bar.grid(row=0, column=1, sticky='ns')
self._canvas.configure(yscrollcommand=self._vertical_bar.set)
# create bottom scrollbar and connect to canvas X
self._horizontal_bar = tk.Scrollbar(self, orient='horizontal', command=self._canvas.xview)
if horizontal:
self._horizontal_bar.grid(row=1, column=0, sticky='we')
self._canvas.configure(xscrollcommand=self._horizontal_bar.set)
# inner frame for widgets
self.inner = tk.Frame(self._canvas, bg='red')
self._window = self._canvas.create_window((0, 0), window=self.inner, anchor='nw')
# autoresize inner frame
self.columnconfigure(0, weight=1) # changed
self.rowconfigure(0, weight=1) # changed
# resize when configure changed
self.inner.bind('<Configure>', self.resize)
self._canvas.bind('<Configure>', self.frame_width)
def frame_width(self, event):
# resize inner frame to canvas size
canvas_width = event.width
self._canvas.itemconfig(self._window, width = canvas_width)
def resize(self, event=None):
self._canvas.configure(scrollregion=self._canvas.bbox('all'))
class Question:
def __init__(self, parent, question, answer):
self.parent = parent
self.question = question
self.answer = answer
self.create_widgets()
def get_input(self):
value = self.entry.get()
print('value:', value)
if value == self.answer:
print("Esatto. è " + self.answer)
self.label['text'] = "Esatto"
def create_widgets(self):
self.labelframe = tk.LabelFrame(self.parent, text="Domanda:")
self.labelframe.pack(fill="both", expand=True)
self.label = tk.Label(self.labelframe, text=self.question)
self.label.pack(expand=True, fill='both')
self.entry = tk.Entry(self.labelframe)
self.entry.pack()
self.button = tk.Button(self.labelframe, text="Click", command=self.get_input)
self.button.pack()
# --- main ---
root = tk.Tk()
root.title("Quiz")
root.geometry("400x300")
window = ScrolledFrame(root)
window.pack(expand=True, fill='both')
for i in range(10):
one = randint(1, 10)
two = randint(1, 10)
Question(window.inner, "Quanto fa {} + {} ?".format(one, two), str(one + two))
root.mainloop()
I have been trying all the many examples in this website about switching windows in tkinter, but all I get is the buttons from the raised frame to be stacked on top, without "hiding" the other frames
Main screen :
import tkinter as tk
import Controller.ProdutoController as viewProduto
class Main:
def __start__(self):
self.roottk = tk.Tk()
self.root = tk.Frame(self.roottk)
self.root.pack(side="top", fill="both", expand=True)
self.root.grid_rowconfigure(0, weight=1)
self.root.grid_columnconfigure(0, weight=1)
self.viewProduto = viewProduto.ProdutoController(self, self.root)
self.viewMain = MainView(self, self.root)
self.viewMain.tkraise()
self.viewMain.master.master.title("Tela Principal")
self.roottk.mainloop()
def toprodutos(self):
self.viewProduto.viewProduto.tkraise()
def tomain(self):
self.viewMain.tkraise()
class MainView(tk.Frame):
def __init__(self, ct, root):
tk.Frame.__init__(self,root)
self.startUI()
self.ct = ct
self.grid( row = 0 , column = 0, sticky = "nsew")
def startUI(self):
botaoProdutos = tk.Button(self, text = "Produtos", command = self.toprodutos , padx = 5 , pady = 5)
botaoProdutos.pack(side = "top")
botaoEntrada = tk.Button(self, text="Entrada", command=self.toentrada, padx=5, pady=5)
botaoEntrada.pack(side="top")
botaoSaida = tk.Button(self, text="Saída", command=self.tosaida, padx=5, pady=5)
botaoSaida.pack(side="top")
def toprodutos(self):
self.ct.toprodutos()
def toentrada(self):
return
def tosaida(self):
return
"Produtos" screen :
class ProdutoController:
def __init__(self, viewMain, root):
self.produtos = []
self.viewMain = viewMain
self.viewProduto = ProdutoView(root, self)
def newproduto(self, nome, preco, quantidade):
return
def listprodutos(self):
return
class ProdutoView(tk.Frame):
def __init__(self, root, ct):
tk.Frame.__init__(self, root)
self.createWidgets()
self.ct = ct
self.grid(row = 0, column = 0)
def createWidgets(self):
self.list = tk.Button(self)
self.list["text"] = "List"
self.list["command"] = self.listProdutos
self.list.pack(side="top")
def listProdutos(self):
self.ct.listprodutos()
You aren't using the "sticky" attribute to force ProdutoView to fill the row and column that it is in.
class ProdutoView(tk.Frame):
def __init__(self, root, ct):
...
self.grid(row = 0, column = 0, sticky="nsew")