change label image everytime when i click a button in tkinter - python

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

Related

Pop up message to appear when text is highlighted using python?

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

how can I use the variable from class A in class B (python)

I want to use the variable (file) in class (PageOne) I'm not good in python so even when I did my research I couldn't do it, can you help me please ?
My project is to take a csv file from the user(in WelcomePage) and it will pick a random row from that file and show it in (PageOne).
i tried different ways but always the same error message (name 'file' is not defined)
from cProfile import label
import tkinter as tk
from turtle import title
from typing import Container
from tkinter import font as tkfont
from tkinter.filedialog import askopenfile
class MainFrame (tk.Tk):
def __init__(self,*args,**kwargs):
tk.Tk.__init__(self,*args,**kwargs)
self.titlefont = tkfont.Font(family = 'Verdana',size=12,
weight = "bold", slant='roman')
container =tk.Frame()
container.grid(row=0, column=0, sticky='nesw')
self.id =tk.StringVar()
self.id.set("Mister")
self.listing = {}
for p in (WelcomePage, PageOne):
page_name = p.__name__
frame = p(parent = container, controller = self)
frame.grid(row=0, column=0, sticky='nesw')
self.listing[page_name] = frame
self.up_frame('WelcomePage')
def up_frame(self,page_name):
page = self.listing[page_name]
page.tkraise()
class WelcomePage(tk.Frame):
global file
def open_file():
file = askopenfile(mode='r', filetypes=[('I', '*csv')])
if file is not None:
pass
return file
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
self.id = controller.id
label = tk.Label(self,text= "Welcome Page \n"+ controller.id.get(),
font= controller.titlefont)
label.pack()
lod = tk.Button (self, text="Select a file",
command= lambda:open_file())
lod.pack()
bou = tk.Button (self, text="Submit",
command= lambda: controller.up_frame("PageOne"))
bou.pack()
class PageOne(WelcomePage,tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
self.id = controller.id
label = tk.Label(self,text= "Page One \n"+ controller.id.get(),
font= controller.titlefont)
label.pack()
bou = tk.Button (self, text="back to main",
command= lambda: controller.up_frame("WelcomePage"))
bou.pack()
if __name__ == '__main__':
app = MainFrame()
app.mainloop()
try this --
use the keyword global in the method as well for the variable purpose
then your error of file name is not defined should resolved.
def open_file():
global file
file = askopenfile(mode='r', filetypes=[('I', '*csv')])
if file is not None:
pass
return file
In Python 3.8 used Walrus. Put keyword inside function
Before:
def open_file():
file = askopenfile(mode='r', filetypes=[('I', '*csv')])
if file is not None:
pass
return file
After Walrus:
def open_file():
global file
if (file := askopenfile(mode='r', filetypes=[('I', '*py' is not None)])):
pass
return file

Adding line numbers to text widget using Tkinter

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

How to add a scrollbar to window made with tkinter

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

Python widgets and button

I'm trying to add a "Done" button to my program that will print the content of both Entry widgets to a new box. I can get the button to appear, but I can't get the information to show up in a new box. What am I doing wrong?
from Tkinter import *
class Application(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
self.grid()
self._name = StringVar()
self._name.set("Enter name here")
self._age = IntVar()
self._age.set("Enter age here")
top = self.winfo_toplevel() # find top-level window
top.title("Entry Example")
self._createWidgets()
self._button = Button(self,
text = "Done")
self._button.grid(row = 1, column = 0, columnspan = 2)
def _createWidgets(self):
textEntry = Entry(self, takefocus=1,
textvariable = self._name, width = 40)
textEntry.grid(row=0, sticky=E+W)
ageEntry = Entry(self, takefocus=1,
textvariable = self._age, width = 20)
ageEntry.grid(row=1, sticky=W)
def _widget(self):
tkMessageBox.showinfo
# end class Application
def main():
Application().mainloop()
You need to assign an action to your button using command: option.
To get what is written in Entry you need to use get() method.
showinfo you need two arguments, one is the title, the other one is what is going to be shown.
Also you need to import tkMessageBox.
Here is a working example.
import Tkinter as tk
import tkMessageBox
class Example(tk.Frame):
def __init__(self,root):
tk.Frame.__init__(self, root)
self.txt = tk.Entry(root)
self.age = tk.Entry(root)
self.btn = tk.Button(root, text="Done", command=self.message)
self.txt.pack()
self.age.pack()
self.btn.pack()
def message(self):
ent1 = self.txt.get()
ent2 = self.age.get()
tkMessageBox.showinfo("Title","Name: %s \nAge: %s" %(ent1,ent2))
if __name__=="__main__":
root = tk.Tk()
root.title("Example")
example = Example(root)
example.mainloop()

Categories

Resources