Alright, so to start I'm using Python 3.7 with tkinter.
I have a canvas and I can drag a label around with that using the mouse events. My next step is to be able to drag around something on which I can put more widgets. So imagine a box which has a text box and an image on it, perhaps a combobox too. That box can then be dragged around.
I figured perhaps what I needed was a frame widget on my canvas which I could then set up in the same way as I had done with the label. But this is where it seems to fall apart - clearly I'm doing something wrong.
Here's the code I've been playing around with, to no avail:
root = tk.Tk()
root.geometry("800x600")
def ClickedCallback(event):
print(f'Clicked: coords: {event.x}, {event.y}')
def ReleaseCallback(event):
print(f'Released: coords: {event.x}, {event.y}')
def MotionCallback(event):
print(f'Motion: coords: {event.x}, {event.y}')
canvas.coords(frame_id, event.x, event.y)
canvas = tk.Canvas(root, width=1000, height=600, bg='blue')
canvas.bind("<B1-Motion>", MotionCallback)
canvas.pack()
frame = tk.Frame(canvas, bg='green')
frame.pack()
l2 = tk.Label(frame, bg='red')
l2.bind("<Button-1>", ClickedCallback)
l2.pack()
l2['text'] = "Test"
frame_id = canvas.create_window((300,300), window=frame)
label_id = canvas.create_window((100, 100), window=l2)
root.mainloop()
My thinking here is that I attach the frame to the canvas and then the label to the frame so that, f I move the frame, the label within it will be moved too.
The above won't work though, and it tells me the following:
File "C:\Users\JohnSmith\AppData\Local\Programs\Python\Python37-32\lib\tkinter__init__.py", line 2480, in _create
*(args + self._options(cnf, kw))))
_tkinter.TclError: can't use .!canvas.!frame.!label in a window item of this canvas
I may be going about this in entirely the wrong way. I had looked around for something more, but if there's something I'm doing entirely wrong or an established way I should be doing this, I would appreciate it if you could point me in the right direction.
Related
How are you supposed to go around sizing all the buttons to be the same size regardless of the text you insert inside, they should all size according to the biggest one.
There is an answer to a similar question like mine already, but it is done using grid and I am using a canvas to place a background image in the window and to place the buttons.
Is it even worth the hassle to get your buttons to the same size according to text automatically, since my text will always be around the same...
I tried getting the size of the buttons using cget() but that returns 0. Where does it store its width then since it has to size itself somehow even if it does it according to text? Can access that in any way? I was thinking of using that value to adjust the value of other buttons somehow, but it turned out as a fail.
If you are wondering why am I making it into a class, idk either, wanted to try it.
I had it working by putting all the buttons in a frame and telling them to fill=x but using a frame destroys the point of using a canvas since the background can't be seen because the frame covers it. Is there a way to make the frame transparent in the canvas, that could also potentially solve my problem.
from tkinter import *
class ThreeButtonMenu():
def __init__(self, button1_text, button2_text, button3_text, image_height = 600, image_width = 500, bg_input = 'space_background.png'):
self.root = Tk()
HxW = str(image_height)+'x'+str(image_width)
self.root.geometry(HxW)
self.root.maxsize(image_height,image_width)
self.root.minsize(image_height,image_width)
self.root.title('Guess')
bg = PhotoImage(file=bg_input)
background_canvas = Canvas(self.root, width = 600, height=500)
background_canvas.pack(fill="both", expand=True)
background_canvas.create_image(0,0, image=bg, anchor='nw')
button1 = Button(self.root, text=button1_text, font = ('Lato',28))
button2 = Button(self.root, text=button2_text, font = ('Lato',28))
button3 = Button(self.root, text=button3_text, font = ('Lato',28), command = self.root.destroy)
button1_window = background_canvas.create_window(300,45, anchor=N, window=button1)
button2_window = background_canvas.create_window(300,160, anchor=N, window=button2)
button3_window = background_canvas.create_window(300,275, anchor=N, window=button3)
print(button1.cget('width'))
print(button2.cget('width'))
print(button3.cget('width'))
self.root.mainloop()
start_menu = ThreeButtonMenu('Start Game', 'Leaderboard', 'Quit')
Thank you for your answers.
You would typically do this when you use a geometry manager (pack, place, or grid).
For example, you need to call pack on each of the buttons. See example of pack below.
import tkinter as tk
root = tk.Tk()
for text in (
"Hello", "short", "All the buttons are not the same size",
"Options", "Test2", "ABC", "This button is so much larger"):
button = tk.Button(root, text=text)
button.pack(side="top", fill="x")
root.mainloop()
I'm working on a GUI with tkinter and i have a problem.
When i add a scrollbar to my app, the frame on my canvas overlaps the outlines (see image)
Here is the code:
from tkinter import *
window = Tk()
window.geometry("400x225")
scrollbar1 = Scrollbar(window, orient=VERTICAL)
canvas1 = Canvas(window, bg="#003333", yscrollcommand=scrollbar1.set)
frame1 = Frame(canvas1, bg="#003333")
scrollbar1.config(command=canvas1.yview)
scrollbar1.pack(side=RIGHT, fill=Y)
canvas1.pack(fill=BOTH, expand=True)
canvas1.create_window((0, 0), window=frame1, anchor="nw")
for x in range(20):
string = "line " + str(x)
label1 = Label(frame1, fg="white", bg="#003333", text=string, font=("Calibri Bold", 14))
label1.pack(pady=5)
window.update()
canvas1.config(scrollregion=canvas1.bbox("all"))
window.mainloop()
I don't know if it's possible but i want the frame to fit within the canvas and keeping the outlines as well.
I hope you get my problem and can probably help me out! Thanks in advance.
The highlightthickness
Specifies a non-negative value indicating the width of the highlight rectangle to draw around the outside of the widget when it has the input focus.
So, this is not really the "border" that you want. It is a part of the drawing space within the canvas, when you use window_create to draw a window, the parent of that window is the canvas, which begins before the highlight and so the window slides over it.
A solution, as also suggested by #martineau would be to make this 0 by specifying highlightthickness=0 and as you suggested that you need the "border" around the whole thing, you can either create a container frame and specify the bd parameter, or just set the bd of the window window.config(bd=2).
I'm making a card game (called monster master) to develop my python skills, specifically OOP.
I have a GUI that has a few static objects: Player 1's side of the table, Player 2's side, a wee line in the middle and I'm now trying to implement an 'end turn' button.
I have tried a lot of different things to try to get this button to display, but I just can't get it to appear even if there are no errors. Just saying that there are a few commented out lines that I've temporarily taken away for the sake of trying to understand the problem with this button.
Here's the code that I'm currently using to try:
def RunGame():
class App():
"""docstring for App"""
def draw():
# Setting up canvas dimensions
canvas_width = 640
canvas_height = 480
master = Toplevel()
master.title("Monster Master by Charles Cameron - Game")
master.resizable(width=False, height=False)
master.geometry("640x480")
w = Canvas(master,
width=canvas_width,
height=canvas_height)
w.pack()
# Drawing static objects
CentrePoints = [(0, canvas_height/2), (canvas_width/2, canvas_height/2),
(canvas_width, canvas_height/2)]
#Left, centre and right centres (save me from retyping them)
Player1Area = w.create_rectangle(CentrePoints[0], canvas_width,
canvas_height, fill="#303AFE") #Player1 Area
Player2Area = w.create_rectangle(0, 0, CentrePoints[2],
fill="#C31B1B") #Player2 Area
Barrier = w.create_line(CentrePoints[0], CentrePoints[2],
fill="#0090E3", width=20) # Centre barrier
# class GameBtn():
# class EndTurnBtn():
# def __init__(self, BtnName, master):
BtnName = Button(master, bg="white", command=lambda:print("Clicked!"))
image = ImageTk.PhotoImage(file="imgs\EndTurn.png")
BtnName.config(image=image, width="70", height="70")
BtnName.pack(side=RIGHT)
# ChangeTurn = GameBtn.EndTurnBtn('ChangeTurn', master)
master.mainloop()
Window = App()
App.draw()
The button code for the actual button worked fine when I tried it in its own script but stopped working when I put it inside this program.
Hope it's not too dumb a question to ask, quite an amateur at python still and honestly can't find the answer anywhere online.
Many thanks
Your button exists, but it's past the edge of the window. This is because you made your window 640x480, and then completely filled it with a 640x480 canvas. Remove the master.geometry("640x480") line and the window will stretch to contain both your canvas and your button.
You might be thinking "but I don't want the button to appear to the side of the canvas. I want the button to be on the canvas. The canvas only really exists because I wanted to color sections of the background". Embedding widgets on a canvas is possible using create_window (see How to place a widget in a Canvas widget in Tkinter?), but it may be more practical to create colored backgrounds by stacking Frame objects together and giving them individual colors. Example:
from tkinter import Tk, Frame, Button
root = Tk()
root.geometry("640x480")
top_player_frame = Frame(root, height=230, bg="red")
barrier = Frame(root, height=20, bg="green")
bottom_player_frame = Frame(root, height = 230, bg="blue")
#configure column 0 so frames can stretch to fit width of window
root.columnconfigure(0, weight=1)
top_player_frame.grid(row=0, sticky="we")
barrier.grid(row=1, sticky="we")
bottom_player_frame.grid(row=2, sticky="we")
bottom_player_end_turn_button = Button(bottom_player_frame, text="End Turn")
#use `place` here because `pack` or `grid` will collapse the frame to be only as tall as all the widgets it contains, i.e. just this button
bottom_player_end_turn_button.place(x=10,y=10)
root.mainloop()
Result:
I am making a text based adventure that prints labels of game text to the screen. I need to make the window scrollable so I am attempting to implement a canvas, containing a frame, which contains the labels.
For some reason when I do this:
display = Tk()
display.geometry('1000x800')
canvas = Canvas(display)
frame = Frame(canvas)
canvas.pack()
frame.pack()
The labels are packed properly, but obviously can't be scrolled.
However when I do this (Create a frame window in the canvas):
canvas = Canvas(display)
frame = Frame(canvas, width=1000, height=700)
canvas.pack()
canvas.create_window((0,0), window=frame, anchor='nw')
The first label does not fit in the frame, and after the second one is printed they all just disappear?
Labels of game text are printed as this function is called throughout the main game loop:
def output(text):
l = Label(frame,text=text,justify=LEFT,font=font)
l.pack()
display.update()
I've been fiddling with container dimensions and some pack() attributes, however nothing seems to be working. The window's dimensions don't seem to change based on the frame's.
I should be able to add a scrollbar if i can just get these to draw properly.
Thanks in advance!
I'm working on a new program in Python 3 with tkinter. I'm trying to add a scrollbar to the whole window, but it isn't working. Right now I'm just trying to make the window 1000 pixels tall (will be set later in the program) and use the a vertical scrollbar to access the parts not seen on screen. I've read multiple other threads trying to figure it out and have attempted. Could someone tell how to get it to work and what I did wrong. No error is displayed, but also no scrollbar is displayed. Here's the code:
from tkinter import *
class MusicPlayer:
def __init__(self):
self.tk = Tk()
self.tk.title("Bass Blaster")
self.screen_width, self.screen_height = self.tk.winfo_screenwidth(), self.tk.winfo_screenheight()
self.frame = Frame(self.tk, width=self.screen_width, height=self.screen_height)
self.frame.grid(row=0, column=0)
self.canvas = Canvas(self.frame, bg="#585858", width=self.screen_width, height=self.screen_height, scrollregion=(0, 0, self.screen_width, 1000))
vbar = Scrollbar(self.frame, orient=VERTICAL)
vbar.pack(side=RIGHT, fill=Y)
vbar.config(command=self.canvas.yview)
self.canvas.config(width=self.screen_width, height=self.screen_height)
self.canvas.config(yscrollcommand=vbar.set)
self.canvas.pack(side=LEFT, expand=True, fill=BOTH)
self.tk.update()
bass_blaster = MusicPlayer()
bass_blaster.tk.mainloop()
The scroll bar was there the whole time, the problem was that I made the canvas width the screen's size and the scroll bar was adding onto the width, therefore going off the screen. I just had to make the width a little less.