I'm relatively new to Python and Tkinter and I am striving to get my head over how mainloop and the after method work.
More specifically, I want to create a splashScreen, which goes away after a timeframe, and then the actual mainWindow is shown.
My code looks like this:
class SplashScreen:
# some code here
def destroyMe(self):
self.destroy()
def destroySplashScreen(self, timeToSleep=0):
if timeToSleep > 0:
self.master.after(timeToSleep*1000, self.destroyMe())
# some other code here
if __name__ == '__main__':
root = Toplevel()
mySP = SplashScreen(root)
mySP.populateSplashScreen()
mySP.destroySplashScreen(5)
root.mainloop()
However, what the code does is to create the whole window after the timeframe given (5 sec.) without any content. Instead, it should create it, wait 5 sec. and then destroy it.
Working example
after expects only function name (without ()).
destroy() needs self.master
from Tkinter import *
class SplashScreen:
# some code here
def __init__(self, master):
self.master = master
self.master.title("SplashScreen")
def destroyMe(self):
self.master.destroy()
def destroySplashScreen(self, timeToSleep=0):
if timeToSleep > 0:
self.master.after(timeToSleep*1000, self.destroyMe)
# some other code here
if __name__ == '__main__':
root = Toplevel()
mySP = SplashScreen(root)
#mySP.populateSplashScreen()
mySP.destroySplashScreen(3)
root.mainloop()
BTW: Toplevel is used to create "child" window so (in example) it create automaticly "parent" window - so I add title("SplashScreen")
BTW: if you will use command= in widget - it also expects function name without ().
If you use (in command or after) function with () you run that function and result is assigned to command= or use as parameter for after.
Related
I am fairly new to object-oriented programming. I have a class called MessageWindow which generates a Tkinter text widget inside of a Frame. In my main function I want to write a message into the text widget but when I run the program I get: Class MessageWindow has no attribute text1
I've tried following other responses here on Stack overflow, and another tutorial on the web and I always end up back at the same error
from Tkinter import *
class MessageWindow(Frame):
def __init__(self, master):
Frame.__init__(self, master)
self.pack(fill='both', expand='yes')
self.text1 =Text(self,height=9, width=13)
self.text1.pack()
def main():
root=Tk()
root.title("MessageWindow")
app = MessageWindow(root)
root.mainloop()
MessageWindow.text1.insert(END,"This is a test Message")
MessageWindow.text1.pack()
if __name__=='__main__':
main()
Any help would be appreciated. I need to understand this for a larger GUI project that I am working on.
Ok so
1. You are referring to MessageWindow as your class object when you should be referring to app, as you have instantiated the class as 'app' in your main definition.
2. Your root.mainloop() should be after all your GUI code as it will not take affect if it is after/outisde the mainloop.
So this is the How the code is supposed to be:
from Tkinter import *
class MessageWindow(Frame):
def __init__(self, master):
Frame.__init__(self, master)
self.pack(fill='both', expand='yes')
self.text1 = Text(self,height=9, width=13)
self.text1.pack()
def main():
root=Tk()
root.title("MessageWindow")
app = MessageWindow(root)
# Fix one
app.text1.insert(END,"This is a test Message")
app.text1.pack()
# Fix two
root.mainloop()
if __name__=='__main__':
main()
I developed a simple Python application doing some stuff, then I decided to add a simple GUI using Tkinter.
The problem is that, while the I call a function called startprocess and begin doing stuff which is processor heavy and the window freezes.
I know it's a common problem and I've already read that I should use multithreads (very complicated, because the function updates the GUI too) or divide my code in different function, each one working for a little time. anyways is there any modification needed in below code to avoid GUI freezing?
import threading
import tkinter as tk
from tkinter import filedialog
from tkinter import messagebox
import os, datetime, sys, subprocess
import parselog_v1
# diplay messagebox window
def MessageBox(windowLable,msg):
messagebox.showinfo(windowLable, msg)
# check if Dir empty
def checkDirEmpty(work_path):
if os.path.isdir(work_path):
if not os.listdir(work_path):
print ("No Files found in directory")
MessageBox('Log Parser', 'No Files found in directory.')
else:
return True
# launch app in center of screen
def center_window(width=300, height=200):
# get screen width and height
screen_width = root.winfo_screenwidth()
screen_height = root.winfo_screenheight()
# calculate position x and y coordinates
x = (screen_width/2) - (width/2)
y = (screen_height/2) - (height/2)
root.geometry('%dx%d+%d+%d' % (width, height, x, y))
# application frame
class Application(tk.Frame):
def __init__(self, master=None):
tk.Frame.__init__(self, master)
self.pack()
self.createWidgets()
self.master.title("Log Parser")
def createWidgets(self):
self.Run_Main = tk.Button(self)
self.Run_Main["text"] = "Browse for logs"
self.Run_Main["fg"] = "blue"
self.Run_Main["command"] = self.startProcess
self.Run_Main.pack(side='left',padx=0)
self.QUIT = tk.Button(self)
self.QUIT["text"] = "Quit!"
self.QUIT["fg"] = "red"
self.QUIT["command"] = self.quit
self.QUIT.pack(side='right',padx=5)
def startProcess(self):
global Src_foldername
Src_foldername = filedialog.askdirectory()
Src_foldername = Src_foldername.replace("/", "\\")
print("Source folder: " + Src_foldername)
if checkDirEmpty(Src_foldername):
# process logs
# multithread
print("Processing...")
self.refresh()
threading.Thread(target=parselog_v1.main(Src_foldername))
# scroll text inside application frame
class scrollTxtArea:
def __init__(self, root):
frame = tk.Frame(root)
frame.pack()
self.textPad(frame)
return
class IORedirector(object):
'''A general class for redirecting I/O to this Text widget.'''
def __init__(self, text_area):
self.text_area = text_area
class StdoutRedirector(IORedirector):
'''A class for redirecting stdout to this Text widget.'''
def textPad(self, frame):
# add a frame and put a text area into it
textPad = tk.Frame(frame)
self.text = tk.Text(textPad, height=21, width=68)
self.text.config()
# add a vertical scroll bar to the text area
scroll = tk.Scrollbar(textPad)
self.text.configure(yscrollcommand=scroll.set,background="black", foreground="green")
# pack everything
self.text.pack(side=tk.LEFT, pady=2)
scroll.pack(side=tk.RIGHT, fill=tk.Y)
textPad.pack(side=tk.TOP)
self.text.insert("end", "Begin by selecting log folder..." + "\n")
self.text.configure(state='disabled') # disable text editing
sys.stdout = (self) # to begin logging stdio to GUI
return
def write(self, txt):
self.text.configure(state='normal')
self.text.insert('end', txt)
self.text.configure(state='disabled')
root = tk.Tk()
root.resizable(width=False, height=False)
center_window(500, 300) # launch in center of screen
app = Application(master=root)
scrollFrame = scrollTxtArea(root)
app.mainloop()
root.destroy()
You use thread in wrong way.
First: target= needs function name without () and arguments.
You can assign arguments to args= (it have to be tuple even if you have only one argument)
threading.Thread(target=parselog_v1.main, args=(Src_foldername,) )
Now your code runs parselog_v1.main as normal function, waits for result and it will assign this result as function name to taget= - so you have something like this:
result = parselog_v1.main(Src_foldername)
threading.Thread(target=result)
It stops mainloop so it can't get mouse/keyboard events, refresh window, etc. so it looks like window freeze.
Second: after you create thread correctly you have to start it
my_thread = threading.Thread(target=parselog_v1.main, args=(Src_foldername,) )
my_thread.start()
I am trying to get my save button to call a function from another class. I would like to click on the save button as much as I want and it should print "hello people" every time. Though, I am having trouble in getting the save button to work.
import tkinter as tk
from tkinter import filedialog
class Application(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.parent = parent
self.pack()
self.createWidgets()
def createWidgets(self):
#save button
self.saveLabel = tk.Label(self.parent, text="Save File", padx=10, pady=10)
self.saveLabel.pack()
#When I click the button save, I would like it to call the test function in the documentMaker class
self.saveButton = tk.Button(self.parent, text = "Save", command = documentMaker.test(self))
self.saveButton.pack()
class documentMaker():
def test(self):
print ("hello people")
root = tk.Tk()
app = Application(root)
app.master.title('Sample application')
object = documentMaker()
object.test()
app.mainloop()
In your documentMaker class, change the test method to a #staticmethod:
class documentMaker():
#staticmethod
def test(cls):
print ("hello people")
Then your saveButton's command can be:
command = documentMaker.test
A staticmethod is bound to the class, not to an instance of the class like an instance method. So, we can call it from the class's name directly. If you did not want it to be a staticmethod, you could keep it an instance method and have the command line change to:
command = documentMaker().test
I'm learning about GUI python using pyQt4. I have function A in another file python. and I want to run in GUI file python that I extracted from file .ui (output of designer pyQt4). How to create activity indicator which is active when the function A is running? can I use progress bar (in pyQt4 designer) without know how many time for my function A running?
Thank you.
this is the function to call A in GUI .py:
def RunFunction():
import Kdtree
_dir = kdTreeOk.getNeighbor(float(radius)) #function 'A'
file = file_open('Summary.txt',_dir) # ignore, just file to save result of `A`
with file:
textOutput=file.read()
ui.result.setPlainText(textOutput)
#### button to run RunFunction in file GUI .py
ui._run.clicked.connect(RunFunction)
QProgressDialog is made for this purpose and generally called via QThread. Here's a (messy) basic example to show how this can work (without any threading). If you are calling this dialog from another window, just set parent as the calling window and you can read attributes in this dialog by calling self.parent.some_variable.
EDITED to work properly ;).
from PyQt4 import QtCore, QtGui
from time import sleep
import sys
class ProgressBarWidget(QtGui.QProgressDialog):
def __init__(self, parent=None, app=None):
super(ProgressBarWidget, self).__init__(parent)
self.app=app
self._allow_close = True
layout = QtGui.QVBoxLayout(self)
# Create a progress bar and a button and add them to the main layout
self.progressBar = QtGui.QProgressBar(self)
self.progressBar.setRange(0,100)
layout.addWidget(self.progressBar)
self.button = QtGui.QPushButton("Start", self)
layout.addWidget(self.button)
self.button.clicked.connect(self.onStart)
self.upload_count = 10
def onStart(self):
self.progressBar.setValue(0)
self.button.setText("Uploading...")
self.run()
def makeProgress(self, current_num, total_num, message = ''):
if total_num == current_num:
self.onFinished()
elif current_num == 0:
self.progressBar.setValue(0)
else:
multiplier = int(float(float(100) / float(total_num)))
c_times_m = current_num * multiplier
for i in xrange(c_times_m - int(self.progressBar.value())):
new_val = int(self.progressBar.value()) + 1
self.progressBar.setValue(new_val)
sleep(.01)
def onFinished(self):
# progress complete
self.progressBar.setRange(0,100)
for i in xrange(int(self.progressBar.value()),101):
self.progressBar.setValue(i)
self.button.setEnabled(True)
self.button.setText('Exit')
self.button.clicked.disconnect(self.onStart)
self.button.clicked.connect(self.close)
def run(self):
self._allow_close = False
self.button.setDisabled(True)
total = self.upload_count * 2
progress_meter = 0
downloaded = []
tests_to_upload = 10
for each in xrange(tests_to_upload):
sleep(0.15)
progress_meter += 1
self.makeProgress(progress_meter,total)
sleep(0.2)
progress_meter += 1
self.makeProgress(progress_meter, total)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
window = ProgressBarWidget(app=app)
window.resize(640, 480)
window.show()
sys.exit(app.exec_())
So, for an assignment that I am supposed to do for my programming course, I ran into a bit of an issue. We never really went through how to create child windows // secondary windows, and for some reason the things I add in the first window are not showing up?...
So far my code looks like:
from Tkinter import*
class Window(Frame):
def __init__(self, master = None):
Frame.__init__(self, master)
self.master = master
self.button1 = Button(self, text="Create new window", command=self.New_Window)
self.button1.pack(fill=BOTH)
def New_Window(self):
win = Toplevel(self)
win.title("New Window")
etiquette1 = Label(root, text = "Text shenanigans")
etiquette1.pack()
if __name__ == "__main__":
root = Tk()
main = Window(root)
main.mainloop()
Also, another question is how can I name the first window? It's just called "tk" by default, but if I try to do something like the example below, it does not work.
self.__init__.title("Main Window")
The first argument when creating widgets is the "parent" -- the widget into which the new widget goes. If you want a label to be in the Toplevel, make the Toplevel be the parent:
etiquette1 = Label(win, ...)
To set the title of the window, call the title method:
root.title("This is the root window")
...
win.title("This is the second window")