Displaying plot in Tkinter GUI frame - python-3.x

I want to show the plot canvas in the top right side of UI (using Tkinter python3). I wrote the following code but I got the following error:
canvas = FigureCanvasTkAgg(fig, master=root) NameError: name 'root' is not defined
My code is:
import tkinter as tk
import tkinter.ttk as ttk
from tkinter import *
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
from matplotlib.figure import Figure
import sys
class GUI(tk.Frame):
def __init__(self, master = None):
self.root = tk.Tk()
self.root.geometry("500x500")
tk.Frame.__init__(self, master)
self.createWidgets()
def start(self):
self.root.mainloop()
def createWidgets(self):
fig = plt.figure(figsize=(8, 8))
canvas = FigureCanvasTkAgg(fig, master=root)
canvas.get_tk_widget().grid(row=0, column=1)
canvas.show()
def main():
appstart = GUI()
appstart.start()
if __name__ == "__main__":
main()

I made some changes to your code, deleting some unnecessary
references such as:
import tkinter.ttk as ttk
from tkinter import *
from matplotlib.figure import Figure
import sys
self.root = tk.Tk()
tk.Frame.__init__(self, master)
that you dont' use and I think only confuse yourself.
I've even simplified your script and add this code below to show to you wath is "self"
print(type(self))
for item in dir(self):
print(type(item),item)
I've add even a toolbar to the plot and some data to plot something.
Regards
import tkinter as tk
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
import numpy as np
class GUI(tk.Tk):
def __init__(self):
super().__init__()
self.title("Hello World")
self.geometry("500x500")
self.createWidgets()
print(type(self))
for item in dir(self):
print(type(item),item)
def createWidgets(self):
t = np.arange(0, 3, .01)
f0 = tk.Frame()
fig = plt.figure(figsize=(8, 8))
fig.add_subplot(111).plot(t, 2 * np.sin(2 * np.pi * t))
canvas = FigureCanvasTkAgg(fig, f0)
toolbar = NavigationToolbar2Tk(canvas, f0)
toolbar.update()
canvas._tkcanvas.pack(fill=tk.BOTH, expand=1)
f0.pack(fill=tk.BOTH, expand=1)
def main():
appstart = GUI()
appstart.mainloop()
if __name__ == "__main__":
main()

In your code you should use self.root if your intent is to use the root window:
canvas = FigureCanvasTkAgg(fig, master=self.root)
... or maybe just self, if your intent is to have it appear inside the frame. I'm not entirely sure what you are intending to do
canvas = FigureCanvasTkAgg(fig, master=self)
Somewhat unrelated, if you're explicitly creating a root window, you should be passing that to tk.Frame.__init__ rather than master.
tk.Frame.__init__(self, self.root)

Related

Tkinter Window too small

My kinter window is coming to small even after using geometry.
What to do so that it doesn't shrink?
Here's my code. And I can see the minimize button is coming towards left of the window. but I don't know ... why does this happen? I want to achieve oop for tkinter.
from logging import root
from tkinter import *
import tkinter as tk
from tkinter import messagebox
import mysql.connector
from tkinter import ttk
# from com.acc.report.database import
class Main(tk.Tk):
def __init__(self):
super().__init__()
# if not tasks:
# self.tasks = []
# instance of tkinter frame, i.e., Tk()
# root = Tk()
style = ttk.Style()
style.map("C.TButton",
foreground=[('pressed', 'red'), ('active', 'blue')],
background=[('pressed', '!disabled', 'black'), ('active', 'white')]
)
# width = self.winfo_screenwidth()
# height = self.winfo_screenheight()
self.geometry("626x431")
def selectReports():
messagebox.showinfo("EDP", "All reports")
def showReports():
messagebox.showinfo("EDP", "Select reports")
#Report Display Window
# def reviewReport():
# win = Toplevel(root)
# win.geometry("626x431")
monitor = ttk.Button(name="",text="Monitor",command=showReports,style="C.TButton")
review = ttk.Button(name="",text="Review",command=selectReports,style="C.TButton")
monitor.pack(pady=100)
review.pack(pady=0)
# Main method
if __name__ == "__main__":
objectMain = Main()
objectMain.withdraw()
objectMain.mainloop()
Make showReports and selectReports class methods, comment out or remove objectMain.withdraw() and everything should work.
from logging import root
from tkinter import *
import tkinter as tk
from tkinter import messagebox
#import mysql.connector
from tkinter import ttk
# from com.acc.report.database import
class Main(tk.Tk):
def __init__(self):
super().__init__()
self.geometry("626x431")
style = ttk.Style()
style.map("C.TButton",
foreground=[('pressed', 'red'), ('active', 'blue')],
background=[('pressed', '!disabled', 'black'), ('active', 'white')]
)
monitor = ttk.Button(name="",text="Monitor",command=self.showReports,style="C.TButton")
review = ttk.Button(name="",text="Review",command=self.selectReports,style="C.TButton")
monitor.pack(pady=100)
review.pack(pady=0)
def selectReports(self):
messagebox.showinfo("EDP", "All reports")
def showReports(self):
messagebox.showinfo("EDP", "Select reports")
# Main method
if __name__ == "__main__":
objectMain = Main()
# objectMain.withdraw()
objectMain.mainloop()

