I want to change the text displaying in frame after my mainloop() has been called. I have created loginfo function to append text in my string but nothing happens. The GUI gets started and displays the text originally contained in it("hi"), I don't see the text I add through loginfo function ("hello") and after exiting the GUI I get the below error.
Traceback (most recent call last):
File "1.py", line 5, in <module>
monitor.loginfo()
File "/home/shreyas/Desktop/test/main.py", line 45, in loginfo
self.text.configure(state='normal')
File "/usr/lib/python3.8/tkinter/__init__.py", line 1637, in configure
return self._configure('configure', cnf, kw)
File "/usr/lib/python3.8/tkinter/__init__.py", line 1627, in _configure
self.tk.call(_flatten((self._w, cmd)) + self._options(cnf))
_tkinter.TclError: invalid command name ".!frame.!text"
My task is to create a function that i will call any time with the text i want to insert.
The function will be called after the mainloop is running as i recieve the text to display.
These are the 2 files I created:
main.py
import tkinter
from tkinter import *
class Monitor:
def __init__(self):
self.root = Tk()
self.root.title('Monitor')
self.root.geometry("800x400")
self.root.grid_columnconfigure((0,1), weight=1)
self.root.grid_rowconfigure(0, weight=1)
"""-----------------------------------------------"""
self.console = Frame(self.root,borderwidth=1)
self.console.grid(row = 0, column = 0, sticky = W+E+N+S)
self.console.grid_columnconfigure(0, weight=1)
self.console.grid_rowconfigure(2, weight=1)
self.lbl_c = Label(self.console, text="console",bg='white')
self.lbl_c.grid(row = 1, column = 0, sticky = W+E+N+S)
self.text = tkinter.Text(self.console)
self.text.grid(row = 2, column = 0,rowspan = 3, columnspan = 1, sticky = N+S+E+W)
self.text.insert(tkinter.END,"hi")
self.text.configure(state='disabled')
"""------------------------------------------------"""
self.fm = Frame(self.root,borderwidth=1)
self.fm.grid(row = 0, column = 1, sticky = W+E+N+S)
self.fm.grid_columnconfigure(0, weight=1)
self.fm.grid_rowconfigure(2, weight=1)
self.lbl_fm = Label(self.fm, text="frequency_monitor",bg='white')
self.lbl_fm.grid(row = 1, column = 0, sticky = W+E+N+S)
self.text1 = tkinter.Text(self.fm)
self.text1.grid(row = 2, column = 0,rowspan = 1, columnspan = 1, sticky = N+S+E+W)
self.text1.insert(tkinter.END,"<---------- Frequency Monitor ---------->\n\n"+"Camera100\n"+"Frequency: 9.6 CPU Time: 3.0ms\n"+("----------------------------------------")+"Screen100\n"+"Frequency: 29.8 CPU Time: 6.0ms\n"+("----------------------------------------"))
self.text1.configure(state='disabled')
def loginfo(self):
self.text.configure(state='normal')
self.text.insert(tkinter.END,"hello")
self.text.update()
self.text.configure(state='disabled')
1.py
import main as m
monitor = m.Monitor()
monitor.root.mainloop()
monitor.loginfo()
I use python 3.1 to run my code. Can someone please tell me what's causing the error and how could I achieve the expected result.
update:
when i use mainloop() like so
import main as m
monitor = m.Monitor()
monitor.root.mainloop()
monitor.root.update()
monitor.root.update_idletasks()
monitor.loginfo()
i get the same error but when i use while
import main as m
monitor = m.Monitor()
#monitor.root.mainloop()
#monitor.loginfo()
while True:
monitor.root.update()
monitor.root.update_idletasks()
monitor.loginfo()
it updates text and keeps updating it since i called loginfo in while But it doesnot update if i call it outside the while loop.
The code after mainloop() gets called only after your applications is closed. So after the application is closed, the method is called, but the widgets used in the method is destroyed. So change your code to:
monitor = Monitor()
monitor.loginfo()
monitor.root.mainloop()
This way, the function is called before you exit the GUI. Think of mainloop() as a while loop that keeps updating till the window is closed. Technically saying mainloop() is same as:
while True: # Only exits, because update cannot be used on a destroyed application
root.update()
root.update_idletasks()
Edit:
Since you wanted a delay, you have to add a button or something that calls this method while the GUI is active, an example is to use after to show the function after some time, like:
monitor = Monitor()
monitor.root.after(1000,monitor.loginfo) # Cause a delay of 1 second or 1000 millisecond
monitor.root.mainloop()
Related
I am constructing a simple hex editor in Python using Tkinter. I want to be able to indicate selection of a value (the "0000"s) in the GUI by changing the colors of a pressed button.
To implement this, I instantiated each button as a class inside a for loop and put each instance into a list to reference them later in the code. When I attempt to alter the Button API properties of the classes in the while loop, the IDLE prints an error when the hex editor is closed:
Traceback (most recent call last):
File "C:/Users/Administrator/Desktop/Files/Python/hex editor.py", line 64, in <module>
ml.button.configure(bg='#00FF00', fg='#000000')
File "C:\Users\Administrator\AppData\Local\Programs\Python\Python36\lib\tkinter\__init__.py", line 1479, in configure
return self._configure('configure', cnf, kw)
File "C:\Users\Administrator\AppData\Local\Programs\Python\Python36\lib\tkinter\__init__.py", line 1470, in _configure
self.tk.call(_flatten((self._w, cmd)) + self._options(cnf))
_tkinter.TclError: invalid command name ".!frame.!frame.!button"
The intended behavior of the button is that when any "0000" is clicked, the selected button will become green and remain so until another button is pressed. (i.e the pressed button will turn green and the previously selected button will turn black)
Source Code:
from tkinter import *
selected_i = 0
selected_j = 0
data_w = 16
address_w = 8
class mem_location:
def __init__(self, row, column, data):
self.row = row
self.column = column
self.data = data
self.button = Button(subframe,
text=self.data,
font=('Consolas',9),
bd=0,
command=self.select)
self.button.pack(side=LEFT)
self.button.configure(bg='#000000', fg='#00FF00')
def select(self):
selected_i = self.row
selected_j = self.column
root = Tk()
root.configure(bg="black")
root.title('Hex Editor')
frame = Frame(root,
padx=30,
pady=10,
bg='black')
frame.pack()
ml_list = []
for i in range((1<<address_w)>>4):
subframe = Frame(frame,
padx=10,
bg='black')
subframe.pack()
addr_label = Label(subframe,
text=hex(i<<4)+" ",
bg='black',
fg='#00FF00',
width=5)
addr_label.pack(side = LEFT)
for j in range(16):
ml_list.append(mem_location(i, j, '0000'))
root.mainloop()
while True:
for ml in ml_list:
if selected_i == ml.row and selected_j == ml.column:
ml.button.configure(bg='#00FF00', fg='#000000')
else:
ml.button.configure(bg='#000000', fg='#00FF00')
I am currently in the process of learning about Tkinter. Why can't I modify the class's Button configuration and how can this be fixed?
The code after root.mainloop will not run until the window is not closed, so you are trying to modify the buttons after the window has been closed.
Instead, you could move the code in the submit method like this:
from tkinter import *
# You don’t need selected_i and selected_j anymore
data_w = 16
address_w = 8
class MemLocation:
def __init__(self, data):
# Same code without self.row and self.column
def select(self):
for ml in [ml_ for ml_ in ml_list if ml_ is not self]: # All elements except this one
ml.button.config(bg='#000', fg='#0F0')
self.button.config(bg='#0F0', fg='#000')
# Same code here
root.mainloop()
#END
Note that I renamed the class according to PEP 8 but there are many other improvement possible like making MemLocation inherit from Frame or Button, or adding event listeners to make the code cleaner.
I want to make a simple GUI program by Python with Tkinter.
I downloaded winPython and extracted its python3.5.2 directory as my python environment.
Then, I used the SpecTcl GUI builder provided in this thread.
I built a simple GUI with this builder, the GUI contains only a button and a text area. The builder generates 2 python files, one is "SimpleGUI_ui.py", the other is "SimpleGUI.py." I put both .py files into python3.5.2 directory and run python3.5.2.exe SimpleGUI.py to display the GUI.
Below is the SimpleGUI.py code. Omitted some codes that is not very important.
""" simpleGUI.py --
UI generated by GUI Builder Build 146 on 2016-12-05 22:47:05 from:
C:/Users/User/Downloads/guimaker/simpleGUI.ui
This file is auto-generated. Only the code within
'# BEGIN USER CODE (global|class)'
'# END USER CODE (global|class)'
and code inside the callback subroutines will be round-tripped.
The 'main' function is reserved.
"""
from tkinter import *
from simpleGUI_ui import SimpleGUIcd
# BEGIN USER CODE global
# END USER CODE global
class CustomSimpleGUI(SimpleGUI):
pass
# BEGIN CALLBACK CODE
# ONLY EDIT CODE INSIDE THE def FUNCTIONS.
# _button_1_command --
# Callback to handle _button_1 widget option -command
def _button_1_command(self, *args):
SimpleGUI._text_1.config(text="hello world") # This is useless
# _text_1_xscrollcommand --
# Callback to handle _text_1 widget option -xscrollcommand
def _text_1_xscrollcommand(self, *args):
pass
# I omit another yScrollCommand here.
def main():
# Standalone Code Initialization, DO NOT EDIT
try: userinit()
except NameError: pass
root = Tk()
demo = CustomSimpleGUI(root)
root.title('simpleGUI')
try: run()
except NameError: pass
root.protocol('WM_DELETE_WINDOW', root.quit)
root.mainloop()
if __name__ == '__main__': main()
Below is the SimpleGUI_ui.py code:
""" simpleGUI_ui.py --
UI generated by GUI Builder Build 146 on 2016-12-05 22:47:05 from:
C:/Users/User/Downloads/guimaker/simpleGUI.ui
THIS IS AN AUTOGENERATED FILE AND SHOULD NOT BE EDITED.
The associated callback file should be modified instead.
"""
import tkinter
import os # needed for relative image paths
class SimpleGUI(object):
_images = [] # Holds image refs to prevent GC
def __init__(self, root):
# Widget Initialization
self._button_1 = tkinter.Button(root,
text = "_button_1",
)
self._text_1 = tkinter.Text(root,
height = 0,
width = 0,
)
# widget commands
self._button_1.configure(
command = self._button_1_command
)
self._text_1.configure(
xscrollcommand = self._text_1_xscrollcommand
)
self._text_1.configure(
yscrollcommand = self._text_1_yscrollcommand
)
# Geometry Management
self._button_1.grid(
in_ = root,
column = 1,
row = 1,
columnspan = 1,
ipadx = 0,
ipady = 0,
padx = 0,
pady = 0,
rowspan = 1,
sticky = ""
)
self._text_1.grid(
in_ = root,
column = 1,
row = 2,
columnspan = 1,
ipadx = 0,
ipady = 0,
padx = 0,
pady = 0,
rowspan = 1,
sticky = "news"
)
# Resize Behavior
root.grid_rowconfigure(1, weight = 0, minsize = 40, pad = 0)
root.grid_rowconfigure(2, weight = 0, minsize = 40, pad = 0)
root.grid_columnconfigure(1, weight = 0, minsize = 40, pad = 0)
root.grid_columnconfigure(2, weight = 0, minsize = 40, pad = 0)
So I followed its comment: modify the callbacks, but not _ui code.
Now the problem I encountered is I couldn't even simply change the text inside _text_1 widget. I tried call SimpleGUI._text_1.config(text="hello world") but it gave me the exception below when I clicked on the button on GUI:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Users\User\Downloads\python-3.5.2\lib\tkinter\__init__.py", line 1550, in __call__
return self.func(*args)
File "simpleGUI.py", line 29, in _button_1_command
SimpleGUI._text_1.config(text="hello world")
AttributeError: type object 'SimpleGUI' has no attribute '_text_1'
Could anyone please help me, how could I accept the _text_1 widget and configure its text in the callback function so I could make my GUI interactive, and hopefully I could use the same way to access & control any other widgets on GUI. Thanks!
Too bad that I couldn't find a way to access the widget without modifying the SimpleGUI_ui.py code.
#DYZ is right, I added a function in SimpleGUI_ui.py and in callback function of SimpleGUI.py
I invoked that function, passed self as the 1st parameter, then successfully
accessed the widget.
In SimpleGUI_ui.py:
class MailLogChecker(object):
# other code omitted
def foo(self):
self._text_1.insert(1.0, 'abc')
In SimpleGUI.py:
class CustomSimpleGUI(SimpleGUI):
# other code omitted
def _button_1_command(self, *args):
# call like this and pass self to access widgets in UI
SimpleGUI.foo(self)
I'm still looking for a way that changing only SimpleGUI.py is sufficient to access the UI widget.
Anyway, thanks for #DYZ giving me the hint.
The goal of this program is to generate a sine wave using a 12 bit DAC. The code is as follows:
from Tkinter import *
import smbus, time, math, random
bus = smbus.SMBus(1)
address = 0x60
t=time.time()
class RPiRFSigGen:
# Build Graphical User Interface
def __init__(self, master):
self.start
frame = Frame(master, bd=10)
frame.pack(fill=BOTH,expand=1)
# set output frequency
frequencylabel = Label(frame, text='Frequency (Hz)', pady=10)
frequencylabel.grid(row=0, column=0)
self.frequency = StringVar()
frequencyentry = Entry(frame, textvariable=self.frequency, width=10)
frequencyentry.grid(row=0, column=1)
# Start button
startbutton = Button(frame, text='Enter', command=self.start)
startbutton.grid(row=1, column=0)
def start(self):
#self.low_freq=IntVar
low_freq = float(self.frequency.get())
out = 4095/2 + (math.sin(2*math.pi*low_freq*t))
#out = math.floor(out)
int(math.floor(out))
print (out)
bus.write_byte_data(address,0,out)
sendFrequency(low_freq)
# Assign TK to root
root = Tk()
# Set main window title
root.wm_title('DAC Controller')
root.geometry('250x150+650+250')
# Create instance of class RPiRFSigGen
app = RPiRFSigGen(root)
# Start main loop and wait for input from GUI
root.mainloop()
When I run the program I receive the following error after the value "out" is printed:
2046.18787764
Exception in Tkinter callback
Traceback (most recent call last):
File "/usr/lib/python2.7/lib-tk/Tkinter.py", line 1437, in __call__
return self.func(*args)
File "/home/pi/DAC Controller.py", line 40, in start
bus.write_byte_data(address,0,out)
TypeError: integer argument expected, got float
It would appear that int(math.floor(out)) is not converting out to an integer because "out" is being printed as float still. Any suggestions?
int(math.floor(out))
This will create an integer version of out, but you aren't assigning it to anything, so it just gets discarded. If you want the changes to be reflected in the value of out, try:
out = int(math.floor(out))
I am very new to Python and have to write a simple GUI program, which I chose to do in tkinter for simplicity.
The GUI I want should be very similar to the dialog one often experiences when installing a program on Windows (where would you like to install, what modules would you like, etc). Basically when it is run in python3.3, I want a window to appear with some options taking up most of the window and 'next', 'back' and 'cancel' buttons at the bottom; by clicking the 'next' button, the current window is closed and a new one opens which looks the same, except that it has different options (or perhaps it is the same window but its contents have been destroyed, I'm not sure which would be better). The rough layout I want is shown in this image
I have looked around for example codes which do something similar to this but have not been able to find any. I have seen this answer, but it's not quite what I want. I have used this tutorial to learn what I know about tkinter but I can't find an answer in it.
Here is my extremely poor attempt at a simplified version of what I want to do: When I run the code it creates a window with two buttons. The 'Quit' button works fine; however, when I click the 'Next' button it closes the window and opens a new one as desired but it also opens another window.
from tkinter import *
from tkinter import ttk
def win1():
mainframe = ttk.Frame(root, padding = '3 3 12 12')
mainframe.grid(column = 0, row = 0, sticky = (N, W, E, S))
mainframe.columnconfigure(0, weight = 1)
mainframe.rowconfigure(0, weight = 1)
ttk.Button(mainframe, text = 'Next', command = win2).grid(
column = 1, row = 1, sticky = W)
ttk.Button(mainframe, text = 'Quit', command = quit).grid(
column = 1, row = 2, sticky = W)
root.mainloop()
def quit():
root.destroy()
def win2():
quit()
new = Toplevel()
new.title('Window 2')
new = ttk.Frame(root, padding = '3 3 12 12')
new.grid(column = 0, row = 0, sticky = (N, W, E, S))
new.columnconfigure(0, weight = 1)
new.rowconfigure(0, weight = 1)
ttk.Button(mainframe, text = 'Next', command = win2).grid(
column = 1, row = 1, sticky = W)
root = Tk()
win1()
This gives the following error message (which I don't understand):
Exception in Tkinter callback
Traceback (most recent call last):
File "/usr/lib/python3.3/tkinter/__init__.py", line 1478, in __call__
return self.func(*args)
File "<stdin>", line 23, in win2
File "/usr/lib/python3.3/tkinter/ttk.py", line 733, in __init__
Widget.__init__(self, master, "ttk::frame", kw)
File "/usr/lib/python3.3/tkinter/ttk.py", line 553, in __init__
tkinter.Widget.__init__(self, master, widgetname, kw=kw)
File "/usr/lib/python3.3/tkinter/__init__.py", line 2078, in __init__
(widgetName, self._w) + extra + self._options(cnf))
_tkinter.TclError: this isn't a Tk applicationNULL main window
Apart from the fact that it doesn't actually do what I want, I feel like I am going about this the completely wrong way (defining the windows in a function, etc), and will run into a lot of trouble when I want to make it more complicated. Would anyone be able to rewrite my code in a better way and in a way that helps me build to more complicated programs, offer a resource for learning what I need to make the program I want or even offer advice? Thanks.
Your problem here is closing the root Tk window when you call quit(). Don't do that. Once you close that you have unloaded Tk and it can't process the Window system messages properly for you. Instead, if you want to create an application that is a series of dialogs, hide the root window by withdrawing it and create each dialog as a new toplevel with the hidden root toplevel as the parent.
I amended your example, with my hopefully correct understanding of what you are trying to achieve.
from tkinter import *
from tkinter import ttk
def win1():
mainframe = ttk.Frame(root, padding = '3 3 12 12')
mainframe.grid(column = 0, row = 0, sticky = (N, W, E, S))
mainframe.columnconfigure(0, weight = 1)
mainframe.rowconfigure(0, weight = 1)
ttk.Button(mainframe, text = 'Next', command = win2).grid(
column = 1, row = 1, sticky = W)
ttk.Button(mainframe, text = 'Quit', command = quit).grid(
column = 1, row = 2, sticky = W)
root.mainloop()
def quit():
root.destroy()
def win2():
root.withdraw()
new = Toplevel()
new.title('Window 2')
mainframe = ttk.Frame(new, padding = '3 3 12 12')
mainframe.grid(column = 0, row = 0, sticky = (N, W, E, S))
mainframe.columnconfigure(0, weight = 1)
mainframe.rowconfigure(0, weight = 1)
ttk.Button(mainframe, text = 'Next', command = win2).grid(
column = 1, row = 1, sticky = W)
root = Tk()
win1()
Hope this helps.
When I try to run the code it gives me this nasty error:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Python33\lib\tkinter\__init__.py", line 1475, in __call__
return self.func(*args)
File "E:\Tkinter\count_num_CHALLENGE.py", line 39, in count_num
for num in range(start, end):
TypeError: 'Entry' object cannot be interpreted as an integer
Please help I don't know what is wrong! I tried int() Around entry but that did not work either If you guys can help me out that would be great. As I am in need of assistance
from tkinter import *
class Application(Frame):
def __init__(self, master):
super(Application, self).__init__(master)
self.grid()
self.create_widgets()
def create_widgets(self):
# Create the instruction Label
Label(self,
text = "Enter a starting number then an ending number."
).grid(row = 0, column = 0, sticky = W)
# Create the entry box for starting/ending
self.starting_num = Entry(self)
self.starting_num.grid(row = 2, column = 0, sticky = W)
self.ending_num = Entry(self)
self.ending_num.grid(row = 3, column = 0, sticky = W)
# Create the text box
self.result_txt = Text(self, width = 20, height = 10, wrap = WORD)
self.result_txt.grid(row = 4, column = 0, columnspan = 1)
# Submit button
Button(self,
text = "Count the numbers",
command = self.count_num
).grid(row = 5, column = 0, sticky = W)
def count_num(self):
start = self.starting_num
end = self.ending_num
for num in range(start, end):
print(num)
self.result_txt.delete(0.0, END)
self.result_txt.insert(0.0, count_num)
# Main
root = Tk()
root.title("Count the numbers")
app = Application(root)
root.mainloop()
In your code, self.starting_num is an instance of an Entry widget, but you are trying to use it as if it were a number just as the error message is telling you.
I'm going to guess your intent is to use the value of the entry widget, in which case you need to use something like start=int(self.starting_num.get()), though you will need to handle the case where the entry is emply or has non-digits in it. The same goes for ending_num