This question already has answers here:
Why is Python running my module when I import it, and how do I stop it?
(12 answers)
Closed 4 years ago.
I am trying to build a GUI.
When I execute the application directly (i.e. double click the python file), I get a different result (console output) to importing it (mainloop).
I would like it to give the following console output:
c
d
e
f
g - from app
as I would like the main loop to be accessible after it has been imported as a module.
I am attempting to make the input and output controllable from an external file importing it as a module.
I get the expected output when I run the file, but when I import it as a module, it appears to run the main loop, as I get a Tkinter mainloop window output.
Here is the code:
class Application(Frame):
def __init__(self, master = None):
Frame.__init__(self, master)
self.text = Lines()
self.text.insert("\n\n\n\n\n")
self.waitingForInput = False
self.inText = ""
self.pack()
self.widgets()
def widgets(self):
self.L1 = Label(self)
self.L1["text"] = self.text.bottomLines
self.L1.pack(side = "top")
self.E1 = Entry(self)
self.E1["width"] = 40
self.E1.pack(side = "top")
self.B1 = Button(self)
self.B1["text"] = "Enter",
self.B1["command"] = self.giveInput
self.B1.pack(side = "top")
def giveInput(self):
if self.waitingForInput:
self.inText = self.B1.get()
self.waitingForInput = False
def getInput(self, output):
giveOutput(output)
self.waitingForInput = True
while True:
time.sleep(0.1)
if not self.waitingForInput:
break
return self.inText
def giveOutput(self, output):
self.text.insert(output)
self.L1["text"] = self.text.bottomLines
print self.text.bottomLines + " - from app"
root = Tk()
app = Application(master = root)
app.giveOutput("a \n b \n c \n d \n e \n f \n g")
The Lines class is essentially a stack of lines of text in a string, stacking more with insert(x) and accessing the final five lines of the stack with bottomLines.
Back on topic, when imported as a module, it runs the main loop, with a label containing what I assume to be 5 empty lines, an entry box and the "Enter" button. I do not want this. I want the same result as when I run the file directly, as I showed before.
I only want the box to appear when the app.mainloop method is called.
What have I done wrong, how is it wrong, and how can I correct it?
This will only run when the module is run directly, not when imported:
if __name__ == "__main__":
root = Tk()
app = Application(master = root)
app.giveOutput("a \n b \n c \n d \n e \n f \n g")
Related
Over here I am using multiprocessing to run multiple algorithms in tkinter. At first I tried using threading, but it can't work properly in my program. Below is an idea of my program workflow, it works something like this, but just different functions:
from tkinter import *
from multiprocessing import Process
def SquarFunc(Square):
for i in range(1,1000):
Square.set(str(i**2))
def CubeFunc(Cube):
for i in range(1,1000):
Cube.set(str(i**3))
if __name__ == "__main__":
window= Tk()
Square= StringVar()
Cube= StringVar()
window.geometry("500x500")
A= Label(window, textvariable= Square)
A.place(x=200, y=200)
B= Label(window, textvariable= Cube)
B.place(x=300, y=300)
Squaring= Process(target=SquarFunc, args=(Square, ))
Cubing= Process(target=CubeFunc, args=(Cube, ))
Squaring.start()#Error originates here
Cubing.start()
Squaring.join()
Cubing.join()
window.mainloop()
The error produced is this:
TypeError: cannot pickle '_tkinter.tkapp' object
Anybody knows how to fix this?? thanks in advance!
Here is an example of how to communicate with other processes if using multiprocessing (explanation is in comments, time.sleep is used just for the example because otherwise those loops will complete in a few microseconds):
from tkinter import Tk, StringVar, Label
from multiprocessing import Process, Manager
import time
def square_func(d, name):
for i in range(1, 1000):
# update data in the shared dict
d[name] = i
time.sleep(0.1)
def cube_func(d, name):
for i in range(1, 1000):
# update data in the shared dict
d[name] = i
time.sleep(0.1)
def update_string_vars(d, *variables):
for var in variables:
# get the value from shared dict
value = d[str(var)]
if value is not None:
# set string var to the value
var.set(str(value))
# schedule this to run again
window.after(100, update_string_vars, d, *variables)
# cleanup process upon closing the window in case
# processes haven't finished
def terminate_processes(*processes):
for p in processes:
p.terminate()
if __name__ == "__main__":
window = Tk()
window.geometry("500x500")
# bind the terminator to closing the window
window.bind('<Destroy>', lambda _: terminate_processes(
square_process, cube_process))
square_var = StringVar()
cube_var = StringVar()
Label(window, text='Square:').pack()
Label(window, textvariable=square_var).pack()
Label(window, text='Cube:').pack()
Label(window, textvariable=cube_var).pack()
# create the manager to have a shared memory space
manager = Manager()
# shared dict with preset values as to not raise a KeyError
process_dict = manager.dict({str(square_var): None, str(cube_var): None})
square_process = Process(
target=square_func, args=(process_dict, str(square_var))
)
cube_process = Process(
target=cube_func, args=(process_dict, str(cube_var))
)
square_process.start()
cube_process.start()
# start the updater
update_string_vars(process_dict, square_var, cube_var)
window.mainloop()
Useful:
Sharing state between processes
shortly about tkinter and processes
See also:
I strongly advise against using wildcard (*) when importing something, You should either import what You need, e.g. from module import Class1, func_1, var_2 and so on or import the whole module: import module then You can also use an alias: import module as md or sth like that, the point is that don't import everything unless You actually know what You are doing; name clashes are the issue.
I strongly suggest following PEP 8 - Style Guide for Python Code. Function and variable names should be in snake_case, class names in CapitalCase. Don't have space around = if it is used as a part of keyword argument (func(arg='value')) but have space around = if it is used for assigning a value (variable = 'some value'). Have space around operators (+-/ etc.: value = x + y(except here value += x + y)). Have two blank lines around function and class declarations. Object method definitions have one blank line around them.
This question already has an answer here:
How i can print(b) from a func which is define in frontend.py to backend.py
(1 answer)
Closed 3 years ago.
There are 2 Files 1.Frontend 2.Backend
In Frontend There is one function pop(), which basically is b = a.get()
and what i want is whenever user type something in entry box it should be printed via backend...
FRONTEND
from tkinter import *
import backend
win = Tk()
win.geometry("500x500")
def pop():
b = a.get()
But = Button(text = "CLICK",command = pop)
But.pack()
a = StringVar()
e_1 = Entry(textvariable = a)
e_1.pack()
BACKEND
from frontend import pop
print(b)
I was expected that whenever use type something in entry box it should be print via backend but i got an error that is "b" is not define..
You could do something like this:
Change pop to:
def pop():
b = a.get()
return b
Backend:
from frontend import pop
print(pop())
This prints b.
Variables are defined in a function are only part of that function's "scope" as JacobIRR said, but you can still return the variable.
In my script, I want to ask the user for input on the correct or incorrect spelling of a sentence (i) and allow the user to make corrections if necessary. I am running this simply in Jupyter Notebook on a Mac. (We do not know in advance which sentence contains errors and which do not.) This is quite straightforward to do. However, I want to give the user the sentence i as an editable input at the prompt. I have tried to achieve this by adding a variable to the 'input' line in the script, but that does not work. I cannot find a positive answer to this question. Perhaps somebody knows if it is possible or impossible?
Here is the script.
i = "Today, John toak the bus to school."
print(i)
print(f"Is this sentence spelled correctly? No = 0, Yes = 1")
choice = input("> ")
if choice == "1":
return i
else choice == "0":
spelling = input("> ", i) # this does not work. Is there a way?
return spelling
Suppose the script gives the user the following line:
John wend to school by bus today.
Is this sentence spelled correctly? No = 0, Yes = 1
If the user selects 0 (=No), I want the sentence to already appear at the prompt so that the user can edit it (just change 'wend' to 'went' and hit 'enter') rather than having to type the entire sentence again (with the risk of new mistakes):
|-----------------------------------------|
| > John wend to school by bus today. |
|-----------------------------------------|
Does anyone know if this is possible and, if so, how?
We can do this in a Tkinter window. Which may not be what you are looking for, but is a solution using the standard library. Here is some example code that creates a window with a default string. You can edit the string. When the return key is pressed, the string in the window is read.
from tkinter import Tk, LEFT, BOTH, StringVar
from tkinter.ttk import Entry, Frame
class Example(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.parent = parent
self.initUI()
def initUI(self):
self.parent.title("Entry")
self.pack(fill=BOTH, expand=1)
self.contents = StringVar()
# give the StringVar a default value
self.contents.set('test')
self.entry = Entry(self)
self.entry.pack(side=LEFT, padx=15)
self.entry["textvariable"] = self.contents
self.entry.bind('<Key-Return>', self.on_changed)
def on_changed(self, event):
print('contents: {}'.format(self.contents.get()))
return True
def main():
root = Tk()
ex = Example(root)
root.geometry("250x100+300+300")
root.mainloop()
if __name__ == '__main__':
main()
I'm new to Tkinter and I'm trying to add event handling to a GUI.
I have a list that contains sentences and words( the list contains a sublist consisting of the sent as a string as its first element and a list of its words as its second element), and I first want to display the sentences using a Label widget. What I'd like to do is switch between the sentences using the Up and Down keys.
My first problem, however is a different one. I want to store the sentence that is currently displayed in a variable called current_sent, so I try to assign 0 to self.current_sent in the constructor of the app. However, when I try to reference this variable in my code, I get an attribute error. When I initialize self.current_sent in the initialize() method of my app, I don't get the error. Can anybody tell me why this is?
Now if I set self.current_sent = 0 in the initialize method, the gui starts, but I don't get any changes when pushing the Down button.
I also tried this using only '' as an event, but that also doesn't cause the second sentence to be displayed.
If I try to call print statements from the next_sent method, nothing is displayed, so I never enter the event handling function.
Can anybody tell me, what I'm doing wrong, please?
import nltk
import Tkinter as tk
import os
class Annotator(tk.Tk):
def __init__(self, parent):
tk.Tk.__init__(self, parent)
self.sents = self.get_sents()
self.initialize()
self.current_sent = 0
self.current_word = 0
def sent_tokenize(self, textfile):
f = open(textfile)
s = f.readlines()
text = " ".join(s)
sents = nltk.sent_tokenize(text)
tags = [[x,nltk.word_tokenize(x)] for x in sents]
return tags
def get_sents(self):
article_files = self.get_articles()
list_of_sents = [self.sent_tokenize(x) for x in article_files]
sents = [sent for sublist in list_of_sents for sent in sublist]
return sents
def get_articles(self):
directory = "/Users/------------/Documents/reuters/reuters/articles"
list_of_articles = []
for f in os.listdir(directory):
if not f.startswith('.'):
filename = directory + "/" + f
list_of_articles.append(filename)
return list_of_articles
def next_sent(self,event):
if (self.current_sent < len(self.sents) - 1):
self.current_sent += 1
self.label.config(text = self.sents[self.current_sent][0])
def initialize(self):
self.label = tk.Label(text = self.sents[self.current_sent][0])
self.label.bind('<KeyPress-Down>', self.next_sent)
self.label.grid(row = 0, column = 0, columnspan = 2)
if __name__ == "__main__":
app = Annotator(None)
app.mainloop()
The AttributeError is coming up because __init__ calls initialize before defining self.current_sent so you just need to rearrange the __init__ a little bit:
def __init__(self, parent):
tk.Tk.__init__(self, parent)
self.current_sent = 0
self.current_word = 0
self.sents = self.get_sents()
self.initialize()
As for the Binding issue, only the widget with keyboard focus will respond to events, either try clicking on the label before testing the events or set it up to respond regardless of what has focus like this:
self.bind_all('<KeyPress-Down>', self.next_sent)
I am working on a tool to log on a motorolla modem, i get it working and display the output on the python console, this tool have 2 part one part with the gui and the button, label and text frame.
i would like to get the output displayed on the Gui and not to the console.
how can i get that done
here is the files :
from Tkinter import *
import motorola
class Application(object):
def init(self):
self.fen = Tk()
self.fen.title("Motorola tool V 0.1")
self.fen.geometry("720x480")
Label(self.fen,
text = "IP address").grid(row=0)
#self.entree = MaxLengthEntry(self.fen, maxlength=5)
self.entree1 = Entry(self.fen)
self.entree1.grid(row=0, column=1)
Label(self.fen,
text = "Password").grid(row=2)
#self.entree = MaxLengthEntry(self.fen, maxlength=5)
self.entree2 = Entry(self.fen)
self.entree2.grid(row=2, column=1)
Button(self.fen, text = 'Connect',
command = self.launch).grid(row = 3, column=2)
Button(self.fen, text = 'Disconect',
command = self.exits).grid(row = 3, column=3)
Button(self.fen, text = 'Quit',
command = self.fen.quit).grid(row = 5, sticky = E)
self.output = Text(self.fen)
self.output.grid(row = 7, column = 1)
self.fen.mainloop()
def launch(self):
self.ip = self.entree1.get()
self.passw = self.entree2.get()
print self.ip, self.passw
if self.passw == "":
self.entree2.config(bg = 'red')
self.fen.after(1000, self.empty)
else:
self.f = motorola.Motorola(self.ip, self.passw)
self.f.sh_dsl()
def empty(self):
self.entree2.configure(bg='white')
def exits(self):
try:
self.f.disconnect()
except AttributeError:
print "You are not connected"
a = Application()
motorola file :
class Motorola(object):
def init(self, ip, passw):
self.ip = ip
self.passw = passw
print "connect on the modem"
self.tn = telnetlib.Telnet(self.ip, '2323' , timeout =5)
self. tn.read_until("login: ")
self.tn.write('radadmin\r\n')
self.tn.read_until("Password:")
self.tn.write(self.passw+"\r\n")
data = self.tn.read_until(">")
print "you are connected"
print data,
def disconnect(self):
self.tn.close()
print "disconnect from the modem"
import telnetlib
once i connect on the modem with the button which launch motorola module, how can the data could be displayed on the frame text of the gui module ?
Thank you
The basic idea is that you have to replace each print with some code that adds the string to the GUI.
The trick is that a Motorola instance probably doesn't know how to do that. So, what you want to do is pass it something—self, or self.output, or best of all, a function (closure) that appends to self.output. Then the Motorola code doesn't have to know anything about Tk (and doesn't have to change if you later write a wx GUI); it just has a function it can call to output a string. For example:
def outputter(msg):
self.output.insert(END, msg + "\n")
self.f = motorola.Motorola(self.ip, self.passw, outputter)
Then, inside the Motorola object, just store that parameter and call it everywhere you were using print:
def __init__(self, ip, passw, outputter):
self.outputter = outputter
# ...
self.outputter("connect on the modem")
That almost does it, but how you do handle the magic trailing comma of the print function, or even simple things like multiple arguments or printing out numbers? Well, you just need to make outputter a little smarter. You could look at the interface of the Python 3 print function for inspiration:
def outputter(*msgs, **kwargs):
sep = kwargs.get("sep", " ")
end = kwargs.get("end", "\n")
self.output.insert(END, sep.join(msgs) + end)
You could go farther—converting non-strings to strings (but not breaking Unicode), etc.—but really, why? If you're trying to get too fancy with print or with your outputter function, you probably want to let str.format do the heavy lifting instead…
Anyway, now, instead of:
print data,
You do:
self.outputter(data, end='')
And all of your other print statements are trivial.