python spawn multiple message boxes at once - python

I need my program to spawn multiple message boxes.
They have to be spawned in cascade at once.
(think of it as mimicry of malicious activity)
I tried do this using Tkinter:
import Tkinter
import tkMessageBox
for i in range(0,5):
tkMessageBox.showerror("", "oops")
but it seems program waits for user interaction with each message before showing next which is not quite what I need
and optional there is an empty form at top left corner. any idea to get rid of it?

The solutions might be to use TopLevel() here. This will allow all windows to pop up and you will be able to set a customer messagebox style as well.
Here is a simple example that will open all the windows at once while also hiding the root window. The below will stack all the windows on top of each other and you can move them. You can also provide some tracked variables to open each windows in a different location if you like.
#For python 3 imports:
import tkinter as tk
from tkinter import ttk
# for python 2 imports:
# import Tkinter as tk
# import ttk
root = tk.Tk()
root.withdraw()
for i in range(0,5):
x = tk.Toplevel(root)
x.title("Error Box!")
x.geometry("150x75+0+0")
x.resizable(False, False)
ttk.Label(x, text = "oops").pack()
ttk.Button(x, text = " OK ", command = x.destroy).pack(side=tk.BOTTOM)
root.mainloop()
In response to your comment on using a counter see below code:
#For python 3 imports:
import tkinter as tk
from tkinter import ttk
# for python 2 imports:
# import Tkinter as tk
# import ttk
root = tk.Tk()
root.withdraw()
counter = 0
def close_window(top_window):
global counter
top_window.destroy()
counter -= 1
if counter == 0:
print("destroying root window")
root.destroy()
for i in range(0,5):
counter += 1
x = tk.Toplevel(root)
x.title("Error Box!")
x.geometry("150x75+0+0")
x.resizable(False, False)
ttk.Label(x, text="oops").pack()
ttk.Button(x, text=" OK ", command=lambda tw=x: close_window(tw)).pack(side=tk.BOTTOM)
# this protocol() method is used to re-rout the window close event to a customer function.
# this will allow us to keep our counter and close windows as needed.
x.protocol("WM_DELETE_WINDOW", lambda tw=x: close_window(tw))
root.mainloop()
Better yet here is an example that places the items inside of a list so we do not need a counter.
#For python 3 imports:
import tkinter as tk
from tkinter import ttk
# for python 2 imports:
# import Tkinter as tk
# import ttk
root = tk.Tk()
root.withdraw()
list_of_windows = []
def close_window(tw):
i = list_of_windows.index(tw)
list_of_windows[i].destroy()
del list_of_windows[i]
if len(list_of_windows) == 0:
root.destroy()
print("root destroyed!")
for i in range(0,5):
x = tk.Toplevel(root)
x.title("Error Box!")
x.geometry("150x75+0+0")
x.resizable(False, False)
ttk.Label(x, text="oops").pack()
ttk.Button(x, text=" OK ", command=lambda tw=x: close_window(tw)).pack(side=tk.BOTTOM)
x.protocol("WM_DELETE_WINDOW", lambda tw=x: close_window(tw))
list_of_windows.append(x)
root.mainloop()

My conclusion:
Using tk message boxes wasn't best approach to the task,
because message boxes are modal, and there is no direct way to change that.
So instead I've just got a form shaped like a message box and spawned them with desirable quantity.
Ended up with following code:
from Tkinter import *
di = {}
for i in range(5):
di[i] = Tk()
offset = 300 + i*10
di[i].geometry('150x50+'+str(offset)+'+'+str(offset))
di[i].title('')
di[i].resizable(False, False)
la = Label(di[i],text = 'oops').pack()
button = Button(di[i], text = 'OK', command=di[i].destroy).pack()
di[0].mainloop()
And it serves my needs well. Thanks to Nae and Vasilis G. for their kind responses leading me to a working code.

Related

Show new info in root of Tkinter waiting time as a multiprocess

