Insert string to tkinter text widget from other thread - python

I've read at least 25 similar questions on this site, and I simply cannot get this working.
As it stands i'm just trying to build a simple chat app with a client and server. The GUI will be running on a separate thread to the logic to ensure things stay fluid and don't lock up. I've trimmed most of the logic out of the code to isolate the problem
import socket, csv, datetime, tkinter as tk, threading
from tkinter import ttk
interface = tk.Tk()
test = tk.StringVar()
test.set("String Var Test")
class serverInterface():
def __init__(self, interface):
global test
self.messageLog = tk.Text(interface, height=10, state="disabled", yscrollcommand="scrollBar.set")
self.scrollBar = ttk.Scrollbar(interface, command=self.messageLog.yview).grid(row=0, column=2, sticky="nsew")
self.messageLog.grid(row=0, column=0, columnspan=2)
test.trace("w", serverInterface.guiUpdate(self))
def guiUpdate(self):
self.messageLog.insert(tk.END, test)
class server():
def __init__(self):
global test
print("Server thread")
while True:
test.set("Updated from server object")
interface.title("Server")
serverInterface = threading.Thread(target=serverInterface(interface)) #Create serverInterface object
server = threading.Thread(target=server, daemon=True) # Create server object
server.start()
interface.mainloop()
This results in the console being spammed with Exception in Tkinter callback Traceback (most recent call last): File "C:\Users\thoma\AppData\Local\Programs\Python\Python38\lib\tkinter\__init__.py", line 1883, in __call__ return self.func(*args) TypeError: 'NoneType' object is not callable
I've also tried to make use of Queue() as I've seen others suggest, but that just results in a different set of errors and I feel using StringVar() is probably the better way of doing this.
I appreciate that there's probably some lines in this code that don't need to be there, they're just leftovers from all the different attempts at bodging it :/
Any solutions would be appreciated.

The error you're asking about is due to this line:
test.trace("w", serverInterface.guiUpdate(self))
That line is functionally identical to this:
result = serverInterface.guiUpdate(self)
test.trace("w", result)
Since guiUpdate(self) returns None, you're asking tkinter to call None. Hence the error TypeError: 'NoneType' object is not callable
The trace method must be given a callable (ie: a reference to a function). In this specific case you need to use self.guiUpdate.
The trace will automatically pass arguments to the function, so you need to properly define the function to accept those arguments. You also have a bug where you're trying to insert an object (test) in the text widget rather than the text contained in the object.

Related

For loop inside a method : TypeError positional arguments

I'm trying to create a simple Gui with tkinter using classes.
But I don't really understand how to make the for-loop work inside the count method, could anyone tell me where should I add the missing argument?
from tkinter import *
import time
class App:
def __init__(self, master):
self.container1 = Frame(master)
self.container1.pack()
self.button1 = Button(self.container1, text="count")
self.button1.bind("<Button-1>", self.count)
self.button1.pack()
def count(self):
for i in range(100):
self.button1["text"] = str(i)
time.sleep(1)
root = Tk()
Myapp = App(root)
root.mainloop()
The error is:
Exception in Tkinter callback
Traceback (most recent call last):
File "/usr/lib/python3.5/tkinter/__init__.py", line 1553, in __call__
return self.func(*args)
TypeError: count() takes 1 positional argument but 2 were given
When you bind an event, a positional argument event is provided to the callback function.
Change your count method to this:
def count(self, event):
You will also need to get rid of time.sleep(1) since .sleep() is a blocking call, which means that it will block the tkinter mainloop which will cause your program to not respond.

Python tkinter trace error