QObject: Cannot create children for a parent that is in a different thread. (Multithreading pyqt5)

I am trying to edit the text in 'QTextEdit' but I got the error which is in the title above. I know that I shouldn't edit the widgets in the main app from another thread and I searched for some answers, But I didn't get the full answer that I want
This is a sample that produce the same error that I have
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5 import QtCore
import sys
import time
import threading
class Main(QMainWindow):
def __init__(self):
super().__init__()
self.move(50, 220)
self.resize(500, 500)
self.text_edit = QTextEdit(self)
self.text_edit.resize(200, 200)
self.btn = QPushButton('start', self)
self.btn.move(10, 250)
self.btn.clicked.connect(lambda x : self.thread_creator(self.test))
self.btn2 = QPushButton('print', self)
self.btn2.move(10, 290)
self.btn2.clicked.connect(lambda x : print('dad'))
def test(self):
for i in range(99):
self.text_edit.setText(f'dad {i}')
time.sleep(0.1)
def thread_creator(self, function):
self.thread = threading.Thread(target=function)
self.thread.start()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Main()
ex.show()
sys.exit(app.exec_())

python tk tkinter multiple widgets is out of my scrollable frame

import tkinter as tk
from tkinter import filedialog
from matplotlib.backends.backend_tkagg import (FigureCanvasTkAgg, NavigationToolbar2Tk)
from matplotlib.figure import Figure
import matplotlib.pyplot as plt
from data import dataprocess
root = tk.Tk()
root.geometry('1500x800')
container = tk.Frame(root)
main_canvas = tk.Canvas(container, width=1480, height=800, bg='blue')
scrollbar = tk.Scrollbar(container, orient="vertical", command=main_canvas.yview)
scrollable_frame = tk.Frame(main_canvas, bg='black')
scrollable_frame.bind(
"<Configure>",
lambda e: main_canvas.configure(
scrollregion=main_canvas.bbox("all")
)
)
main_canvas.create_window((0, 0), window=scrollable_frame, anchor="nw")
main_canvas.configure(yscrollcommand=scrollbar.set)
for i in range(1400):
btn = tk.Button(scrollable_frame, text='btn' + str(i)).grid(row=i, column=0)
container.grid()
main_canvas.grid()
scrollbar.grid(row=0, column=7, sticky="nse")
[enter image description here][1]container.mainloop()
My buttons can not show inside the frame, seems like my frame can not afford that much widgets.
Is there any method to fix it?
[1]: https://i.stack.imgur.com/qgZGh.png

real-time plotting to a TK window using python3

