tkinter scrolledtext.insert and label.configuration point of execution - python-3.x

I have the following python36 code wich is checking values, and then set a tkinter label that the entry values has been accepted, to then execute another threaded function. Before and after the function as well as inside the function I would like to display something in a tkinter scrolledtext box. Like it where a console/shell.
All the label.configure() as well as scrolledtext.insert() are only getting displayed all at the same time after everything has been run.
I am not able to use the scrolledtext.insert() inside the threaded function (fundamentals question, could I use it inside a function of an imported module?)
I would like to have the execution time of these functions like if I would use the print() function. So execute it as soon as the script went over it.
It would be nice if you could explain to me why this is not executed immideately since I am currently learning python or point me to the appropriate reference.
elif str(x2) == 'None' and str(x3) == 'None':
E2T = 'accepted'
E2L.configure(text=E2T, fg="green")
E2L.grid(row=5, column=2)
E3T = 'accepted'
E3L.configure(text=E3T, fg="green")
E3L.grid(row=6, column=2)
# Start scanning process
scrolledtext.insert(tkinter.INSERT, 'Start scanning....\n' )
print('testprint')
portlist = scan(E1.get(),E2.get(),E3.get())
# try work with returned value and display as in a console
print(portlist)
print('testprint')
scrolledtext.insert(tkinter.INSERT, 'Following Ports are open\n' )
scrolledtext.insert(tkinter.INSERT, str(portlist))

You can do scrolledtext.update_idletasks() after a scrolledtext.insert(), it will refresh the widget and your text will pop.
see that comment for more.
Hope it help!

Related

How to close window after user agreed with terms and conditions

The problem here is that I cannot disable the button before closing it, how can I do that?
import tkinter as tk
w_window = tk.Tk()
w_window.title('Introduction')
w_window.geometry("450x100")
def tez():
w_button['state'] = 'disabled'
w_button = tk.Button(w_window,
text='Agree',
font = 'Bold 11',
command=tez)
w_button.pack()
if (w_button['state'] == 'disabled'):
w_window.destroy()
w_window.mainloop()
I expected it to disable the button before closing it, but what happened is that it closed without disabling the button.
Your code has indentation errors at the time I am writing my answer, please fix them.
Coming to the point, every Tkinter widget has an after() method, with the following syntax:
<widget-name>.after(time, callback=None)
The time argument takes a value as an int in milliseconds, and callback argument takes a function to repeat after every time interval.
For example if root is the main window,
root.after(100, my_function)
This will call my_function function every 100 milliseconds or 1 second. Notice that unlike sleep() method, this does not halt the main thread, and executes as an independent thread, so the GUI will not pause.
So what you can do is check for the state of w_button after every 50 milliseconds (you can change it as per your wish), and when it gets disabled, the window will destroy().
Here's your full code with the solution implemented:
import tkinter as tk
w_window = tk.Tk()
w_window.title('Introduction')
w_window.geometry("450x100")
def tez():
w_button['state'] = 'disabled' # fixed indentation
w_button = tk.Button(w_window, text='Agree', font = 'Bold 11', command=tez)
w_button.pack()
def check_state():
if (w_button['state'] == 'disabled'): # fixed indentation
w_window.destroy() # fixed indentation
w_window.after(50, check_state) # after every 50 milliseconds, check_state() method is called
check_state()
w_window.mainloop()
Now your code will run as expected. Note that you can make the destroy() method to implement faster by modifying the time in w_window.after() method to a lesser value.
Hope my answer helps. :)
Refer to this article for further research: after Method - tutorialspoint

ValueError: complex() arg is a malformed string