I'm trying to write a GUI for my code. My plan is to use tkinter's StringVar, DoubleVar, etc. to monitor my input in real time. So I found out the DoubleVar.trace('w', callback) function. However, every time I make the change I get an exception:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Users\Anaconda2\lib\lib-tk\Tkinter.py", line 1542, in __call__
return self.func(*args)
TypeError: 'NoneType' object is not callable
I have no idea what's going wrong. I'm using python 2.7
My code is as follows:
from Tkinter import *
class test(Frame):
def __init__(self,master):
Frame.__init__(self,master=None)
self.main_frame = Frame(master);
self.main_frame.pack()
self.testvar = DoubleVar()
self.slider_testvar = Scale(self.main_frame,variable = self.testvar,from_ = 0.2, to = 900, resolution = 0.1, orient=HORIZONTAL,length = 300)
self.slider_testvar.grid(row = 0, column = 0, columnspan = 5)
self.testvar.trace('w',self.testfun())
def testfun(self):
print(self.testvar.get())
root = Tk()
root.geometry("1024x768")
app = test(master = root)
root.mainloop()
Consider this line of code:
self.testvar.trace('w',self.testfun())
This is exactly the same as this:
result = self.testfun()
self.testvar.trace('w', result)
Since the function returns None, the trace is going to try to call None, and thus you get 'NoneType' object is not callable
The trace method requires a callable. That is, a reference to a function. You need to change that line to be the following (notice the missing () at the end):
self.testvar.trace('w',self.testfun)
Also, you need to modify testfun to take arguments that are automatically passed by the tracing mechanism. For more information see What are the arguments to Tkinter variable trace method callbacks?

Gui with button to open port scanner

