Tkinter: Text in Frame in Nootbook tab not painted - python-2.7

OS is Win7 64bit, Python is 2.7.16 64bit. I have a simple Tkinter GUI: Root containing a Notebook with two tabs. First tab contains a Frame which contains a Button. Second tab contains a Frame which contains a Text. The command bound to the Button spawns a thread with sets the content of the Text.
import Tkinter
import ttk
import threading
r = Tkinter.Tk()
n = ttk.Notebook(r)
n.pack(expand=1, fill="both")
control = ttk.Frame(n)
info = ttk.Frame(n)
tInfo = Tkinter.Text(info)
tInfo.pack(expand=1, fill="both")
n.add(control, text='Control')
n.add(info, text='Info')
infoMutex = threading.Lock()
def doGuiTest():
try:
infoMutex.acquire()
tInfo.config(state='normal')
tInfo.delete('1.0', Tkinter.END)
tInfo.insert(Tkinter.END, 'Test')
tInfo.config(state='disabled')
finally:
infoMutex.release()
def workerThread():
doGuiTest()
def execute():
worker=threading.Thread(target=workerThread)
worker.start()
bExecute=Tkinter.Button(control, text='Execute', command=execute)
bExecute.pack()
r.mainloop()
Expected result: The Text is reliably visible with the set content after the Button is clicked.
Actual result: The Text is only visible when the tab containing the Text has been manually brought to the foreground before the Button is clicked.
When I set the content of the Text directly from the Button's command everything works as expected. Sadly, in the real application I am working on the functionality triggered by the Button will be running for several minutes so using another thread is a must.
What am I missing to achieve a consistent behavior?

Related

Tkinter - Multiple windows opening

Like the title says I'm having a problem with multiple windows being able to open. New window (same window) every time I click the button. I haven't been able to find an answer to this specific scenario. I've even tried disabling the button after opening window (which led to the problem of re-enabling).
Below is enough code to work with, you can see the problem by clicking the button a few times. I am hoping for a somewhat easy solution as I am fairly new to Tkinter. Also, the smaller window needs to resize (expand) with the main window, so I dont think a Toplevel window would work.
There may be others having this same problem, and thanks in advance!
Note: I'm using Python 2.7 (Tkinter)
#!/usr/bin/python
import os
import sys
import Tkinter as tk
from Tkinter import *
from ScrolledText import *
import tkFileDialog
import tkMessageBox
# Main
root = tk.Tk(className = "tex")
root.geometry("500x300")
root.title("tex")
tex = ScrolledText(root, padx=2, pady=2, undo=True, font=('Arial 11'))
def note_area():
btn_frame = Frame()
note = LabelFrame(tex, bd=1, relief='ridge')
tx = Text(note, width=18, relief='flat', padx=2, pady=2)
tx.insert('1.0', "Notes..")
tx.pack(side='top', fill=BOTH, expand=True)
note.pack(side='right', fill=Y)
btn_frame.pack(side='bottom', fill=Y)
# ToolBar Button (should only open one instance of note_area)
toolbar = Frame(root, bd=2, relief='groove')
b4 = Button(toolbar, text="Notes", width=4, command=note_area)
b4.pack(side=RIGHT, padx=4, pady=2)
toolbar.pack(side=TOP, fill=X)
tex.pack(fill="both", expand=True)
root.mainloop()
You just need make up a variable to keep track of if you have a note window open or not.
tex.notes_open = False
def note_area():
if tex.notes_open:
return # abort the function, notes already open
else:
tex.notes_open = True # set the flag for the next time
# rest of your code

How to place a button on the TopLevel window in tkinter python?