I have to take complex number as an input from Entry widget of tkinter and perform the conjugate operation on that complex number. I applied explicit conversion method but my code is not able to convert Entry widget string into complex number and showing error "ValueError: complex() arg is a malformed string" Can anyone help me?
Thank you in advance.
lbl_shh=Label(second_root,text="Enter parameter Shh",fg="red").grid(column=0,row=7,padx=20,pady=20)
e_shh = Entry(second_root)
lbl_svv=Label(second_root,text="Enter parameter Svv",fg="red").grid(column=0,row=8,padx=20,pady=20)
e_svv = Entry(second_root)
e_shh.grid(column=1,row=7)
e_svv.grid(column=1,row=8)
shh=e_shh.get()
svv=e_svv.get()
shh=shh.replace(" ","")
svv=svv.replace(" ","")
shh=complex(shh)
svv=complex(svv)
#shh=complex(''.join(shh.split()))
#svv=complex(''.join(svv.split()))
shhs=np.conjugate(shh)
svvs=np.conjugate(svv)
num= svv*svvs
dem=shh*shhs
f=np.power(num/dem, 0.25)
I have to print the value of f
I think you misunderstand how to properly get information within tkinter and probably Python in general.
You cannot just use .get() when your code is just initializing. It will always return an empty string unless you have some code that sets the value prior to get and at that point its just redundant to use get.
What you need to do is have some code like a button that will pull the value of your entry(s) after someone has added something to them.
Also I noticed in your example code you have second_root and this leads me to believe you are using 2 instances of Tk() in your code. If that is the case this may also be part of your problem. You should only ever have one instance of Tk() when coding in tkinter.
To ilistrate your problem Take this example:
I added some print statements, a function and a button to show what was actually being grabbed by get() or rather to show it is an empty string. If you do not have anything in the field by the time get() is executed.
And here is an example result from when you put a proper value that complex() can use.
See below example to get an idea of how get() works:
import tkinter as tk
root = tk.Tk()
entry = tk.Entry(root)
entry.pack()
def print_entry():
print(entry.get())
tk.Button(root, text='Print Entry', command=print_entry).pack()
root.mainloop()

tkinter function repeats itself twice when ttk widgets are engaged

