In my main script I have the following code:
class Sequence:
def __init__(self, colour, text):
self.colour = colour
self.width = 5
self.height = 5
self.text = text
def create_window(self, root):
self.name=tk.Label(root,text=self.text, wdith=self.width, height=self.height,
bg=self.colour
self.name.pack()
In my gui script this code is run as
Sequence.create_window(self, root)
I get the error:
Sequence.create_window(self, root) NameError: name 'self' is not defined
Im not really sure how to fix this, does it have do with the scope of the program?
You need to initialize an object first:
class Sequence:
def __init__(self, colour, text):
self.colour = colour
self.width = 5
self.height = 5
self.text = text
def create_window(self, root):
self.name=tk.Label(root,text=self.text, wdith=self.width, height=self.height,
bg=self.colour)
self.name.pack()
sequence = Sequence("blue", "test")
sequence.create_window(root)
root is a variable defined in create_window method
tk.Label need to be placed in either window or frame which is passed as first argument.
import sys
if sys.version_info.major == 2
# for python 2
import Tkinter as tk
else
# for python 3
import tkinter as tk
class Sequence:
def __init__(self, colour, text):
self.colour = colour
self.width = 5
self.height = 5
self.text = text
def create_window(self, root):
self.name=tk.Label(root, text=self.text, wdith=self.width, height=self.height,
bg=self.colour)
self.name.pack()
if __name__ == "__main__":
root = tk.Tk()
sequence = Sequence("blue", "test")
sequence.create_window(root)
root.mainloop()
Related
UIconstruction.py
import tkinter
class Window:
def __init__(self):
self.height = None
self.width = None
self.title = None
def setDimensions(self, height, width):
self.height = height
self.width = width
def setTitle(self, title):
self.title = title
def GetDimensions(self):
dimensionsDict = {
"Height": self.height,
"Width": self.width
}
return dimensionsDict
def getTitle(self):
return self.title
def startWindow(self):
Window.tk = tkinter.Tk()
Window.tk.geometry(f"{self.width}x{self.height}")
Window.tk.title(self.title)
Window.tk.mainloop()
class Button:
def __init__(self):
self.height = None
self.width = None
self.label = None
self.container = None
def setDimensions(self, height, width):
self.height = height
self.width = width
def GetDimensions(self):
dimensionsDict = {
"Height": self.height,
"Width": self.width
}
return dimensionsDict
def setLabel(self, labelText):
self.label = labelText
def getLabel(self):
return self.label
def createButton(self):
button = tkinter.Button(text = self.label, width = self.width, height = self.height)
main.py
from UIconstruction import Window, Button
background = Window()
button1 = Button()
background.setDimensions(500, 500)
background.setTitle("Test")
background.startWindow()
button1.setDimensions(100, 100)
button1.setLabel("test")
button1.createButton()
What I want is for 'button1' is to be in the window 'background', but I have not been able to figure out how to do this (incorrect attempts omitted)
Is there a way to do this with this setup - if so, how?
If there is not a way to do this like this, how should I be doing it optimally.
I'm a relative python beginner, so I don't know much more than the basics past object oriented support.
Thanks!
This code has many mistakes - and this can make big problem.
First: you should keep tk.Tk() as self.tk instead of Window.tk
Second: you need backgroud.tk to assign button. But this makes problem. You have to create tk.Tk() before you create Button() (to use it as parent for for Button()), but you have to run mainloop() after adding Button() to Window() (because mainloop() blocks rest of code)
You should send it as parameter
button1 = Button(background.tk)
and keep it in __init__
class Button:
def __init__(self, parent):
self.height = None
self.width = None
self.label = None
self.container = None
self.parent = parent
and use it when you create real tk.Button
def createButton(self):
button = tkinter.Button(self.parent, text=self.label, width=self.width, height=self.height) # PEP8: inside `()` use `=` without spaces
Eventually you could send it directly to createButton()
def createButton(self, parent):
button = tkinter.Button(parent, text=self.label, width=self.width, height=self.height) # PEP8: inside `()` use `=` without spaces
and run it as
button1.createButton(background)
And you have to also use .pack(), .grid() or .place() to show widget in window.
def createButton(self):
button = tkinter.Button(self.parent, text=self.label, width=self.width, height=self.height) # PEP8: inside `()` use `=` without spaces
button.pack()
Full working code
import tkinter
class Window:
def __init__(self):
self.height = None
self.width = None
self.title = None
def setDimensions(self, height, width):
self.height = height
self.width = width
def setTitle(self, title):
self.title = title
def GetDimensions(self):
dimensionsDict = {
"Height": self.height,
"Width": self.width
}
return dimensionsDict
def getTitle(self):
return self.title
def create(self):
self.tk = tkinter.Tk()
self.tk.geometry(f"{self.width}x{self.height}")
self.tk.title(self.title)
def startWindow(self):
self.tk.mainloop()
class Button:
def __init__(self, parent):
self.height = None
self.width = None
self.label = None
self.container = None
self.parent = parent
def setDimensions(self, height, width):
self.height = height
self.width = width
def GetDimensions(self):
dimensionsDict = {
"Height": self.height,
"Width": self.width
}
return dimensionsDict
def setLabel(self, labelText):
self.label = labelText
def getLabel(self):
return self.label
def create(self):
self.button = tkinter.Button(self.parent, text=self.label, width=self.width, height=self.height)
self.button.pack()
#from UIconstruction import Window, Button
background = Window()
background.setDimensions(500, 500)
background.setTitle("Test")
background.create()
button1 = Button(background.tk)
button1.setDimensions(100, 100)
button1.setLabel("test")
button1.create()
background.startWindow()
EDIT:
Code would be more useful if you would create window and button in __init__ and you would use special function to set values in widgets because in current code you can't use setLabel to change text in button after creating it.
I'm working with python 3.8 and tkinter to create a GUI that uses a ttk notebook (tabbed window). I want to have tooltips displayed for the individual tabs but I can't see how to add the tooltip for the tab. I have a class defined that takes an object and text and adds the tooltip and it works for buttons and other objects.
I have example code here:
'''
At no time were Katz, Dawgs, Mice or Squirrels harmed in any way during the development of this application. But...
Well just lets say that skunk next door had it coming!
'''
import tkinter as tk
from tkinter import ttk
class CreateToolTip(object):
"""
create a tooltip for a given widget
"""
def __init__(self, widget, text='widget info'):
self.waittime = 250 #miliseconds
self.wraplength = 180 #pixels
self.widget = widget
self.text = text
self.widget.bind("<Enter>", self.enter)
self.widget.bind("<Leave>", self.leave)
self.widget.bind("<ButtonPress>", self.leave)
self.id = None
self.tw = None
def enter(self, event=None):
self.schedule()
def leave(self, event=None):
self.unschedule()
self.hidetip()
def schedule(self):
self.unschedule()
self.id = self.widget.after(self.waittime, self.showtip)
def unschedule(self):
id = self.id
self.id = None
if id:
self.widget.after_cancel(id)
def showtip(self, event=None):
x = y = 0
x, y, cx, cy = self.widget.bbox("insert")
x += self.widget.winfo_rootx() + 25
y += self.widget.winfo_rooty() + 20
# creates a toplevel window
self.tw = tk.Toplevel(self.widget)
# Leaves only the label and removes the app window
self.tw.wm_overrideredirect(True)
self.tw.wm_geometry("+%d+%d" % (x, y))
label = tk.Label(self.tw, text=self.text, justify='left',
background="cornsilk", relief='solid', borderwidth=1,
wraplength = self.wraplength)
label.pack(ipadx=1)
def hidetip(self):
tw = self.tw
self.tw= None
if tw:
tw.destroy()
class MyCanvas(tk.Tk):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.tabControl = ttk.Notebook(self)
self.geometry('600x300')
self.title('An Indepth study of the Differences Between Katz and Dawgs!')
self.tab = {}
self.tab['dawg tab'] = ttk.Frame(self.tabControl)
self.tabControl.add(self.tab['dawg tab'], text='dawgs', state='normal')
self.tab['katz tab'] = ttk.Frame(self.tabControl)
self.tabControl.add(self.tab['katz tab'], text='katz', state='normal')
self.tabControl.enable_traversal()
btn1 = tk.Button(self.tab['dawg tab'], text='Squirrel')
btn1.grid(row=1, column=0)
CreateToolTip(btn1, 'Despised by dawgs!!')
btn2 = tk.Button(self.tab['katz tab'], text='Mice')
btn2.grid(row=1, sticky='EW')
CreateToolTip(btn2, 'Katz find them a tasty treat.')
self.tabControl.grid(row=0, column=0, sticky="W")
# testing ...
if __name__ == '__main__':
root = MyCanvas()
root.mainloop()
Any advice or constructive comments would be greatly appreciated. And yes, the tooltip class was totally plagiarized from someone else here on stack overflow.
Cheers!!
So after a few days and lots of help from a friend here is what I have that actually works:
'''
At no time were Katz, Dawgs, Mice or Squirrels harmed in any way during the development of this application. But...
Well lets just say that skunk next door had it coming!
'''
import tkinter as tk
import tkinter.ttk as ttk
import tkinter.scrolledtext as tkst
class MyCanvas(ttk.Notebook):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.tooltips = dict()
self.tab_list = dict()
self.current_tab = None
self.waittime = 250 #miliseconds
self.wraplength = 180 #pixels
self.id = None
self.tw = None
self.bind('<Motion>', self.motion)
self.bind('<ButtonPress>', self.leave)
self.line_number = 0
def leave(self, event=None):
self.unschedule()
self.hide_tip()
def schedule(self):
self.unschedule()
self.id = self.after(self.waittime, self.show_tip)
def unschedule(self):
id = self.id
self.id = None
if id:
self.after_cancel(id)
def show_tip(self):
if self.tw:
return
if self.current_tab in self.tooltips.keys():
x = y = 0
x, y, cx, cy = self.bbox('insert')
x += self.winfo_rootx() + 25
y += self.winfo_rooty() - 20
# creates a toplevel window
self.tw = tk.Toplevel(self)
# Leaves only the label and removes the app window
self.tw.wm_overrideredirect(True)
self.tw.wm_geometry(f'+{x}+{y}')
self.tw.wm_attributes('-topmost', True)
label = ttk.Label(self.tw, text=self.tooltips[self.current_tab], background='cornsilk')
label.pack()
def add(self, *args, **kwargs):
if 'tooltip' in kwargs.keys():
tooltip = kwargs['tooltip']
del kwargs['tooltip']
else:
tooltip = None
self.tab_list[kwargs.get('text')] = args[0]
super().add(*args, **kwargs)
if tooltip and ('text' in kwargs.keys()):
self.set_tooltip(kwargs['text'], tooltip)
def set_tooltip(self, tab_text, tooltip):
for idx in range(self.index('end')):
if self.tab(idx, 'text') == tab_text:
self.tooltips[tab_text] = tooltip
return True
return False
def motion(self, event):
if self.identify(event.x, event.y) == 'label':
index = self.index(f'#{event.x},{event.y}')
if self.current_tab != self.tab(index, 'text'):
self.current_tab = self.tab(index, 'text')
self.show_tip()
else:
self.hide_tip()
self.current_tab = None
def hide_tip(self):
tw = self.tw
self.tw = None
if tw:
tw.destroy()
def insert_text_to_tab(self, **kwargs):
string_var = tk.StringVar()
if 'tab' in kwargs.keys():
the_tab = kwargs.get('tab')
string_var.set(kwargs.get('data'))
the_label = tk.Label(self.tab_list[the_tab], textvariable=string_var, background='lightblue')
the_label.pack()
def get_scrolled_text_widget(parent):
st_widget = tkst.ScrolledText(parent)
return st_widget
if __name__ == '__main__':
root = tk.Tk()
root.title('An Indepth study of the Differences Between Katz and Dawgs!')
canvas = MyCanvas(root)
canvas.pack(fill='both', expand=1)
canvas.add(get_scrolled_text_widget(canvas), text='Dawgz', tooltip='A quadraped that doesn\'t look like a kat')
canvas.add(get_scrolled_text_widget(canvas), text='Katz', tooltip='A quadraped that doesn\'t look like a dawg')
canvas.insert_text_to_tab(data='Dawgz hate squirrels!!', tab='Dawgz')
canvas.insert_text_to_tab(data='Katz find mice a tasty treat.', tab='Katz')
root.mainloop()
So it would appear that I was thinking about it a bit wrong and I had only to think of each tabbed entry as an object instead of an attribute (I'm sure the python peanut gallery are going to be all over me for saying that) the solution is quit straight forward.
Cheers!!
I'm making a chess game.
I've created a main file with the Tkinter code in a class "Window". In this class, I created a canvas.
Then I've created a second file with the name "pieces", where I put the behaviour of the different pieces. In this one, I have a superclass "Pieces", and a subclass "Bishop" (because I haven't created the classes for the other pieces yet)
What I tried first to do, is to create a bishop's icon in the constructor of the class "Bishop".
My class "Bishop" has the argument "color", so that, when we create an object "Bishop", we can choose if he's black or white.
So I wrote :
if self.color == "black":
icon_path = 'black_bishop.png'
elif self.color == "white":
icon_path = 'white_bishop.png'
self.icon = PhotoImage(file=icon_path)
main.Window.canvas.create_image(x_position, y_position, self.icon)
The problem is, when I create a new object of the bishop, it makes a loop.
I don't really know how to fix this, a solution would be to put the whole code in the same file, but I don't like it because it's not clean.
If you need the whole code, I can give it to you.
that's the complete error:
Traceback (most recent call last):
File "C:\Users\CSP\PycharmProjects\chess_game\main.py", line 2, in <module>
import pieces
File "C:\Users\CSP\PycharmProjects\chess_game\pieces.py", line 3, in <module>
import main
File "C:\Users\CSP\PycharmProjects\chess_game\main.py", line 28, in <module>
fen = Window()
File "C:\Users\CSP\PycharmProjects\chess_game\main.py", line 20, in __init__
pieces.Bishop("black", 5, 5)
AttributeError: partially initialized module 'pieces' has no attribute 'Bishop' (most likely due to a circular import)
I've written import main in the file pieces, because I want to create the image in the constructor of the class Bishop, I thought it was cleaner than do it in the class Windows, because I would have to do I 32 times (one per piece) and it would be very heavy.
And to create the image in the class bishop, I need to import the module main (because I use canvas.create_image(), and the canvas is in the class Windows)
But if I write import main it makes a loop, so do you have an idea to fix this?
Here's the code, very simplified
main.py
from tkinter import *
import pieces
#GUI class
class Window:
def __init__(self):
self.window = Tk()
self.window.title("My chess game")
self.frame = Frame(self.window, bg='#41B77F')
self.canvas = Canvas(self.frame, width=500, height=500, bg="skyblue", bd=0, highlightthickness=0)
self.canvas.pack()
bishop = Bishop("black",5 , 5)
self.frame.pack(expand=YES)
win = Window()
win.window.mainloop()
pieces.py
from tkinter import PhotoImage
import main
#superclass
class Pieces:
def __init__(self, color, x_position, y_position):
self.color = color
self.x_position = x_position
self.y_position = y_position
#subclass
class Bishop(Pieces):
def __init__(self, color, x_position, y_position):
super().__init__(color, x_position, y_position)
if self.color == "black":
icon_path = 'black_bishop.png'
elif self.color == "white":
icon_path = 'white_bishop.png'
self.icon = PhotoImage(file=icon_path)
main.Window.canvas.create_image(x_position, y_position, image=self.icon)
If you are familiar with Model/View approach of writing code, it will help you find your way around tkinter applications. In such a case you would place all the code relating to views in one class and all the data is managed in the Model class(es).
In your case, you could start with the structure illustrated below and grow from it:
# model.py
from tkinter import *
class GameModel():
# maintains game state/data
# may include positions of pieces on board
# number of moves made by each player
pass
#superclass, inherits from Tk().Canvas
class Pieces(Canvas):
def __init__(self, color, x_position, y_position):
self.color = color
self.x_position = x_position
self.y_position = y_position
#subclass
class Bishop(Pieces):
def __init__(self, color, x_position, y_position):
super().__init__(color, x_position, y_position)
if self.color == "black":
icon_path = 'black_bishop.png'
elif self.color == "white":
icon_path = 'white_bishop.png'
self.icon = PhotoImage(file=icon_path)
# main.Fenetre.canvas.create_image(x_position, y_position, image=self.icon)
self.create_image(x_position, y_position, image=self.icon)
# because bishop inherits from pieces which inherits from Canvas, its possible
# to call .create_image() from within Bishop class using self.create_image()
# gameinterface.py
from tkinter import *
#GUI/View class
class GameInterface():
def __init__(self, window: Tk()):
self.window = window
self.frame = Frame(self.window, bg='#41B77F')
self.canvas = Canvas(self.frame, width=500, height=500, bg="skyblue", bd=0, highlightthickness=0)
self.canvas.pack()
self.frame.pack(expand=YES)
def play(self, game: GameModel):
# the game objects give access to state of game and all data
self.window.mainloop()
# main.py
from tkinter import *
def main() -> None:
"""Entry point to gameplay."""
window = Tk()
# window = Tk()
#set title and position at center of screen
window.title("My chess game")
# game object gives you access to all relevant data
game = GameModel()
# app object gives you access to view
app = GameInterface(window)
# app.play combines the model and the view
app.play(game)
if __name__ == '__main__':
main()
If you need a reference to the window in Bishop, you have to pass it as an argument:
from tkinter import PhotoImage
class Pieces:
def __init__(self, color, x_position, y_position):
self.color = color
self.x_position = x_position
self.y_position = y_position
#subclass
class Bishop(Pieces):
def __init__(self, win, color, x_position, y_position):
super().__init__(color, x_position, y_position)
if self.color == "black":
icon_path = 'black_bishop.png'
elif self.color == "white":
icon_path = 'white_bishop.png'
self.icon = PhotoImage(file=icon_path)
win.canvas.create_image(x_position, y_position, image=self.icon)
I have a class - code below - which works flawlessly when called from if __name__ == '__main__': but when I call it from another .py file it spits an error. Apologies if this is a basic mistake on my part, but I tried a LOT of stuff and nothing seemed to work, would really love some help!
The error:
File "c:\Users\...\test.py", line 6, in <module>
app = Screenshot(root)
File "c:\Users\...\Screenshot.py", line 18, in __init__
self.master_screen = Toplevel(root)
NameError: name 'root' is not defined
The Class:
import time
from tkinter import Toplevel, Canvas, Frame, BOTH, YES, Tk
import pyautogui
import datetime
class Screenshot():
def __init__(self, master):
self.master = master
self.rect = None
self.x = self.y = 0
self.start_x = None
self.start_y = None
self.curX = None
self.curY = None
self.master_screen = Toplevel(root)
self.master_screen.title("Fenify")
self.master_screen.attributes("-transparent", "blue")
self.picture_frame = Frame(self.master_screen)
self.picture_frame.pack(fill=BOTH, expand=YES)
self.createScreenCanvas()
#TakeScreenshot
def takeBoundedScreenShot(self, x1, y1, x2, y2):
...
#Window
def createScreenCanvas(self):
...
#Controls
def on_button_press(self, event):
...
def on_button_release(self, event):
...
def on_right_click(self, event):
...
def on_move_press(self, event):
...
#Exit
def exit_screenshot(self):
...
if __name__ == '__main__':
root = Tk()
app = Screenshot(root)
root.mainloop()
example of calling from another class:
import tkinter
from Screenshot import Screenshot
root = tkinter.Tk()
app = Screenshot(root)
root.mainloop()
You want to set the parent/master of your Toplevel to self.master instead of root. root isn't defined in the scope of the Screenshot class, but the input argument master does point to it.
self.master_screen = Toplevel(self.master)
I'm trying to make a grid with a canvas using tkinter and after I made some change the lines stopped being drawn. I'm not sure what I did but it might have to do with how the Canvas object is nested in the Frame object, but it was working just fine like that at one point.
I also tried using shapes and couldn't get them to draw.
from tkinter import *
class Application(Frame):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.canvas = CanvasClass()
class CanvasClass(Canvas):
def __init__(self):
super().__init__()
self.CanvasHeight = 600
self.CanvasWidth = 800
self.c = Canvas(width=self.CanvasWidth, height=self.CanvasHeight)
self.CreateBackground(20)
self.c.pack(expand=True)
def CreateBackground(self, d):
print(self.CanvasHeight)
print(self.CanvasWidth)
for x in range(d, self.CanvasWidth, d):
print(x)
self.create_line(x, 0, x, self.CanvasHeight)
root = Tk()
root.title = "TESTING"
app = Application()
app.canvas.create_polygon(50,200,50,200)
root.mainloop()
from tkinter import *
class Application(Frame):
def __init__(self,p):
super().__init__(p)
self.initUI()
def initUI(self):
self.canvas = CanvasClass(self)
self.canvas.pack()#1
class CanvasClass(Canvas):
def __init__(self,p):#2
super().__init__(p)
self.CanvasHeight = 600
self.CanvasWidth = 800
self.c = Canvas(width=self.CanvasWidth, height=self.CanvasHeight)
self.CreateBackground(20)
self.c.pack(expand=True)
def CreateBackground(self, d):
print(self.CanvasHeight)
print(self.CanvasWidth)
for x in range(d, self.CanvasWidth, d):
print(x)
self.create_line(x, 0, x, self.CanvasHeight)
root = Tk()
root.title = "TESTING"
app = Application(root)
app.canvas.create_polygon(50,200,50,200)
app.pack()#3
root.mainloop()
you forgot to add the parent and insert it into the window I have used pack() you can change if you want. and I got grids of lines in the window at the bottom when I execute after packing