Multiple Windows in Tkinter (Python 2.7) - python-2.7

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")

Related

tkinter Checkbox from list loop. How rebuild (update) checkboxes when new list arrives?

In the tkinter app I'm building (win10 python 3.8) each time I open a new file I get a new list that I distribute to textboxes (Ok), combobox(ok), etc. When I open the first list checkbox loop builds ok, next file called checkboxes don't change. I can't update checkboxes, I mean, remove the previous list and insert another one. In the example I used to buttons (in app askopenfilename), lists build one bellow other. I need one replacing the other. I believe I need to use grid.clear() or destroy, but how? Thanks in advance.
from tkinter import *
root = Tk()
root.geometry('400x400')
my_friends = ['Donald', 'Daisy', 'Uncle Scrooge', 'Ze Carioca']
my_heroes = ['Captain America', 'Hulk', 'Spider Man', 'Black Widow',
'Wanda Maximoff', 'Vision', 'Winter Soldier']
what_list = ' '
def list_my_friends():
global what_list
what_list = my_friends
create_cbox()
def list_my_heroes():
global what_list
what_list = my_heroes
create_cbox()
def create_cbox():
for index, friend in enumerate(what_list):
current_var = tk.StringVar()
current_box = tk.Checkbutton(root, text= friend,
variable = current_var,
onvalue = friend,
offvalue = '')
current_box.pack()
button1= tk.Button(root, text = "my_friends",command=lambda:list_my_friends()).pack()
button2= tk.Button(root, text = "my_heroes",command=lambda:list_my_heroes()).pack()
root.mainloop()
You can put those checkbuttons in a frame, then it is more easier to remove old checkbuttons before populating new checkbuttons:
def create_cbox():
# remove existing checkbuttons
for w in container.winfo_children():
w.destroy()
# populate new checkbuttons
for friend in what_list:
current_var = tk.StringVar()
# use container as the parent
current_box = tk.Checkbutton(container, text=friend,
variable=current_var,
onvalue=friend,
offvalue='')
current_box.pack(anchor='w')
tk.Button(root, text="my_friends", command=list_my_friends).pack()
tk.Button(root, text="my_heroes", command=list_my_heroes).pack()
# frame to hold the checkbuttons
container = tk.Frame(root)
container.pack(padx=10, pady=10)
worked with little adjustments as I'm working in class:
class PageEight(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent) # parent is Frame
self.controller = controller
# to adequate to widgets outside the self.container_pg8:
self.rowspan_list = len(what_list)
#container:
self.container_pg8 = tk.Frame(self)
self.container_pg8.grid(row=2,
rowspan = self.rowspan_list,
column=1,)

tkinter avoid GUI from freezing

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()

Invoking destroy() method on window after mainloop in tkinter

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.

Python Tkinter instance has no attribute 'tk'

AttributeError: MyGUI instance has no attribute 'tk'
Also, how do I make the created window have a fixed size and not be able to resize with the mouse? Or after changing label value by clicking on button.
My code:
from Tkinter import*
class MyGUI(Frame):
def __init__(self):
self.__mainWindow = Tk()
#lbl
self.labelText = 'label message'
self.depositLabel = Label(self.__mainWindow, text = self.labelText)
#buttons
self.hi_there = Button(self.__mainWindow)
self.hi_there["text"] = "Hello",
self.hi_there["command"] = self.testeo
self.QUIT = Button(self.__mainWindow)
self.QUIT["text"] = "QUIT"
self.QUIT["fg"] = "red"
self.QUIT["command"] = self.quit
#place on view
self.depositLabel.pack()
self.hi_there.pack() #placed in order!
self.QUIT.pack()
#What does it do?
mainloop()
def testeo(self):
self.depositLabel['text'] = 'c2value'
print "testeo"
def depositCallBack(self,event):
self.labelText = 'change the value'
print(self.labelText)
self.depositLabel['text'] = 'change the value'
myGUI = MyGUI()
What's wrong?
Thanks
You should invoke the super constructor for Frame. Not sure, but I guess this will set the tk attribute that the quit command relies on. After that, there's no need to create your own Tk() instance.
def __init__(self):
Frame.__init__(self)
# self.__mainWindow = Tk()
Of course, you will also have to change the constructor calls for your widgets accordingly, e.g.,
self.hi_there = Button(self) # self, not self.__mainWindow
or better (or at least shorter): set all the attributes directly in the constructors:
self.hi_there = Button(self, text="Hello", command=self.testeo)
Also add self.pack() to your constructor.
(Alternatively, you could change the quit command to self.__mainWindow.quit, but I think the above is better style for creating Frames, see e.g. here.)
This error typically means you are calling SomeTKSuperClass.__init__ and forgetting the first parameter, which must be self. Remember that __init__ is a class method (static function) in this context, not an instance method, which means that you must explicitly pass it self.