The program works as intended when I simply use tkinter's widgets. When I use ttk's widgets the program repeats itself twice. I tried almost everything in my knowledge to fix this, I believe that *args have something to do with it. Is there anyway to prevent my function _up_options from running twice?
from tkinter import *
from tkinter import ttk
root = Tk()
first = StringVar(root)
second = StringVar(root)
Ore = {'Options': [''], 'Yes': ['One'], 'No': ['Two']}
entry1 = ttk.OptionMenu(root, first, *Ore.keys())
entry2 = ttk.OptionMenu(root, second, '')
entry1.pack()
entry2.pack()
def _up_options(*args):
print('update_options')
ores = Ore[first.get()]
second.set(ores[0])
menu = entry2['menu']
menu.delete(0, 'end')
for line in ores:
print('for')
menu.add_command(label=line, command=lambda choice=line: second.set(choice))
first.trace('w', _up_options)
root.mainloop()
PS, I used *args in my function to work. If anyone can explain this, I would be very grateful
I think I figured this out. The problem is that the variable actually is set twice by the ttk OptionMenu.
Take a look at this piece of code from the tkinter OptionMenu:
for v in values:
menu.add_command(label=v, command=_setit(variable, v, callback))
This adds a button to the menu for each value, with a _setit command. When the _setit is called it sets the variable and another callback if provided:
def __call__(self, *args):
self.__var.set(self.__value)
if self.__callback:
self.__callback(self.__value, *args)
Now look at this piece of code from the ttk OptionMenu:
for val in values:
menu.add_radiobutton(label=val,
command=tkinter._setit(self._variable, val, self._callback),
variable=self._variable)
Instead of a command this adds a radiobutton to the menu. All radiobuttons are "grouped" by linking them to the same variable. Because the radiobuttons have a variable, when one of them is clicked, the variable is set to the value of the button. Next to this, the same command is added as in the tkinter OptionMenu. As said, this sets the variable and then fires another command of provided. As you can see, now the variable is updated twice, once because it is linked to the radiobutton and once more because it is set in the _setit function. Because you trace the changing of the variable and the variable is set twice, your code also runs twice.
Because the variable is set twice from within the ttk code, I guess there's not much you can do about that. If you don't change the variable from any other part of your code than from the OptionMenu though, you could choose to not trace the variable, but instead add your function as command to the OptionMenu:
entry1 = ttk.OptionMenu(root, first, *Ore.keys(), command=_up_options)
P.S. this was introduced with this commit after this bugreport.
I guess when adding the variable=self._variable the command should have been changed to just command=self._callback.
You can understand the problem in the error message:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Users\user\AppData\Local\Programs\Python\Python36\lib\tkinter__init__.py", line 1699, in call
return self.func(*args)
TypeError: _up_options() takes 0 positional arguments but 3 were given
Initially, you don't use _up_options When you change the Options you call _up_options to trace the first StringVar and change it to the value of the next object in the dictionary.
Now when you do that you are running on all the objects in the dictionary, therefore, you need the *args so the lambda function will run on all args given!
As for your problem:
When I use ttk's widgets the program repeats itself twice.
EDIT
See #fhdrsdg's answer!
The solution is just to change command=tkinter._setit(self._variable, val, self._callback) to command=self._callback.
Hope you find this helpful!
Instead of tracing the StringVar, add a callback as command argument for OptionMenu constructor.
I created a subclass of ttk.OptionMenu to solve this (as well as to provide slightly simpler usage of the widget and a more useful callback). I think this is a more stable approach than modifying the original class directly or just overriding the original method because it guarantees compatibility with potential changes to the built-in/original widget in future Tkinter versions.
class Dropdown( ttk.OptionMenu ):
def __init__( self, parent, options, default='', variable=None, command=None, **kwargs ):
self.command = command
if not default:
default = options[0]
if not variable:
variable = Tk.StringVar()
if command:
assert callable( command ), 'The given command is not callable! {}'.format( command )
ttk.OptionMenu.__init__( self, parent, variable, default, *options, command=self.callBack, **kwargs )
else:
ttk.OptionMenu.__init__( self, parent, variable, default, *options, **kwargs )
def callBack( self, newValue ):
self.command( self, newValue )
You can then use it like this:
def callback( widget, newValue ):
print 'callback called with', newValue
print 'called by', widget
options = [ 'One', 'Two', 'Three' ]
dropdown = Dropdown( parent, options, command=callback )
dropdown.pack()
Besides avoiding the double-trace issue, other notable differences from the original ttk.OptionMenu includes not needing to supply a Tkinter variable or default value if you don't need them (the default item will be the first item in the options list if not provided), and being able to get the widget that called the callback function when it fires. The latter is very helpful if you have many dropdown widgets sharing the same callback and you need to know which one is being used within the call.
Soon after writing this, I also found another solution using lambda: Passing OptionMenu into a callback (or retrieving a reference to the used widget)
I thought I might still share this Dropdown widget anyway since it can make the higher-level code simpler, and it provides a good base if you have some other custom methods to add in.

Multiprocessing - tkinter pipeline communication