I am trying to plot in real-time to a tkinter window in python3.
I am attempting to wrap my window in a class.
my code shows the graph, but data is not being plotted. Here is the code:
#! /usr/bin/python3
# -*- coding: utf-8 -*-
import sys
import matplotlib
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
import matplotlib.animation as animation
import tkinter
import time
import numpy as np
matplotlib.use("TkAgg")
class Window(tkinter.Frame):
def __init__(self, master = None):
tkinter.Frame.__init__(self, master)
self.master = master
self.master.grid()
self.fig = Figure(figsize=(5,5), dpi=100)
self.ax = self.fig.add_subplot(111)
self.t, self.y = [], []
self.init_window()
self.ani = animation.FuncAnimation(self.fig, self.animate, interval=1000)
def init_window(self):
# set window title
self.master.title("PlotSomething")
# run show_graph
self.show_graph()
def animate(self):
self.t.append(time.time())
self.y.append(np.random.random())
self.ax.clear()
self.ax.plot(self.t,self.y)
def show_graph(self):
canvas = FigureCanvasTkAgg(self.fig, self.master)
canvas.get_tk_widget().grid(row=1, column=1)
canvas.draw()
def client_exit(self):
sys.exit()
def main(args):
root = tkinter.Tk()
app = Window(root)
root.mainloop()
return 0
if __name__=="__main__":
sys.exit(main(sys.argv[1:]))
Please let me know what the issue is.
Bonus if you can help me get the real-time plot stuff into a separate class that I can call with my tkinter window.
I obviously need some learning on how to create and call classes using tkinter.
As always thank you for your support!

How to display multicursor on a QTabWidget?

The multicursor example
The question is : If I want the plot to be displayed on a tab of the QTabWidget,how to make the MultiCursor works?
# -*- coding: utf-8 -*-
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
import matplotlib
matplotlib.use('Qt5Agg')
import matplotlib.pyplot as plt
import numpy as np
import sys
from matplotlib.gridspec import GridSpec
from matplotlib.widgets import MultiCursor
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
class MainWindow(QMainWindow):
def __init__(self):
super().__init__(flags=Qt.Window)
self.setFont(QFont("Microsoft YaHei", 10, QFont.Normal))
self.setMinimumSize(1550, 950)
self.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
centralwidget = QWidget(flags=Qt.Widget)
centralwidget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
self.setCentralWidget(centralwidget)
self.tabview = QTabWidget()
self.tabview.currentChanged.connect(self.onchange)
self.chart_widget0 = QWidget()
self.chart_widget1 = QWidget()
self.dc0 = my_Canvas(self.chart_widget0, width=20, height=8, dpi=100)
self.dc1 = my_Canvas(self.chart_widget1, width=20, height=8, dpi=100)
self.tabview.addTab(self.dc0, "MultiCursor")
self.tabview.addTab(self.dc1, "Cursor")
toplayout = QHBoxLayout()
toplayout.addWidget(self.tabview)
centralwidget.setLayout(toplayout)
def onchange(self,i):
if i == 0:
self.dc0.update_figure()
elif i == 1:
self.dc1.update_figure()
class my_Canvas(FigureCanvas):
def __init__(self, parent=None, width=10, height=7, dpi=100):
self.fig = plt.figure(figsize=(width, height), dpi=dpi)
gs = GridSpec(2, 1, height_ratios=[3, 1])
self.axes1 = plt.subplot(gs[0])
self.axes2 = plt.subplot(gs[1])
self.compute_initial_figure()
FigureCanvas.__init__(self, self.fig)
self.setParent(parent)
def compute_initial_figure(self):
self.axes1.cla()
self.axes2.cla()
def update_figure(self):
t = np.arange(0.0, 2.0, 0.01)
s1 = np.sin(2*np.pi*t)
s2 = np.sin(4*np.pi*t)
self.axes1.plot(t, s1)
self.axes2.plot(t, s2)
multi = MultiCursor(self.fig.canvas, (self.axes1, self.axes2), color='r', lw=1)
self.draw()
if __name__ == '__main__':
app = QApplication(sys.argv)
w1 = MainWindow()
w1.show()
sys.exit(app.exec_())
How to modify the code to make the MultiCursor works, and could I control the display of the cursor by key or mousebutton click?
Further more, how to display the coordinate with the cursor?
As the Multicursor documentation tells us,
For the cursor to remain responsive you must keep a reference to it.
The easiest way is to make it a class variable,
self.multi = MultiCursor(...)

Resources