Events with QGraphicsItemGroup

In my application I want to use QGraphicsItemGroup for grouping items into one item.
I played with it a little and not sure using it because when I want to catch events, events are merged together but I want to handle specific event with specific child.
How can I achieve this?
You need to call QGraphicsItemGroup::setHandlesChildEvents(false). This stops the QGraphicsItemGroup trying to handle the event, and lets the child QGraphicsItems handle them instead.
I think that's the point of the QGraphicsItemGroup. Judging from the documentation, this is meant to simplify moving and transforming multiple items at once e.g. imagine the following case: a user draws a selection rectangle around several items in an application because he wants to move all of them. Perhaps what you want more is to create a hierarchy of items, e.g. have one parent item with several child items. This way you'll get the individual events for each item. This can be accomplished by calling QGraphicsItem::setParentItem();
The question doesn't specify which version of Qt is concerned and there is a correct answer for Qt4. Here is an answer for Qt5 (it works for PyQt5 and I guess it'll work also in C++). The solution was to reimplement sceneEvent, reimplementing a specialized event-catcher such as contextMenuEvent was not sufficient.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from PyQt5 import QtCore
from PyQt5 import QtGui
from PyQt5 import QtWidgets
class GraphicsItem(QtWidgets.QGraphicsItem):
def __init__(self,
rect: QtCore.QRectF,
name: str,
parent: QtWidgets.QGraphicsItem = None):
super().__init__(parent)
self._name = name
self._rect = rect
def boundingRect(self):
return self._rect
def paint(self,
painter: QtGui.QPainter,
option: QtWidgets.QStyleOptionGraphicsItem,
widget: QtWidgets.QWidget = None):
painter.setPen(QtGui.QPen(QtCore.Qt.NoPen))
painter.setBrush(QtGui.QBrush(QtCore.Qt.red))
painter.drawRect(self._rect)
def sceneEvent(self, event: QtCore.QEvent):
if (event.type() == QtCore.QEvent.GraphicsSceneContextMenu):
self.contextMenuEvent(event)
event.accept()
return True
def contextMenuEvent(self, event: QtWidgets.QGraphicsSceneContextMenuEvent):
print(f'contextMenuEvent in "{self._name}"')
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self._scene = QtWidgets.QGraphicsScene()
layout = QtWidgets.QHBoxLayout()
self._view = QtWidgets.QGraphicsView(self._scene)
layout.addWidget(self._view)
self._widget = QtWidgets.QWidget()
self._widget.setLayout(layout)
group = QtWidgets.QGraphicsItemGroup()
self._scene.addItem(group)
scene_item = GraphicsItem(QtCore.QRectF(0, 0, 100, 100), 'in scene')
self._scene.addItem(scene_item)
group_item = GraphicsItem(QtCore.QRectF(150, 0, 100, 100), 'in group')
group.addToGroup(group_item)
group_item = GraphicsItem(QtCore.QRectF(300, 0, 100, 100), '2nd in group')
group.addToGroup(group_item)
self.setCentralWidget(self._widget)
self.setWindowTitle('contextMenuEvent with QGraphicsItemGroup')
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
mainWindow = MainWindow()
mainWindow.setGeometry(100, 100, 800, 500)
mainWindow.show()
sys.exit(app.exec_())