Answer as pointed out down for other innocent newbies like me
in python2, we have Tkinter
In python3, we have tkinter.
Notice the difference in casing. Thats the reason, why error was coming up.
I have two screens: window(child) and root(master)
I am trying to place a button on the "window" screen created by the method: command(). I have written this piece of code.
from tkinter import *
root = Tk()
def writeText():
print "hello"
def command():
window=Toplevel(root)
Button(window,text="Button2",command=writeText).grid()
Label(window,text="hello").grid()
button = Button(root, text="New Window", command=command)
button.grid()
root.mainloop()
But this button2 is not appearing on the second screen. Meanwhile, Label is appearing on this screen. And the control is going into the writeText() function.
And when I remove the command argument from the Button of window screen, the button appears.
Can anyone please help me with this?
Here is my suggestion.
From your question you have put from tkinter import * however in your tags you put Python 2.7. This is contradictory because tkinter(all lower case) is used in Python 3.x and Tkinter should instead be used for python 2.x. That said try fixing your import first. If you are in fact using Python 3 then you will need to correct your print statement to include brackets. print("hello")
2nd I would try to follow PEP8 more closely however in this case I don't see anything out of the ordinary that would cause this issue.
Take my below example and let me know if you are still having the same problem.
Python 2.x example:
import Tkinter as tk # Upper case T in Tkinter for Python 2.x
root = tk.Tk()
def write_text():
print "hello"
def command():
window = tk.Toplevel(root)
tk.Button(window,text="Button2",command=write_text).grid()
tk.Label(window,text="hello").grid()
button = tk.Button(root, text="New Window", command=command)
button.grid()
root.mainloop()
Python 3.x example:
import tkinter as tk # all lowercase tkinter for Python 3.x
root = tk.Tk()
def write_text():
print("hello") # Python 3.x requires brackets for print statements.
def command():
window = tk.Toplevel(root)
tk.Button(window,text="Button2",command=write_text).grid()
tk.Label(window,text="hello").grid()
button = tk.Button(root, text="New Window", command=command)
button.grid()
root.mainloop()
If you are still having problems can you tell me if you are using Windows, Linux or Mac?
Have you guys ever tried button with image on Toplevel? It seems it cannot be work with code below on Toplevel(Prompt out windows). Root level is ok.
tp = Toplevel()
tp.geometry("400x400")
btnphotoAdd=PhotoImage(file="32adduser.png")
btnAdd = Button(tp, text="Add User", font="Helvetica 20 bold", image=btnphotoAdd,compound=TOP)
btnAdd.grid(row=10, column=0, sticky=W)

Closing Tkinter GUI from another function A and passes Tkinter variable to function A