I have a question on multiprocessing and tkinter. I am having some problems getting my process to function parallel with the tkinter GUI. I have created a simple example to practice and have been reading up to understand the basics of multiprocessing. However when applying them to tkinter, only one process runs at the time. (Using Multiprocessing module for updating Tkinter GUI) Additionally, when I added the queue to communicate between processes, (How to use multiprocessing queue in Python?), the process won't even start.
Goal:
I would like to have one process that counts down and puts the values in the queue and one to update tkinter after 1 second and show me the values.
All advice is kindly appreciated
Kind regards,
S
EDIT: I want the data to be available when the after method is being called. So the problem is not with the after function, but with the method being called by the after function. It will take 0.5 second to complete the calculation each time. Consequently the GUI is unresponsive for half a second, each second.
EDIT2: Corrections were made to the code based on the feedback but this code is not running yet.
class Countdown():
"""Countdown prior to changing the settings of the flows"""
def __init__(self,q):
self.master = Tk()
self.label = Label(self.master, text="", width=10)
self.label.pack()
self.counting(q)
# Countdown()
def counting(self, q):
try:
self.i = q.get()
except:
self.label.after(1000, self.counting, q)
if int(self.i) <= 0:
print("Go")
self.master.destroy()
else:
self.label.configure(text="%d" % self.i)
print(i)
self.label.after(1000, self.counting, q)
def printX(q):
for i in range(10):
print("test")
q.put(9-i)
time.sleep(1)
return
if __name__ == '__main__':
q = multiprocessing.Queue()
n = multiprocessing.Process(name='Process2', target=printX, args = (q,))
n.start()
GUI = Countdown(q)
GUI.master.mainloop()
Multiprocessing does not function inside of the interactive Ipython notebook.
Multiprocessing working in Python but not in iPython As an alternative you can use spyder.
No code will run after you call mainloop until the window has been destroyed. You need to start your other process before you call mainloop.
You are calling wrong the after function. The second argument must be the name of the function to call, not a call to the function.
If you call it like
self.label.after(1000, self.counting(q))
It will call counting(q) and wait for a return value to assign as a function to call.
To assign a function with arguments the syntax is
self.label.after(1000, self.counting, q)
Also, start your second process before you create the window and call counting.
n = multiprocessing.Process(name='Process2', target=printX, args = (q,))
n.start()
GUI = Countdown(q)
GUI.master.mainloop()
Also you only need to call mainloop once. Either position you have works, but you just need one
Edit: Also you need to put (9-i) in the queue to make it count down.
q.put(9-i)
Inside the printX function

Scale not updating during key inputs

I am currently working on scales using tkinter. I have gotten the code to work except one thing. I have bound keys to the motion of a servo motor. When I press the keys however the scale does not follow what the key bindings do.How can I get the scale to follow the key bindings?
Please see code below
....GPIO setup code above not shown...
def fMin(event):
iDCServo = 2.5
pServo.ChangeDutyCycle(iDCServo) #this is pwm code for the servo motor
def fMin2(event):
iDCServo = 7.5
pServo.ChangeDutyCycle(iDCServo)
def fMax(event):
iDCServo = 12.5
pServo.ChangeDutyCycle(iDCServo)
def fMax2(event):
iDCServo = 7.5
pServo.ChangeDutyCycle(iDCServo)
def fOperation():
global guiSliderServo1, iLoop
while True:
win = Tk()
win.wm_title(">>>Servo Slider<<<")
win.geometry("800x100+0+0")
guiSliderServo1 = Scale(win, from_=-45, to_=45, orient=HORIZONTAL, length_=700, sliderlength_=10, tickinterval_=5, command=fSliderServo1)
guiSliderServo1.set(0)
guiSliderServo1.grid(row=0)
guiSliderServo1.pack(side=TOP)
guiSliderServo1.bind('<Key-q>', fMin)
guiSliderServo1.bind('<KeyRelease-q>', fMin2)
guiSliderServo1.bind('<Key-e>', fMax)
guiSliderServo1.bind('<KeyRelease-e>', fMax2)
guiSliderServo1.focus_set()
guiButtonExit = Button(win, text="Exit Slider", command=quit)
guiButtonExit.pack(side=BOTTOM)
win.mainloop()
...there is some remaining code regarding looks, functions and imports not shown, not sure, but probably would just clutter the real question.
Thank you
The while True hogs the computer so there is no time left for Tkinter to update the widget. You should be able to just delete the while True in the above code and be fine because Tkinter's mainloop() essentially does the same thing, i.e. continually checks for a keypress. Also mixing grid and pack, you use both, yields unknown results. Settle on one and use it. Note that this code does not change the scale, and the Button command should be win.quit.

Resources