In python, i want to add the Message 2 to the Tkinter window if a condition is given. In this example I use value of X (time) as a multiprocess, but once in the loop of the Tkinter root I cant give the new Message (2).
from tkinter import *
import time as t
from threading import Thread
def time1():
global x
x = 0
while x<3:
t.sleep(1)
x += 1
def gui():
root = Tk()
Label(root, text=("Message 1")).pack()
if x == 2:
Label(root, text=("Message 2")).pack()
root.mainloop()
generator = Thread(target=time1)
processor1 = Thread(target=gui)
generator.start()
processor1.start()
generator.join()
In your gui function, you are only testing for the value of x once, just before entering the mainloop. If x becomes 2 later, nothing will happen.
Try this instead:
import tkinter as tk
import time
from threading import Thread
def time1():
"""
Add a second Label after a certain condition is met.
"""
x = 0
while x < 3:
time.sleep(1)
x += 1
tk.Label(root, text="Message 2").pack()
if __name__ == "__main__":
root = tk.Tk()
tk.Label(root, text="Message 1").pack()
generator = Thread(target=time1)
generator.start()
root.mainloop()
From the Python tutorial:
Note that in general the practice of importing * from a module or package is frowned upon, since it often causes poorly readable code. However, it is okay to use it to save typing in interactive sessions.
It is considered good practice with tkinter that the GUI is run from the main thread. This is to take ensure that everything works if tkinter is not built with multithreading support.

How to update Tkinter Progressbar?

So I'm writing some code to check VAT-IDs using a web API. Since some files have quite a large amount of API-calls it sometimes takes quite a while to complete and I want to show a progressbar so other users know that the program hasn't crashed yet. I found an example and modified it slightly so that it fits my needs. This code shows a window with a progressbar and once the for loop is finished the window closes.
import tkinter as tk
from tkinter import ttk
import time
def wrap():
MAX = 30000
root = tk.Tk()
root.geometry('{}x{}'.format(400, 100))
progress_var = tk.IntVar() #here you have ints but when calc. %'s usually floats
theLabel = tk.Label(root, text="Sample text to show")
theLabel.pack()
progressbar = ttk.Progressbar(root, variable=progress_var, maximum=MAX)
progressbar.pack(fill=tk.X, expand=1)
def loop_function():
k = 0
for k in range(MAX):
### some work to be done
progress_var.set(k)
time.sleep(0.002)
root.update()
root.destroy()
root.after(100, loop_function)
root.mainloop()
wrap()
Now I wanted to implement this into my tool:
import pandas as pd
import re
import pyvat
from tkinter import ttk
import tkinter as tk
def vatchecker(dataframe):
#initialise progressbar
root = tk.Tk()
root.geometry('{}x{}'.format(400, 100))
progress_var = tk.DoubleVar() #here you have ints but when calc. %'s usually floats
theLabel = tk.Label(root, text="Calling VAT Api")
theLabel.pack()
maxval = len(dataframe['Vat-ID'])
progressbar = ttk.Progressbar(root, variable=progress_var, maximum=maxval)
progressbar.pack(fill=tk.X, expand=1)
checked =[]
def loop_function():
for row in range(len(dataframe['Vat-ID'])):
print("Vatcheck: " + str(round(row/maxval * 100, 2)) + " %")
if pd.isna(dataframe['Vat-ID'][row]):
checked.append('No Vat Number')
else:
#check if vat id contains country code
groups = re.match(r'[A-Z][A-Z]', dataframe['Vat-ID'][row])
if groups != None:
querystring = dataframe['Vat-ID'][row][:-2]
country = dataframe['Vat-ID'][row][-2:]
#else get VAT-ID from Country ISO
else:
querystring = dataframe['Vat-ID'][row]
country = dataframe['Land-ISO2'][row]
try:
result = pyvat.check_vat_number(str(querystring), str(country))
checked.append(result.is_valid)
except:
checked.append('Query Error')
progress_var.set(row)
root.update()
root.destroy()
root.quit()
root.after(100, loop_function)
root.mainloop()
dataframe['Vat-ID-check'] = checked
return dataframe
This function gets called by the main script. Here the progressbar window is shown yet the bar doesn't fill up.
With print("Vatcheck: " + str(round(row/maxval * 100, 2)) + " %") I can still track the progress but it's slightly ugly.
Earlier in the main script the user already interacts with a Tkinter GUI but afterwards I close those windows and loops with root.destroy()' and 'root.quit() so I think it should be fine to run another Tkinter instance like this?
Any help or hints would be greatly appreciated.
as #jasonharper mentioned above changing progress_var = tk.DoubleVar() to progress_var = tk.DoubleVar(root) works

Move two windows together tkinter

