I'm trying to embed a matplotlib animation into my PyQt5-GUI. Here's my class of the animation:
class AnimationCanvas(FigureCanvas):
def __init__(self, parent=None, width=5, height=4, dpi=100):
self.fig = Figure(figsize=(width, height), dpi=dpi)
self.fig.set_facecolor('#1a1a1a')
self.ax = self.fig.add_subplot(111,polar=True)
self.ax.set_facecolor('#333333')
self.ax.set_ylim(0,1)
self.ax.tick_params(axis='x', labelsize=8,colors='#ffffff')
self.ax.set_yticks([]) # Shows no r values
self.ax.set_xticks(np.pi/180. * np.linspace(0, 360, 18, endpoint=False)) # A line every 20 degrees
self.ax.grid(color='#595959', linestyle='-.', linewidth=0.7)
self.ax.set_title("Position of the speakers", pad = 8,
fontdict= dict(
color = '#ffffff',
fontsize = 15,
fontweight = 0.9))
self.line, = self.ax.plot([], [], marker = 'X', color= '#ffffe6',markersize=15)
super(AnimationCanvas, self).__init__(self.fig)
When I use the following code in a MainWindow class (that handles the animation) to set it as a central widget it shows, no problem there. But I want to embed it into my existing GUI so that it's not the central widget. When I try to give it an absolute position by using setGeometry() or move(), it just doesn't show up anymore. I've also tried using QVBoxLayout and adding self.canvas to the layout, that works too, but I don't know how to stylize it properly so that it doesn't look like a huge mess with those unwanted whitespaces.
class MainWindow(QtWidgets.QMainWindow):
def __init__(self,xdata, *args, **kwargs,):
super(MainWindow, self).__init__(*args, **kwargs)
widget = QWidget()
vbox = QVBoxLayout(widget)
self.canvas = AnimationCanvas(self, width=5, height=4, dpi=100)
self.setCentralWidget(self.canvas)
vbox.addWidget(self.canvas)
self.angles = xdata*(3.14/180)
self.ydata = np.ones(len(self.angles))*0.96
self.index = 0
self.line = self.canvas.line
self.plot_button = QtWidgets.QPushButton(self.canvas)
self.plot_button.setGeometry(QtCore.QRect(5, 5, 50, 25))
self.plot_button.setText("plot")
self.plot_button.setObjectName("plot")
self.plot_button.clicked.connect(self.plot_animation)
vbox.addWidget(self.plot_button)
self.setCentralWidget(widget)
self.setGeometry(300, 300, 800, 600)
Questions:
Why doesn't setGeometry() work in this instance and how to fix it.
If I have to use QVBoxLayout, what's the best way to achieve a nice look? addSpacing() and addStretch() everywhere seems like a bit of an overkill to me.
Thanks in advance!
If you want to place your AnimationCanvas directly on the central widget then call setParent in the constructor.
class AnimationCanvas(FigureCanvas):
def __init__(self, parent=None, width=5, height=4, dpi=100):
...
super(AnimationCanvas, self).__init__(self.fig)
self.setParent(parent)
And construct the AnimationCanvas with parent widget, the central widget of the QMainWindow. The call to setGeometry on the QPushButton will now work, it will be placed on the AnimationCanvas at position (5, 5) with the size (50, 25).
class MainWindow(QtWidgets.QMainWindow):
def __init__(self,xdata=None, *args, **kwargs,):
super(MainWindow, self).__init__(*args, **kwargs)
widget = QWidget()
self.setCentralWidget(widget)
self.canvas = AnimationCanvas(parent=widget, width=5, height=4, dpi=100)
self.plot_button = QtWidgets.QPushButton(self.canvas)
self.plot_button.setGeometry(QtCore.QRect(5, 5, 50, 25))
self.setGeometry(300, 300, 800, 600)
If you use a layout then setGeometry will usually be ignored because the layout manages the position and size of all its widgets. However, you could add only the AnimationCanvas to the layout and not the QPushButton, allowing it to stay on the AnimationCanvas with the exact geometry you specify.
class MainWindow(QtWidgets.QMainWindow):
def __init__(self,xdata=None, *args, **kwargs,):
super(MainWindow, self).__init__(*args, **kwargs)
widget = QWidget()
self.setCentralWidget(widget)
vbox = QVBoxLayout(widget)
self.canvas = AnimationCanvas(width=5, height=4, dpi=100)
vbox.addWidget(self.canvas)
self.plot_button = QtWidgets.QPushButton(self.canvas)
self.plot_button.setGeometry(QtCore.QRect(5, 5, 50, 25))
self.setGeometry(300, 300, 800, 600)
Related
I'm trying to make the self.frame widget fill the entire canvas. I'm new to canvas so I'm lost, this was my last failed attempt. I don't know what I'm missing. The code is executable, thank you very much for any help.
from tkinter import *
class Example(Frame):
def __init__(self, master, x=None, y=None, *args, **kwargs):
super().__init__(master, *args, kwargs)
self.lblframe = LabelFrame(self, text='Ajustes 0', bg='#2b313c', fg='green2', cursor='arrow')
self.lblframe .pack(side='left', fill='both', expand=True, padx=(10,5), pady=(0,10))
self.canvas = Canvas(self.lblframe, borderwidth=0, bg='red', width=x, height=100, bd=0, highlightthickness=0)
self.canvas .pack(side="left", fill='x', expand=True)
self.scrolly = Scrollbar(self.lblframe, orient="vertical", command=self.canvas.yview)
self.scrolly .pack(side="right", fill="y")
self.frame = Frame(self.canvas, bg='#2b313c', width=x, height=100)
self.frame .pack(fill='both', expand=True)
self.canvas .config(yscrollcommand=self.scrolly.set)
self.canvas .bind("<Configure>", lambda e: self.canvas.configure(scrollregion=self.canvas.bbox('all')))
self.canvas .create_window((0,0), window=self.frame, anchor="nw")
#---loop----
for i in range(8):
label = Label(self.frame, text='number : %s' % i, font=('Calibri',9,'bold'), bg='#2b313c', fg='white', bd=0)
label .grid(column=0, row=i, padx= (10,5), pady=(0,0), sticky='w')
root = Tk()
root .geometry('300x150')
frame = Example(root, 200, 100).pack()
root.mainloop()
You can resize the frame inside the callback of <Configure> event of the canvas:
class Example(Frame):
def __init__(self, master, x=None, y=None, *args, **kwargs):
...
self.canvas .bind("<Configure>", self.on_canvas_resized)
# save the frame item ID
self.frame_id = self.canvas .create_window((0,0), window=self.frame, anchor="nw")
...
def on_canvas_resized(self, event):
w = max(event.width, self.frame.winfo_width())
h = max(event.height, self.frame.winfo_height())
# resize the frame
self.canvas.itemconfig(self.frame_id, width=w, height=h)
self.canvas.configure(scrollregion=self.canvas.bbox('all'))
...
initially I made a purchase with a traffic light, I want the colors to change alternately, but this does not work
may need to be divided into several classes?
please tell me what i'm doing wrong
from tkinter import Tk, Canvas, Frame, BOTH
import time # time.sleep(3)
class TrafficLight(Frame):
__canv_loc = [(10, 10, 80, 80), (10, 80, 80, 150), (10, 150, 80, 220)]
_color_off = 'grey0'
_color_on = ['red', 'yellow', 'green']
_time_on = [7, 2, 7]
def __init__(self):
super().__init__()
self.initUI()
def initUI(self): # заготовка светофора
(...)
def red_light(self):
self.master.title('Светофор')
self.pack(fill=BOTH, expand=1)
self.initUI()
canvas = Canvas(self)
canvas.create_oval(
self.__canv_loc[0], outline='grey0',
fill=self._color_on[0], width=2
)
canvas.pack(fill=BOTH, expand=1)
def running(self):
self.red_light()
def main():
root = Tk()
ex = TrafficLight()
root.geometry("330x350+300+300")
root.mainloop()
main()
tl = TrafficLight()
I am new to the Tkinter platform so please help me where I'm going wrong.
I have a floorplan image in which I want to cut the objects in it and place it on a canvas screen so that individual objects can be dragged if I want.
I'm able to cut and paste the objects on the screen except the first object. It is not placed on the screen. Can anyone help me?
I am using a Matlab code to identify the objects in the floorplan image. I am attaching the Matlab file.
Is it possible to add the wall to the screen? I have no idea at all. Can anyone suggest how to add the wall?
Here is my code
import tkinter as tk
from tkinter import *
from PIL import Image,ImageTk
from scipy.io import loadmat
root = tk.Tk()
canvas = tk.Canvas(width=800, height=800)
canvas.grid(row=4,column=0,sticky=(N,W,E,S))
#canvas.config(width=100,height=100)
root.grid_columnconfigure(0, weight=1)
root.grid_rowconfigure(4, weight=1)
mfile=loadmat('C:\\Users\\User\\Desktop\\tkinter_codes\\obj identification\\ans.mat')
#print(mfile.values())
#print(len(mfile['ans'][0]))
print(mfile.values())
class Example(tk.Frame):
def __init__(self, parent):
self.parent =parent
tk.Frame.__init__(self, parent)
self.canvas = tk.Canvas(width=800, height=800)
self.canvas.grid(row=0,column=0,sticky=(N,W,E,S))
#canvas.pack (expand =1, fill =tk.BOTH)
self.canvas.tag_bind("DnD","<Button-1>")
self._drag_data = {"x": 0, "y": 0, "item": None}
self.canvas.tag_bind("token", "<ButtonPress-1>", self.drag_start)
self.canvas.tag_bind("token", "<ButtonRelease-1>", self.drag_stop)
self.canvas.tag_bind("token", "<B1-Motion>", self.drag)
self.canvas.bind("<ButtonPress-1>", self.on_button_1)
self.iimg=Image.open("C:\\Users\\User\\Desktop\\tkinter_codes\\floorplans\\ROBIN\\Dataset_3rooms\\Cat1_1.jpg")
#iimg=iimg.resize((1000, 800), Image.ANTIALIAS)
self.canvas.img=ImageTk.PhotoImage(self.iimg)
#canvas.img = canvas.img.resize((250, 250), Image.ANTIALIAS)
self.canvas_img=canvas.create_image(0,0,image=self.canvas.img,anchor="nw")
self.mylist=[]
for x in range(len(mfile['ans'][0])):
#canvas.create_rectangle((mfile['ans'][0][x][0][0],mfile['ans'][0][x][0][1],mfile['ans'][0][x][0][0]+mfile['ans'][0][x][0][2],mfile['ans'][0][x][0][1]+mfile['ans'][0][x][0][3]),outline='red',tags=("token","DnD"))
self.im_crop = self.iimg.crop((mfile['ans'][0][x][0][0],mfile['ans'][0][x][0][1],mfile['ans'][0][x][0][0]+mfile['ans'][0][x][0][2],mfile['ans'][0][x][0][1]+mfile['ans'][0][x][0][3]))
self.canvas.im_crop2=ImageTk.PhotoImage(self.im_crop)
self.canvas.create_image(mfile['ans'][0][x][0][0],mfile['ans'][0][x][0][1], image=self.canvas.im_crop2)
#canvas.create_image(1000,1000,image=im_crop2)
#if(x>=0):
self.mylist.append(self.canvas.im_crop2)
#im_crop.show()
#canvas.iiiimg=ImageTk.PhotoImage(im_crop)
#canvas.create_image(150,150,image=canvas.im_crop2)
self.popup = tk.Menu(root, tearoff=0)
#self.popup.add_command(label="delete",command=lambda: self.dele(id))
self.popup.add_command(label="delete",
command=lambda: self.dele(id))
self.popup.add_command(label="add",command= lambda: root.bind("<Button-1>",self.addn))
root.bind("<Button-3>", self.do_popup)
self.canvas.delete(self.canvas_img)
def do_popup(self,event):
# display the popup menu
try:
self.popup.tk_popup(event.x_root, event.y_root, 0)
finally:
# make sure to release the grab (Tk 8.0a1 only)
self.popup.grab_release()
def on_button_1(self, event):
iid = self.canvas.find_enclosed(event.x-150, event.y-150, event.x + 150, event.y + 100)
#iid=canvas.find_closest(event.x, event.y)[0]
self.canvas.itemconfigure("DEL")
self.canvas.dtag("all","DEL")
self.canvas.itemconfigure(iid, tags=("DEL","DnD","token","drag"))
#canvas.unbind("<Button-1>")
def create_token(self, x, y, color):
"""Create a token at the given coordinate in the given color"""
self.canvas.create_rectangle(
x ,
y ,
x + 50,
y + 50,
outline=color,
fill=color,
tags=("token","DnD"),
)
def create_token1(self,x,y,color):
self.canvas.create_rectangle(
x ,
y ,
x + 25,
y + 25,
outline=color,
fill=color,
tags=("token","DnD"),
)
def drag_start(self, event):
"""Begining drag of an object"""
# record the item and its location
self._drag_data["item"] = self.canvas.find_closest(event.x, event.y)[0]
rect=self.canvas.bbox(self._drag_data["item"])
self.canvas.addtag_enclosed("drag",*rect)
print(rect)
self._drag_data["x"] = event.x
self._drag_data["y"] = event.y
def drag_stop(self, event):
"""End drag of an object"""
# reset the drag information
self._drag_data["item"] = None
self._drag_data["x"] = 0
self._drag_data["y"] = 0
self.canvas.dtag("drag","drag")
def drag(self, event):
"""Handle dragging of an object"""
# compute how much the mouse has moved
self.delta_x = event.x - self._drag_data["x"]
self.delta_y = event.y - self._drag_data["y"]
# move the object the appropriate amount
self.canvas.move("drag", self.delta_x, self.delta_y)
# record the new position
self._drag_data["x"] = event.x
self._drag_data["y"] = event.y
def dele(self,id):
#canvas.tag_bind(id,"<Button-1>")
self.canvas.delete("DEL")
def addn(self,event):
canvas.create_rectangle(event.x,event.y,event.x+25,event.y+25,fill='red',tags=("DnD","token"))
root.unbind("<Button-1>")
Example(root) #pack(fill="both", expand=True)
root.mainloop()
This is the Matlab code I am using for identifying objects
I'm creating a dynamic container that can change between frames. In one of the frames I have a list of values that I need to scroll through because it is so long. However I cannot get the scrollbar to work with the canvas and frame set up.
i have tried using Listbox and this works but does not give me the control over the displays that I am looking for. I want to be able to configure the names of the tag and then to the right the value.
#!Python
import matplotlib
matplotlib.use("TkAgg")
from matplotlib import style
import tkinter as tk
from tkinter import *
from YahooParser import Yahoo_Parser
TITLE_FONT = ("Helvetica", 10, "bold")
LARG_FONT = ("Helvetica", 12)
NORM_FONT = ("Helvetica", 10)
AIR_FONT = ("Arial", 10)
SMALL_FONT = ("Helvetica", 8)
style.use("ggplot")
#style.use("ggplot")
Gray = "#%02x%02x%02x" % (85, 85, 85)
Wight ="#%02x%02x%02x" % (220, 220, 220)
class Midas_Screen(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
# Title
tk.Tk.wm_title(self, "Midas Program")
tk.Tk.geometry(self,"500x400")
#tk.Tk.configure(self, background='black')
# This set the seting for the container
container = tk.Frame(self)
container.pack(side="top", fill="both", expand = True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
# Makes all the frames and stores them
for F in (HomePage,):
# You pass the container to your page function. this makes the frame
frame = F(container, self)
# this aligns the frame "nsew" = North, South etc
frame.grid(row=0, column=0, sticky="nsew")
# This uses the function as a key in the dic
self.frames[F] = frame
self.show_frame(HomePage)
def show_frame(self, key):
frame = self.frames[key]
frame.tkraise()
class HomePage(tk.Frame):
def __init__(self, perent, controller):
self.yp = Yahoo_Parser()
self.names = []
self.values = {}
for tag in self.yp.values_sum:
self.names.append(tag[0])
for tag in self.yp.values_sta:
self.names.append(tag[0])
for tag in self.names:
self.values[tag]='0'
tk.Frame.__init__(self, perent)
frame = Frame(perent)
frame.grid(row=1, column=1)
canvas = Canvas(frame)
canvas.configure(scrollregion=(0,0,500,500), width=200, height=200)
myscrollbar = Scrollbar(frame, orient="vertical")
myscrollbar.grid(row=1, column=2, sticky="ns")
myscrollbar.config(command=canvas.yview)
canvas.config(yscrollcommand=myscrollbar.set)
R = 1
for key in self.values:
label = tk.Label(canvas, text=key + ':', font=AIR_FONT, bg=Gray, fg=Wight, borderwidth=0,
relief="solid")
value = tk.Label(canvas, text=self.values[key], font=AIR_FONT, bg=Gray, fg=Wight, borderwidth=0,
relief="solid")
label.grid(row=R, column=1, sticky="nsew")
value.grid(row=R, column=2, sticky="nsew")
R += 1
canvas.grid(row=1, column=1)
app = Midas_Screen()
app.mainloop()
The canvas can't scroll things added to the canvas with grid. The most common solution is to add a frame to the canvas with create_window, and then adding the labels to the frame.
See Adding a scrollbar to a group of widgets in Tkinter
So i made a few changes thanks and got it working :)
Bye using pack as you sugeted it works
#!Python
import matplotlib
matplotlib.use("TkAgg")
from matplotlib import style
import tkinter as tk
from tkinter import *
from YahooParser import Yahoo_Parser
TITLE_FONT = ("Helvetica", 10, "bold")
LARG_FONT = ("Helvetica", 12)
NORM_FONT = ("Helvetica", 10)
AIR_FONT = ("Arial", 10)
SMALL_FONT = ("Helvetica", 8)
style.use("ggplot")
#style.use("ggplot")
Gray = "#%02x%02x%02x" % (85, 85, 85)
Wight ="#%02x%02x%02x" % (220, 220, 220)
class Midas_Screen(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
# Title
tk.Tk.wm_title(self, "Midas Program")
tk.Tk.geometry(self,"500x400")
#tk.Tk.configure(self, background='black')
# This set the seting for the container
container = tk.Frame(self)
container.pack(side="top", fill="both", expand = True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
# Makes all the frames and stores them
for F in (HomePage,):
# You pass the container to your page function. this makes the frame
frame = F(container, self)
# this aligns the frame "nsew" = North, South etc
frame.grid(row=1, column=1, sticky="nsew")
# This uses the function as a key in the dic
self.frames[F] = frame
self.show_frame(HomePage)
def show_frame(self, key):
frame = self.frames[key]
frame.tkraise()
class HomePage(tk.Frame):
def __init__(self, perent, controller):
self.values = {}
for tag in range(20):
self.values['Text'+str(tag)+':']='0'
tk.Frame.__init__(self, perent)
def myfunction(event):
canvas.configure(scrollregion=canvas.bbox("all"), width=200, height=200)
canvas = Canvas(perent)
frame = Frame(canvas)
myscrollbar = Scrollbar(frame, orient="vertical", command=canvas.yview)
canvas.config(yscrollcommand=myscrollbar.set)
myscrollbar.pack(side=RIGHT, fill=Y)# sticky="ns")
canvas.grid(row=0, column=0, sticky="nw")
#canvas.pack(side=LEFT)
canvas.create_window((0, 0), window=frame, anchor='nw')
R = 1
for key in self.values:
row = Frame(frame)
label = tk.Label(row, text=key + ':', font=AIR_FONT, bg=Gray, fg=Wight, borderwidth=0,
relief="solid")
value = tk.Label(row, text=self.values[key], font=AIR_FONT, bg=Gray, fg=Wight, borderwidth=0,
relief="solid")
label.pack(side=LEFT, fill=X)
value.pack(side=LEFT,fill=X)
row.pack(side=TOP, fill=X)
R += 1
frame.bind("<Configure>", myfunction)
app = Midas_Screen()
app.mainloop()
I have a main window with a grid layout
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(1850, 1700) # width, height
self.gl_main = QGridLayout(self.centralwidget)
w = ChartWidget(self)
self.gl_main.addWidget(w, 4, 0, 1, 1)
class ChartWidget(QWidget):
def __init__(self, parent=None):
super(ChartWidget, self).__init__(parent)
self.figure = plt.figure()
self.canvas = FigureCanvas(self.figure)
self.axes = self.figure.add_subplot(111)
self.axes.hold(False)
self.axes.plot(range(10), range(10), 'b')
self.canvas.draw()
self.show()
The chartwidget is not added to the gridlayout, though. I can briefly see it flash up on the screen, but then it is gone. how do I have to attach it?
adding the canvas to the gridlayout instead of the widget solved this.
w = ChartWidget(self)
self.gl_main.addWidget(w.canvas, 4, 0, 1, 1)