I am making a GUI with tkinter that allows me to click a button that will run a port scan. I have a script for a port scan that functions correctly, I have managed to open the port scanner through the button on the GUI but then I receive an error that I otherwise don't receive when running the port scanner alone.
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Users\Steve\AppData\Local\Programs\Python\Python35-32\lib\tkinter\__init__.py", line 1550, in __call__
return self.func(*args)
File "<string>", line 51, in Scan
NameError: name 'IP_Input' is not defined
My code:
class CallWrapper:
"""Internal class. Stores function to call when some user
defined Tcl function is called e.g. after an event occurred."""
def __init__(self, func, subst, widget):
"""Store FUNC, SUBST and WIDGET as members."""
self.func = func
self.subst = subst
self.widget = widget
def __call__(self, *args):
"""Apply first function SUBST to arguments, than FUNC."""
try:
if self.subst:
args = self.subst(*args)
return self.func(*args) # THIS IS THE ERROR #
except SystemExit:
raise
except:
self.widget._report_exception()
class XView:
"""Mix-in class for querying and changing the horizontal position
of a widget's window."""
def xview(self, *args):
"""Query and change the horizontal position of the view."""
res = self.tk.call(self._w, 'xview', *args)
THIS IS THE CODE FOLLOWING FOR THE LINE 51 ERROR
def Scan():
print ('Scan Called.') #Debugging
IP = str(IP_Input.get(0.0, tkinter.END)) #THIS IS ERROR LINE 51#
print ("IP #Debugging")
Start = int(PortS.get(0.0, tkinter.END))
End = int(PortE.get(0.0, tkinter.END))
TestSocket = socket.socket()
CurrentPort = Start
OpenPorts = 0
print ('Starting scan...')
HowFar = int(CurrentPort/End * 100)
ProgText = HowFar, r'%'
Label1.config(text=('Percentage Done:', ProgText))
The problem is with your exec statement. You're opening your other .py file named port_scanner.py and then calling exec(open("./port scanner.py)).
This just isn't going to work.
Why this doesn't work:
When you do exec(open("path to .py file").read()) exec is of course executing this code, but the problem is that the global variables in this file aren't within the scope.
So, to make this work (which I don't recommend) you'd have to use:
exec(open(path).read(), globals())
From the documentation
If the globals dictionary does not contain a value for the key builtins, a reference to the dictionary of the built-in module builtins is inserted under that key. That way you can control what builtins are available to the executed code by inserting your own builtins dictionary into globals before passing it to exec().
If you really want to call your file this way then you should just use os.system.
Alternative approach:
You really don't need to call your file this way. You now have two instances of Tk() running. If you need another window then a widget is provided for this purpose. It is the Toplevel widget. You can restructure your code to create a Toplevel instance containing the port scanner app on your button click. An example being, create your port scanner app with the Toplevel widget (in your other file if you wish) then import the "app" into your file and on the button click have it initialize the app.
Additional Notes:
You're calling a while loop and if this runs (for any noticeable amount of time) then this is going to block the GUI's main event loop and causing your GUI to "hang".
Your first guess should not be that a part of the widely tested and used python standard library is flawed. The problem is (99.9% of the time)
while True:
print("In your own code.")

TypeError: 'NoneType' object is not callable - Python Tkinter

Hi guys i'm making a Temperature Converter based on Tkinter in Python and it was all good but now i'm getting this error. I looked here on stackoverflow for this error, i found a lot of them but i couldn't figured out, so i'm here to post my code to see if you guys can help. The program crashes exactly when i click a option on the OptionMenu. (om_input specifically)
Here is the error:
Traceback (most recent call last):
TypeError: 'NoneType' object is not callable
It throws another exception:
AttributeError: 'StringVar' object has no attribute '_report_exception'
Here are snippets of my code:
This function is supposed to be called everytime the user selects an option in the OptionMenu in the GUI.
def check_entry():
if temperature_input.get() == "Celsius":
celsius_converter(value_entry.get(), temperature_output.get(), output_entry)
elif temperature_input.get() == "Kelvin":
kelvin_converter(value_entry.get(), temperature_output.get(), output_entry)
else:
fahrenheit_converter(value_entry.get(), temperature_output.get(), output_entry)
The vars to be used with the OptionMenus
root = Tk()
temperature_list = ["Celsius", "Kelvin", "Fahrenheit"]
temperature_input = StringVar(root)
temperature_input.set(temperature_list[0])
temperature_output = StringVar(root)
temperature_output.set(temperature_list[0])
output_entry = Entry(root, state=NORMAL)
Initialization of the OptionMenus
om_input = OptionMenu(root, temperature_input, *temperature_list, command=check_entry)
om_output = OptionMenu(root, temperature_output, *temperature_list, command=check_entry)
root.mainloop()
I solved it and the problem was in a method that i didn't post here, stupidly, and i apologize for that.
The method was like that:
def update_entry():
temp_input.trace("w", check_entry())
root.after(1, update_entry)
I went through Tkinter documentations and read about StringVar and the trace() method and i just changed "w" to "u", worked charmly. Thanks for your time.

Python:Function takes no arguments

When I run this code I get the error message:
File "Start.py", line 22, in <module>
c.lo()
TypeError: lo() takes no arguments (1 given)
I don't know exactly why I am getting this error could someone please explain?
I know it's saying that I put an argument when calling that function but I don't understand why that is?
If someone could shed some light on this issue that would be great.
import subprocess as sp
import Tkinter as Tk
from Tkinter import *
root = Tk()
text = Text(root)
class Console:
def Start():
proc = sp.Popen(["java", "-Xmx1536M", "-Xms1536M", "-jar", ".jar"],stdin=sp.PIPE,stdout=sp.PIPE,)
def lo():
while True:
line = proc.stdout.readline()
text.insert(INSERT,line)
text.pack()
if (line == "Read Time Out"):
proc.stdin.write('stop')
if (line == "Unloading Dimension"):
text.insert(INSERT,"Ready for command")
text.pack()
c = Console()
c.Start()
c.lo()
root.mainloop()
Methods always get the instance as the first argument.
Your method definitions should look like:
def some_method(self):
# do_stuff
In short, that is because lo() is a method of the class Console which is always passed the instance as first argument. So lo() must define a parameter (mostly called self) to hold that argument:
class Console:
def start(self): # functions and methods should have lowercase names
self.proc = sp.Popen(...)
def lo(self):
line = self.proc.stdout.readline()
...
I am surprised that your Start() call worked; it has the same issue.

Categories