I have two types of windows: Main and Child. When I move main, all child windows must move also.
So I tried to write a method, but I am new to Tkinter so it is a bit hard. Isn't there a mehtod which Tkinter already provides?
There are two errors which occure:
line 21, in move_me if second_window != None:
NameError: name 'second_window' is not defined
wm_geometry() takes from 1 to 2 positional arguments but 3 were given
''' import tkinter as tk
from tkinter import *
from tkinter import Tk
from functools import partial
from tkinter import filedialog
import tkinter as tk
root=Tk()
def second_window_X():
global second_window
second_window=Tk()
label=Label(second_window, text='window')
label.pack()
button=Button(root, text='second window', command=second_window_X)
button.pack()
def move_me(event):
if second_window != None:
x = root.winfo_x()
y = root.winfo_y()
second_window.geometry(x,y)
root.bind("<Configure>", move_me)
root.mainloop()````
Is there someone who can give me an example how to link both windows togehter and make them move at the same time? And who can explain to me, why move me doesn't knows second_window even if i declared it as global?
Thank you very much already
Sorry for all the imports
As I suggested in a comment, you shouldn't have two instances of Tk in an application. Your second window should be an instance of Toplevel.
The below code moves the second window when the first window is moved/resized.
from tkinter import *
root=Tk()
second_window = None
def second_window_X():
global second_window
second_window=Toplevel(root)
label=Label(second_window, text='window')
label.pack()
button=Button(root, text='second window', command=second_window_X,width=100)
button.pack()
def move_me(event):
try:
if second_window != None:
x = root.winfo_x()
y = root.winfo_y()
second_window.geometry(f"+{x}+{y}")
except NameError:
pass
root.bind("<Configure>", move_me)
root.mainloop()

How can I get the option selected by a user from a combobox in toplevel

I'm new to Python and Tkinter and was trying to create an interface to search & plot data. I created a very simple toplevel window to get the values from a combobox that would be selected from users. However, I find the script would only print the first item in the list if comboxlist2.current(0) was set or it would print nothing, no matter which one is selected in the box. I created a sample script to test this. If I click on the "search & create", then the return values can change according to the user selection in comboxlist1, while it would all return "1" no matter what the user selected in comboxlist2. So may I ask where is the issue and how to solve?
Thanks in advance for the potential suggestions or solutions!
import tkinter as tk
from tkinter import ttk
from tkinter import *
def root_print():
reg_in = comboxlist1.get()
print(reg_in) #print the value selected
def on_click():
tl = Toplevel()
comvalue2 = tk.StringVar()
comboxlist2 = ttk.Combobox(tl,textvariable=comvalue2)
comboxlist2["values"] = ("1","2","3")
comboxlist2.grid()
comboxlist2.current(0) #select the first one as default
#mm = comboxlist2.get()
#print(mm) #print directly
go(comboxlist2,tl)
tl.wait_window()
return
def go(comboxlist2,tl):
mm = comboxlist2.get()
Button(tl,text='go', command=lambda:test(mm)).grid()
def test(mm):
print(mm) #do the same thing for the comboxlist2
root = Tk()
root.title('search') #create an interface
root.geometry('+400+200') #size and position
Label(text='region ').grid(row=2,column=0)
comvalue1 = tk.StringVar()
comboxlist1=ttk.Combobox(root,textvariable=comvalue1)
comboxlist1["values"]=("all","africa","asia","australia","canada","europe","mexico","southamerica","usa")
comboxlist1.grid(row=2,column=1)
comboxlist1.current(0)
Button(text='search & create', command=root_print).grid(row=0,column=4)
Button(text='click', command=on_click).grid(row=1, column=4)
loop = mainloop()#go!
Here is the working code, which should take care of your needs. I have removed the imports and some code snippets which are not useful.
import tkinter as tk
from tkinter import ttk
def root_print():
reg_in = comboxlist1.get()
print(reg_in)
def on_click():
tl = tk.Toplevel()
comvalue2 = tk.StringVar()
comboxlist2 = ttk.Combobox(tl,textvariable=comvalue2)
comboxlist2["values"] = ("1","2","3")
comboxlist2.grid()
comboxlist2.current(0) #select the first one as default
tk.Button(tl,text='go', command=lambda: test(comboxlist2.get())).grid()
tl.wait_window()
def test(mm):
print(mm)
root = tk.Tk()
root.title('search') #create an interface
root.geometry('+400+200') #size and position
tk.Label(text='region ').grid(row=2,column=0)
comvalue1 = tk.StringVar()
comboxlist1=ttk.Combobox(root,textvariable=comvalue1)
comboxlist1["values"]=("all","africa","asia","australia","canada","europe","mexico","southamerica","usa")
comboxlist1.grid(row=2,column=1)
comboxlist1.current(0)
tk.Button(text='search & create', command=root_print).grid(row=0,column=4)
tk.Button(text='click', command=on_click).grid(row=1, column=4)
root.mainloop()

Python TKinter: frame and canvas will expand horizontally, but not vertically, when window is resized

For some reason, I was able to get this TKinter frame (allValOuterFrame) to expand both vertically and horizontally when its window is resized, but however, it appears that the canvas that the frame holds, as well as the frame and vertical scrollbar inside that canvas, will only expand horizontally. Could someone please explain why?
Here is my code:
# At first I only had "from X import *" but then discovered that
# it is bad practice, so I also included "import X" statements so
# that if I need to use something from tk or ttk explicitly, I can,
# but I had 2/3 of my code done at that point so I had to leave in the
# "import *" things.
try:
from Tkinter import * #python 2
import Tkinter as tk
from ttk import *
import ttk
import tkMessageBox as msg
import tkFileDialog as openfile
except ImportError:
from tkinter import * #python 3
import tkinter as tk
from tkinter.ttk import *
import tkinter.ttk as ttk
from tkinter import messagebox as msg
from tkinter import filedialog as openfile
import csv
# My stuff:
from extractor import Analysis
from extractor import createDictionariesAndLists as getRawData
from nitrogenCorrector import correct as nCorrect
from carbonCorrector import correct as cCorrect
( ... )
def createAllValWindow(self):
allValWindow = Toplevel(self)
allValWindow.grab_set()
if self.element == "N":
allValWindow.title("All Nitrogen Raw Data Values")
elif self.element == "C":
allValWindow.title("All Carbon Raw Data Values")
else:
allValWindow.title("All Raw Data Values")
allValOuterFrame = tk.Frame(allValWindow,background="#00FF00")
allValCanvas = Canvas(allValOuterFrame, borderwidth=0)
allValInnerFrame = Frame(allValCanvas, borderwidth=5)
def allValOnFrameConfigure(event):
allValCanvas.configure(scrollregion=allValCanvas.bbox("all"))
allValOuterFrame.grid_rowconfigure(0,weight=1)
allValOuterFrame.grid_columnconfigure(0,weight=1)
allValInnerFrame.grid_rowconfigure(0,weight=1)
allValInnerFrame.grid_columnconfigure(0,weight=1)
allValVertScrollbar = Scrollbar(allValOuterFrame, orient="vertical",command=allValCanvas.yview)
allValHorizScrollbar = Scrollbar(allValOuterFrame, orient="horizontal",command=allValCanvas.xview)
allValCanvas.configure(yscrollcommand=allValVertScrollbar.set, xscrollcommand=allValHorizScrollbar.set)
allValVertScrollbar.grid(row=1,column=12,sticky=N+S)
allValHorizScrollbar.grid(row=2,column=0,columnspan=12,sticky=E+W)
allValCanvas.grid(row=1,column=0,columnspan=12,sticky=N+S+E+W) allValCanvas.create_window((4,4),window=allValInnerFrame,anchor="nw",tags="allValInnerFrame")
allValInnerFrame.bind("<Configure>",allValOnFrameConfigure)
allValDoneButton = Button(allValWindow,text="Done",command=allValWindow.destroy)
allValOuterFrame.pack(fill="both",expand=1)
allValDoneButton.pack()
textRows = [self.rawHeader]
textLabels = [[]]
numDiffAminos = len(self.analyses) - 1 # Ignore the "trash" list at the end
for singleAminoAnalyses in self.analyses[0:numDiffAminos]: # Once again, ignoring the last list
if len(singleAminoAnalyses) < 1:
continue
for analysis in singleAminoAnalyses:
textRows.append([str(analysis.analysisID),
str(analysis.row),
str(analysis.identifier1),
str(analysis.identifier2),
str(analysis.comment),
str(analysis.peakNumber),
str(analysis.rt),
str(analysis.component),
str(analysis.areaAll),
str(analysis.ampl),
str(analysis.r),
str(analysis.delta)])
textLabels.append([])
for i in range(len(textRows)):
if i == 0:
listRow = i
else:
listRow = i+1
for j in range(len(textRows[i])):
if i == 0:
textLabels[i].append(Label(allValInnerFrame,text=textRows[i][j],font=("Fixedsys",10,"bold")))
else:
textLabels[i].append(Label(allValInnerFrame,text=textRows[i][j]))
if j == 9:
textLabels[i][j].grid(row=listRow,column=j,sticky=W+E,padx=(4,10))
else:
textLabels[i][j].grid(row=listRow,column=j,sticky=W+E,padx=(4,4))
if i == 0:
separator = tk.Frame(allValInnerFrame, height=2, borderwidth=1, bg="black", relief=SUNKEN)
separator.grid(row=1,column=0,columnspan=12,sticky=W+E)
It is because you give row 0 of AllValOuterFrame a weight of 1, but you put the canvas in row 1. If you move the canvas to row 0 or give row 1 a weight of 1, it will resize the way you expect.

Categories

Resources