This question is about programming in Python 2.7.x
I wanted to code a programme where there are two functions exist: one of those is a method to get input from the user, and the other one is to show the input. Both are supposed to be done in GUI. Let's call the first function as GET TEXT function, and the second as SHOW TEXT function; my strategy is to open a GUI, show a text box, and put a button to go to SHOW TEXT function. Then, the first line of the SHOW TEXT function is to close the window opened by the GET TEXT function, get the value of the input text, and print it in another GUI.
So, I tried doing this,
from Tkinter import *
import tkMessageBox
def texttobeenteredhere():
application = Tk()
textbox = Text(application)
textbox.pack()
submitbutton = Button(application, text="OK", command=showinputtext)
submitbutton.pack()
application.mainloop()
def showinputtext():
application.quit()
thetext = textbox.get()
print "You typed", thetext
texttobeenteredhere()
I got errors that I could not comprehend, but I hope you get my idea even though my explanation could be really bad. Please suggest a solution to my problem, where the GET TEXT function and SHOW TEXT function have to exist separately in the code.
EDIT:
Thanks Josselin for introducing the syntax class in python. What I actually wanted to say was, I want the programme to open a window to get input from the user, and then close the window, and finally open another window to show the input text. I am honestly new to this, but through my prior knowledge and guessing, I tried to modify the code to meet my expectation.
import Tkinter as tk
global passtext
class application(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.textbox = tk.Text(self)
self.textbox.pack()
self.submitbutton = tk.Button(self, text="OK", command=self.showinputtext)
self.submitbutton.pack()
self.mainloop()
def showinputtext(self):
self.thetext = self.textbox.get("1.0", "end-1c")
print "You typed:", self.thetext
self.destroy()
class showtext(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.setthetext = tk.StringVar()
self.setthetext.set(passtext)
self.showthetext = tk.Label(self, textvariable=self.setthetext)
self.showthetext.pack()
self.submitbutton = tk.Button(self, text="OK", command=self.destroy)
self.submitbutton.pack()
self.mainloop()
# Launch the GUI
app = application()
# Access the entered text after closing the GUI
passtext = app.thetext
text = showtext()
My English can sometimes be not understandable, but this question is answered. Thank you very much.
There are 2 main problems in your code:
First, in your showinputtext function, you want to access elements of your GUI, but they are not defined within the scope of the function.
Second, when reading the content of a tk.Text widget, the .get() method takes 2 arguments (see this link).
To fix the first problem, the best is to define your application as a class, with an inner function taking the class instance self as input argument, such that application widgets can be called within the function.
Code:
import Tkinter as tk
class application(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.textbox = tk.Text(self)
self.textbox.pack()
self.submitbutton = tk.Button(self, text="OK", command=self.showinputtext)
self.submitbutton.pack()
self.mainloop()
def showinputtext(self):
self.thetext = self.textbox.get("1.0", "end-1c")
print "You typed:", self.thetext
self.destroy()
# Launch the GUI
app = application()
# Access the entered text after closing the GUI
print "you entered:", app.thetext

Python tk(): No window appears when using in scripts (console works)

I have the following problem with this easy script:
from Tkinter import *
root = Tk()
while 1:
pass
I think, after the 2nd line everyone would expect a Tkinter window would appear. But it does not!
If I put this line into the Python console (without the endless-while-loop), it works.
[I wanted to add an image here, but as I'm new I'm to allowed to :-(]
But running the script (double-clicking on the *.py file in the Windows Explorer) results only in an empty Python console!
Background:
Actually I want to use Snack for Python. This is based on Tkinter. That means I have to create an Tk() instance first. Everything works fine in the Python console. But I want to write a bigger program with at least one Python script thus I cannot type the whole program into the console everytime :-)
I have installed Python 2.7 and Tcl/Tk 8.5 (remember: it works in the console)
EDIT: So here's my solution:
First, I create a class CSoundPlayer:
from Tkinter import*
import tkSnack
class CSoundPlayer:
def __init__(self, callbackFunction):
self.__activated = False
self.__callbackFunction = callbackFunction
self.__sounds = []
self.__numberOfSounds = 0
self.__root = Tk()
self.__root.title("SoundPlayer")
tkSnack.initializeSnack(self.__root)
def __mainFunction(self):
self.__callbackFunction()
self.__root.after(1, self.__mainFunction)
pass
def activate(self):
self.__activated = True
self.__root.after(1, self.__mainFunction)
self.__root.mainloop()
def loadFile(self, fileName):
if self.__activated:
self.__sounds.append(tkSnack.Sound(load=fileName))
self.__numberOfSounds += 1
# return the index of the new sound
return self.__numberOfSounds - 1
else:
return -1
def play(self, soundIndex):
if self.__activated:
self.__sounds[soundIndex].play()
else:
return -1
Then, the application itself must be implemented in a class thus the main() is defined when handed over to the CSoundPlayer() constructor:
class CApplication:
def __init__(self):
self.__programCounter = -1
self.__SoundPlayer = CSoundPlayer(self.main)
self.__SoundPlayer.activate()
def main(self):
self.__programCounter += 1
if self.__programCounter == 0:
self.__sound1 = self.__SoundPlayer.loadFile("../mysong.mp3")
self.__SoundPlayer.play(self.__sound1)
# here the cyclic code starts:
print self.__programCounter
CApplication()
As you can see, the mainloop() is called not in the constructor but in the activate() method. This is because the CApplication won't ever get the reference to CSoundPlayer object because that stucks in the mainloop.
The code of the class CApplication itself does a lot of overhead. The actual "application code" is placed inside the CApplication.main() - code which shall be executed only once is controlled by means of the program counter.
Now I put it to the next level and place a polling process of the MIDI Device in the CApplication.main(). Thus I will use MIDI commands as trigger for playing sound files. I hope the performance is sufficient for appropriate latency.
Have you any suggestions for optimization?
You must start the event loop. Without the event loop, tkinter has no way to actually draw the window. Remove the while loop and replace it with mainloop:
from Tkinter import *
root = Tk()
root.mainloop()
If you need to do polling (as mentioned in the comments to the question), write a function that polls, and have that function run every periodically with after:
def poll():
<do the polling here>
# in 100ms, call the poll function again
root.after(100, poll)
The reason you don't need mainloop in the console depends on what you mean by "the console". In IDLE, and perhaps some other interactive interpreters, tkinter has a special mode when running interactively that doesn't require you call mainloop. In essence, the mainloop is the console input loop.

PyQt - comboBox and/or lineEdit value as subProcess.call argument

I am new to Python and Qt4 and am running into some problems with taking user entered/selected information and then using them as arguments for other python files. Here's the two situations & code:
Users enter an ID # into a lineEdit box, and on button click, program will run a script with the ID # entered as an argument. e.g., ID # = 11503, on button click: programname.py 11503 will run. This sort of works, but puts spaces between each number in the ID and I don't know if it's the cleanest way to do what I want.
ID # process button SIGNAL:
QtCore.QObject.connect(self.ui.pushButton_2, QtCore.SIGNAL('clicked()'), self.processID)
SIGNAL calls this:
def processID(self):
import subprocess
from PyQt4 import QtCore, QtGui
rawID = (self.ui.lineEdit.text())
idList = []
for x in rawID:
idList.append(str(x))
subprocess.call(["Python" "programname.py"] + idList, shell=True)
Pretty much the same situation as above, but one of the arguments needed for the "programname.py" script is a file directory. I have a comboBox populating with the names of the directories, but can't get it to take the selection and print it as an argument. Here's the code for that:
Combobox Directory population
import glob, os
myList = sorted(glob.glob('C:\\Python27\\test_directories\\*'))
new_myList = []
for x in myList:
new_myList.append(os.path.basename(x))
self.ui.comboBox_4.addItems(new_myList)
Directory combobox SIGNAL
self.ui.comboBox_4.activated[str].connect(self.Directory)
I connected a different button to the comboBox and tried to replicate what I did with the lineEdit, but it just won't work at all. However, I CAN print the user selection with the code below, so it's functional, just not in the way I want.
Directory comboBox selection print test
def Directory(self, item):
print(item)
Any help would be greatly appreciated. (And if you make it all the way through this long post, thank you!)