I am running tkinter library in the Odoo15 for a specific purpose.
I have created a custom python interpreter to run python code inside odoo.
To handle user inputs i specially designed a concept and via tkinter i am taking inputs from user.
In a code there may be more than one inputs and so i need to open the window more than one time. for example taken one input, Entered, closed window and then repeat the same process till final user input.
So in that case, at some movement my server getting terminated with a runtime error:
Tcl_AsyncDelete: cannot find async handler
Aborted (core dumped)
can anyone guide me how can i resolve this please ?
import tkinter as tk
import gc
root=tk.Tk()
root.geometry('800x200+600+300')
name_var=tk.StringVar()
var_1 = ''
def submit():
name=name_var.get()
global %s
var_1 = name
root.destroy()
name_label = tk.Label(root, text = 'Enter value', font=('calibre',10, 'bold'))
name_entry = tk.Entry(root,textvariable = name_var, font=('calibre',10,'normal'))
sub_btn=tk.Button(root,text = 'Submit', command = submit)
name_label.grid(row=0,column=0)
name_entry.grid(row=0,column=1)
sub_btn.grid(row=2,column=1)
root.mainloop()
when there is a line like value = input("Enter value")
i am replacing that line with the above code to take user input.
Looking forward to hear on this .. thanks
Without a minimal reproducible example it is more of a guessing than an answer.
But due to your request, I'll try my best guess. The information how you code this section below, can be crucial.
In a code there may be more than one inputs
and so i need to open the window more than
one time. for example taken one input,
Entered, closed window and then repeat the
same process till final user input.
If you destroy the root window, the instance of tkinter.Tk() and you try to retrieve the users input (data located in tkinter) of the destroyed instance you could run into trouble. Instead of creating new instances of tkinter.Tk use tkinter.Toplevel. You could hide the root window in many ways. I.e with overrideredirect and transparency.
TL;DR
Make sure you use the same instance of tkinter.Tk() through out the whole session and or connect the new instance with your server and vice versa.
Hope it will work out for you, especially because I don't know about odoo.
Related
Currently I'm working on a project of mine involving sensors, and showing that sensory data on a display via TKinter. Everythings written in Python 3.7.3.
The issue im currently handling, is to update the label in the window, while the mainloop is running.
What i mean by this, is that if i execute the script, first the window options get defined, then the update function gets defined with a while true loop. Then its supposed to start the window. Now because of the while true loop it does not reach the window.mainloop() point (obviously, the while loop doesn't break...). My interest was peaked and i tried to put the window.mainloop() function inside the while loop of the update (please don't blame me, i know my script is a spaghetti mess.) I figured out that i could run the whole thing in threads, and so i decided to thread the whole window process, and add queues for the sensor data. Now the while loop was still in the way and didnt work properly, and after a bit of googling i found a code snippet that might help me. After trying to implement it in my script, i got an exception "function init expects 3 arguments, but 4 were given.." (code below) and I'm kinda running out of ideas on this.
Bear in mind that im not raelly a developer, i just need a script that can handle sensor data, dispaly it in a window, and export the current data to a database. So go easy on the blame please.
Current Script:
import time
import board
import adafruit_dht
import threading
import queue
from tkinter import *
dhtDevice = adafruit_dht.DHT22(board.D4, use_pulseio=False)
tempQ = queue.Queue(maxsize=0)
humQ = queue.Queue(maxsize=0)
class windowMain:
def __init__(self):
self.tempC_label = Label(fenster, text="Placeholder TempC")
self.humidity_label = Label(fenster, text="Placeholder Humidity")
self.tempC_label.pack()
self.humidity_label.pack()
self.tempC_label.after(2000, self.labelUpdate)
self.humidity_label.after(2000, self.labelUpdate)
def labelUpdate(self, tempQ, humQ):
self.tempC_label.configure(text= tempQ.get() + "°C")
#this is just to confirm if the function called or not, to see if the label updated or not.
#if the label didnt update, and the function called, there is something wrong with the function
#if the label didnt update, and the function didnt call, there is a problem somwhere else
print("Current Temp: " +tempQ.get() + "°C")
self.label.after(2000, self.labelUpdate)
if __name__ == "__main__":
windowName = Tk()
windowName.title = ("Climatemonitor")
windowMain(windowName)
windowName.mainloop()
try:
windowThread = threading.Thread(target=windowMain, args=(tempQ, humQ, ))
windowThread.start()
except:
print("Unable to start thread")
while True:
try:
temperature_c= dhtDevice.temperature
tempText= temperature_c
tempText= str(tempText)
tempQ.put(tempText)
humidity = dhtDevice.humidity
humidityP = str(humidity)
#this one is just to check if the sensor reads data
print(
"Temp: {:.1f} C Humidity: {}% ".format(
temperature_c, humidity
)
)
time.sleep(2.0)
except RuntimeError as error:
print(error.args[0])
time.sleep(2.0)
continue
except Exception as error:
dhtDevice.exit()
raise error
time.sleep(2.0)
The ultimate goal is to display my sensor data, with a 2 second refresh (the HZ rate of the Sensor), while the sensor continues to read every 2 seconds.
I'd also like to add that this is my first time using Python, since im, again, not really a developer yet.
Thanks a bunch in advance for every critique and help
most simple way of doing this would be using a button to execute a function and then including your while loop in that function,
Using an button gives you an point where you can start running while instead of directly starting it as soon as you run your program
Sample code should be something like this,
import tkinter as t
def execute():
print('hello')
window = t.Tk()
window.title("system")
window.geometry("550x250")
b1 = t.Button(window, text="Start", width=15, command=execute)
b1.grid(row=1, sticky="W", padx=4)
window.mainloop()
As there will be no user interaction, a button can invoked using button.invoke method such as following,
import tkinter as t
def execute():
print('hello')
window = t.Tk()
window.title("system")
window.geometry("550x250")
b1 = t.Button(window, text="Start", width=0, command=execute)
#b1.grid(row=1, sticky="W", padx=4)
b1.invoke()
window.mainloop()
here removing .grid() will cause the button to disapper but can affect your GUI while updating the label value later , also have a look at this ->
Is there a way to press a button without touching it on tkinter / python?
Python tkinter button.invoke method trouble
This is my code below :
from tkinter import *
login = Tk()
username_1 = Entry()
username_1.pack(side=TOP)
input_username = login.username_1.get("1.0", END)
username = input_username
loginInfo = {"user1": "blue", "user2": "yellow", "user3": "green"}
if username in loginInfo:
print('Username correct!')
login.mainloop()
This is my error:
'AttributeError: '_tkinter.tkapp' object has no attribute 'username_1'':
When you create widgets, you are creating children of the root window. However, these children are not attributes of the root window. Thus, login.username_1 is invalid, just as the error is telling you.
In this specific case, the widget is simply username_1 (eg: input_username = username_1.get("1.0", END)). However, even that won't work for the following reasons:
the get method of the entry doesn't take arguments. You need to do username_1.get().
you are calling get about one millisecond after the entry has been created, well before the user has a chance to enter anything.
With a GUI toolkit you can't think of your program running linearly from top to bottom. Instead, you set things up to initially appear, and then you need to write functions that respond to events such as key presses and button clicks. For example, you might want to create a button with the label "Login" that calls a function to check for valid credentials. It would be within that function that you call the get() method.
Can you please provide information about what your code is supposed to do?
If you just want to avoid this error - change line 6 for this:
input_username = username_1.get()
I have a class, ClientGUI (make in tkinter), which represents the client's class for my application. In this class, upon pressing a button, a new tkinter window is opened (using a module I imported), which represents a UI for a drawing tool. When I close this drawing tool window, I want it to return a message to the client class that the drawing tool has been closed. The problem is that I receive this message only when I close the main client window as well. The code for all this is below:
This is the code that declares and starts the main client window:
root = Tk()
root.resizable(False, False)
root.title("Client")
root.protocol("WM_DELETE_WINDOW", onX)
clientGui = clientGUI(root) //clientGUI is the client class. It contains all the UI and functionality elements
root.mainloop()
This is the function I use to close it:
def onX():
answer = tkinter.messagebox.askquestion("Quit", "Are you sure you want to quit the application ?")
if answer == "yes":
root.destroy()
In the clientGUI, upon pressing a button, a new window is opened from a module I created, this is the code that starts the new window:
def startDrawingTool(self, username, password):
drawingToolStatus = drawingTool.startTool() // the new window is opened here
print(drawingToolStatus) // This only gets printed when I close the main client window(clientGui)
This is the startTool() function, declared in the drawingTool module I imported:
def startTool():
def onX():
answer = tkinter.messagebox.askquestion("Quit", "Quiting without saving may result in data loss, make sure to always save first. Are you sure you want to quit?")
if answer == "yes":
root.destroy()
root = Tk()
root.resizable(False, False)
root.title("Drawing program")
root.protocol("WM_DELETE_WINDOW", onX)
app = Application(root) // Application is the drawing tool class, contains all the UI elements and functionality
root.mainloop()
return "window closed" // This is how I want to let the main client class know that the drawing tool has been closed
Hopefully, I have provided all the code that you need to get an idea of the application and the issue I am facing. As I already said, I want to know why the "print(drawingToolStatus)" is only printed when the clientGui gets closed and it doesn't in face get printed as soon as I close the drawingTool.
Also, if you know a better way to have the two communicate, I'm open to improvements as this is the first Python application I work on.
I'm writing a 'wizard' type Python Tkinter GUI that collects information from the user and then performs several actions based on the user's entries: file copying, DB updates, etc. The processing normally takes 30-60 seconds and during that time, I want to:
Provide the user with text updates on the activity and progress
Prevent the user from closing the app until it's finished what it's doing
I started on the route of having the text updates appear in a child window that's configured to be trainsient and using wait_window to pause the main loop until the activities are done. This worked fine for other custom dialog boxes I created which have OK/cancel buttons that call the window's destroy method. The basic approach is:
def myCustomDialog(parent,*args):
winCDLG = _cdlgWin(parent,*args)
winCDLG.showWin()
winCDLG.dlgWin.focus_set()
winCDLG.dlgWin.grab_set()
winCDLG.dlgWin.transient(parent)
winCDLG.dlgWin.wait_window(winCDLG.dlgWin)
return winCDLG.userResponse
class _cdlgWin():
def __init__(self,parent,*args):
self.parent = parent
self.dlgWin = tk.Toplevel()
self.userResponse = ''
def showWin(self):
#Tkinter widgets and geometry defined here
def _btnOKClick(self):
#self.userResponse assigned from user entry/entries on dialog
self.dlgWin.destroy()
def _btnCancelClick(self):
self.dlgWin.destroy()
However this approach isn't working for the new monitor-and-update dialog I want to create.
First, because there's no user-initiated action to trigger the copy/update activities and then the destroy, I have to put them either in showWin, or in another method. I've tried both ways but I'm stuck between a race condition (the code completes the copy/update stuff but then tries to destroy the window before it's there), and never executing the copy/update stuff in the first place because it hits the wait_window before I can activate the other method.
If I could figure out a way past that, then the secondary problem (preventing the user from closing the child window before the work's done) is covered by the answers below.
So... is there any kind of bandaid I could apply to make this approach work the way I want? Or do I need to just scrap this because it can't work? (And if it's the latter, is there any way I can accomplish the original goal?)
self.dlgWin.overrideredirect(1) will remove all of the buttons (make a borderless window). Is that what you're looking for?
As far as I know, window control buttons are implemented by the window manager, so I think it is not possible to just remove one of them with Tkinter (I am not 100% sure though). The common solution for this problem is to set a callback to the protocol WM_DELETE_WINDOW and use it to control the behaviour of the window:
class _cdlgWin():
def __init__(self,parent,*args):
self.parent = parent
self.dlgWin = tk.Toplevel()
self.dlgWin.protocol('WM_DELETE_WINDOW', self.close)
self.userResponse = ''
def close(self):
tkMessageBox.showwarning('Warning!',
'The pending action has not finished yet')
# ...
I am currently making a chat application in python. I am having 2 separate codes: one for the server and one for the client. The server script is taking the login data of new clients that connects and in another thread manages the messages that he has to receive and send.
The client application is made into a class and works well, excepts that when the script requests the UI to show, the only new window is a empty one:
def __init__(self, master):
self.nr=0
self.frameul=self.tbox=self.txt=self.scrollbar=self.button=self.roottk=[0]*20
self.OameniSiIduri={}
self.LoginUI(master)
self.framestate=""
def ChatUI(self, peer_id):
no=self.no
self.no+=1
self.PeoplesAndId[peer_id]=no
self.base[no]=Toplevel()
self.theframe[no] = Frame(self.base[no])
self.theframe[no].pack()
self.entry[no] = Entry(self.theframe[no], width=95)
self.tbox[no] = Text(self.theframe[no], state=DISABLED, wrap=WORD)
self.button[no] = Button(self.theframe[no], text="Send", fg="green", command=lambda x=self.entry[no].get(), y=peer_id, z=self.tbox[nr]: self.Sendmsg(x,y,z), width=10)
self.tbox[no].pack(side=TOP, fill=X)
self.button[no].pack(side=RIGHT)
self.entry[no].pack(side=LEFT)
.....
All the vars and the functions are declared. Can anyone give me a hint about what can the cause of this problem is?
Found myself the mistake after searching all the night through the code. Apparently if i use this line:
self.theframe=self.tbox=self.entry=self.scrollbar=self.button=self.base=[0]*20
All the objects point to the same value.
My guess is, the code that is creating the UI is throwing an error that you are not seeing. For example, are you importing DISABLED and WORD properly? If not, the code would fail after creating the frame but before creating the other widgets, leaving you with an empty widget.
One way to debug this is to give each toplevel and frame a distinct color. This lets you see which are visible and which are not -- maybe you're looking at a window or frame and thinking it's one thing when it's something else.