I try to make a line graph with python and the graph only appears a little in the end of the canvas in the GUI. The data that should be paper was taken from the database.
enter image description here
import sqlite3 ###----------------Connecting to the database-------------#####
DB = sqlite3.connect ("personal_project.db")
CURSOR = DB.cursor()
###----------------create the SQL command to create the table and save data-------------######
COMMAND1 = """CREATE TABLE IF NOT EXISTS
balance (
UserID INTEGER PRIMARY KEY,
Date TEXT,
Amount TEXT,
Descriotion)"""
CURSOR.execute(COMMAND1)
from tkinter import *
from tkinter import messagebox
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
###----------------Create the window-------------#####
main_WINDOW = Tk()
main_WINDOW.title("Study App")
main_WINDOW.geometry("1940x1080")#width*length
main_WINDOW.configure(bg="#ffffff")
###------Show Information Using Graph-------###
graquery = '''SELECT Date, Amount FROM balance'''
CURSOR.execute(graquery)
graresults = CURSOR.fetchall()
Date = [result[0] for result in graresults]
Amount = [result[1] for result in graresults]
figure = plt.figure()
plt.plot(Date, Amount)
plt.xlabel('Date')
plt.ylabel('Amount')
plt.title('Balance graph Graph')
gracanvas = Canvas(main_WINDOW, width=1070, height=452)
gracanvas.pack()
gracanvas.place(x=356, y=270)
figure_canvas = FigureCanvasTkAgg(figure, gracanvas)
gracanvas.create_window(0,0,window=figure_canvas.get_tk_widget())
Consider this code:
gracanvas.create_window(0,0,window=figure_canvas.get_tk_widget())
The create_window method by default centers the window at the given coordinate, which is what your image looks like. If you want the top-left corner of the window to be at 0,0, specify anchor="nw" so that the northwest corner of the window is at the given coordinate.
gracanvas.create_window(0,0,window=figure_canvas.get_tk_widget(), anchor="nw")
# ^^^^^^^^^^^
Related
I try to make a line graph with python and the graph only appears a half of the canvas and the rest is only black in the GUI. The data that should be appear was taken from the database.
###----------------Connecting to the database-------------#####
import sqlite3
DB = sqlite3.connect ("personal_project.db")
CURSOR = DB.cursor()
###----------------create the SQL command to create the table and save data-------------######
COMMAND1 = """CREATE TABLE IF NOT EXISTS
balance (
UserID INTEGER PRIMARY KEY,
Date TEXT,
Amount TEXT,
Descriotion)"""
CURSOR.execute(COMMAND1)
from tkinter import *
from tkinter import messagebox
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
###----------------Create the window-------------#####
main_WINDOW = Tk()
main_WINDOW.title("Study App")
main_WINDOW.geometry("1940x1080")#width*length
main_WINDOW.configure(bg="#ffffff")
###------Show Information Using Graph-------###
graquery = '''SELECT Date, Amount FROM balance'''
CURSOR.execute(graquery)
graresults = CURSOR.fetchall()
Date = [result[0] for result in graresults]
Amount = [result[1] for result in graresults]
figure = plt.figure()
plt.plot(Date, Amount)
plt.xlabel('Date')
plt.ylabel('Amount')
plt.title('Balance graph Graph')
gracanvas = Canvas(main_WINDOW, width=1070, height=452)
gracanvas.place(x=356, y=270)
figure_canvas = FigureCanvasTkAgg(figure, gracanvas)
gracanvas.create_window(0,0,window=figure_canvas.get_tk_widget(), anchor="nw")
main_WINDOW.mainloop()
There are plenty of web examples (1,2,3,4) and threads (1,2,3) about imbedding a plot into a tkinter window, but very few that address plotting in a separate environment and importing the resulting graph to the tkinter window.
In a nutshell, I have a program that calculates many different values, and exports those values to a separate file that creates a large number of plots. My tkinter application accepts parameters in Entry boxes, before applying them to the main file that does all the calculations. Typically, I would just follow the examples I linked, but with such a large number of plots being generated and the need to be able to select any particular graph I need at a given time, this would be inefficient and time consuming to brute-force. There must be a better way!
Below is a simplified example of how I am trying to accomplish this task:
import tkinter as tk
from matplotlib import pyplot as plt
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import (FigureCanvasTkAgg,
NavigationToolbar2Tk)
import numpy as np
def example_plot(A):
# Plot generated outside of tkinter environment, but controlled by
# variable within tkinter window.
x = np.linspace(0, 10, 50)
y = A*x**2
fig, ax = plt.subplots()
ax.plot(x,y)
ax.set_xlabel('x')
ax.set_ylabel('y')
return fig
window = tk.Tk()
window.geometry('256x256')
variableEntry = tk.Entry(width = 10)
variableLabel = tk.Label(window, text = "A")
variableEntry.grid(row = 0, column = 0)
variableLabel.grid(row = 0, column = 1)
def plotButton():
A = variableEntry.get()
A = int(A)
figure = Figure(figsize = (1,1), dpi = 128)
add = figure.add_subplot(1,1,1)
example = example_plot(A)
add.imshow(example)
canvas = FigureCanvasTkAgg(figure)
canvas.get_tk_widget().grid(row = 2, column = 0)
toolbar = NavigationToolbar2Tk(canvas)
toolbar.update()
canvas._tkcanvas.grid(row = 3 , column = 0)
canvas.show()
applyButton = tk.Button(master = window, text = "Apply", command = plotButton)
applyButton.grid(row = 1,column = 0)
window.mainloop()
When I run this, set A to some integer and press apply, I get an error
TypeError: Image data of dtype object cannot be converted to float
It seems that add.imshow() doesn't like that I fed it the figure. Is there some way to obtain the figure (ie: example = example_plot(A)) and store it to display later?
Try this:
import tkinter as tk
from matplotlib import pyplot as plt
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, \
NavigationToolbar2Tk
import numpy as np
def example_plot(A):
# Plot generated outside of tkinter environment, but controlled by
# variable within tkinter window.
x = np.linspace(0, 10, 50)
y = A*x*x # This should run slightly faster
fig, ax = plt.subplots()
ax.plot(x,y)
ax.set_xlabel("x")
ax.set_ylabel("y")
return fig
window = tk.Tk()
frame = tk.Frame(window)
frame.pack()
variable_entry = tk.Entry(frame, width=10)
variable_label = tk.Label(frame, text="A")
variable_entry.pack(side="left", fill="x")
variable_label.pack(side="left")
def plot():
A = int(variable_entry.get())
figure = Figure(figsize=(1, 1), dpi=128)
add = figure.add_subplot(1, 1, 1)
figure = example_plot(A)
canvas = FigureCanvasTkAgg(figure)
canvas.get_tk_widget().pack()
toolbar = NavigationToolbar2Tk(canvas, window)
toolbar.update()
canvas.get_tk_widget().pack()
# canvas.show() # There is no need for this
apply_button = tk.Button(window, text="Apply", command=plot)
apply_button.pack(fill="x")
window.mainloop()
Your example_plot returns a Figure so you can use figure = example_plot(A) and then FigureCanvasTkAgg(figure). I also added a frame and tried to make everything look better.
i have an issue that i want to query from combo box value selected from sqllite database and show in graph,here is my code.I will be appreciate any one could help me.
from tkinter import *
import tkinter as tk
from tkinter import ttk
from tkinter import filedialog
import pandas as pd
import sqlite3
from sqlalchemy import create_engine
from PyQt5 import QtCore, QtGui, QtWidgets
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import (FigureCanvasTkAgg,
NavigationToolbar2Tk)
def imprt_data():
import_file_path = filedialog.askopenfilename(filetypes=[("Excel files", "*.xlsx")],
title = "Choose a file")
df = pd.read_excel(import_file_path)
return df
def svetosql():
df=imprt_data()
engine = create_engine('sqlite:///save_pandas.db', echo=True)
sqlite_connection = engine.connect()
sqlite_table="Data"
df.to_sql(sqlite_table, sqlite_connection, if_exists='fail')
def sqlconection():
con=sqlite3.connect('save_pandas.db')
c = con.cursor()
return c
def callback_obj(event):
comboobject=event.widget.get()
return (comboobject)
def callback_rgn(event):
comboregion=event.widget.get()
return (comboregion)
root=tk.Tk()
root.title('Testing Tkinter Combobox With Sqllite')
root.geometry('500x500')
##---------Widget---------------
comboregion=ttk.Combobox(root,width=50 , height=20)
c=sqlconection()
for row in c.execute('SELECT DISTINCT Data.شهرستان from data '):
Query = [row[0] for row in c.fetchall()]
comboregion['values']=Query
comboregion.pack( ipadx=2, ipady=2 )
selectvalueregion=comboregion.bind("<<ComboboxSelected>>", callback_rgn)
#------------------------
comboobject=ttk.Combobox(root,width=50 , height=20)
c=sqlconection()
for row in c.execute('SELECT DISTINCT from Data '):
Query = [member[0] for member in c.description]
comboobject['values']=Query
comboobject.pack( ipadx=2, ipady=2 )
selectvalueobject=comboobject.bind("<<ComboboxSelected>>", callback_obj)
for row in c.execute('SELECT DISTINCT * from Data WHERE region LIKE ? ;' , (selectvalueobject,)):
Q = [row[0] for row in c.fetchall()]
print(Q)
#------------------------
f = Figure(figsize=(5,5), dpi=80)
a = f.add_subplot(111)
a.plot([1,2,3,4,5,6,7,8],[5,6,1,3,8,9,3,5])
canvas = FigureCanvasTkAgg(f, root)
canvas.draw()
canvas.get_tk_widget().pack(ipadx=2, ipady=2)
root.mainloop()
the graph has some data only for show,but i want to get data from combo box and then make query from database and after that show in graph.
if any one want to get database ,please leave comments.
I am trying to make an interactive plotting GUI using Tkinter and matplotlib (python 3.7 and matplotlib 3.0.0) I want the user to be able to resize the figure as it is displayed on the screen without resizing the window, and have achieved this by editing the dpi, width, and height properties of the figure. So far, this works, but if the figure is bigger than the display area, I want the user to be able to scroll to see the whole figure. And if the figure is smaller than the display area, I want the scrollbars to be disabled.
I have tried applying scrollbars directly to the FigureCanvasTkAgg object itself as well as embedding the FigureCanvasTkAgg canvas inside a second scrollable canvas, but it seems that the problem is that the drawable area of the FigureCanvasTkAgg widget doesn't change when the figure size changes. Minimal code reproducing the problem is below. Is there some property of the FigureCanvasTkAgg object that I'm missing that makes this work?
import tkinter as tk
from tkinter import ttk
from tkinter.simpledialog import askfloat
from matplotlib.figure import Figure
from matplotlib.axes import Axes
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
class InteractivePlot(tk.Frame):
def __init__(self,master,**kwargs):
super().__init__(master,**kwargs)
self._figure = Figure(dpi=150)
self._canvas = FigureCanvasTkAgg(self._figure, master=self)
self._sizebutton = tk.Button(self,text="Size (in.)", command=self._change_size)
self._axis = self._figure.add_subplot(111)
# Plot some data just to have something to look at.
self._axis.plot([0,1,2,3,4,5],[1,1,3,3,5,5],label='Dummy Data')
self._cwidg = self._canvas.get_tk_widget()
self._scrx = ttk.Scrollbar(self,orient="horizontal", command=self._cwidg.xview)
self._scry = ttk.Scrollbar(self,orient="vertical", command=self._cwidg.yview)
self._cwidg.configure(yscrollcommand=self._scry.set, xscrollcommand=self._scrx.set)
self._cwidg.bind(
"<Configure>",
lambda e: self._cwidg.configure(
scrollregion=self._cwidg.bbox("all")
)
)
self._sizebutton.grid(row=0,column=0,sticky='w')
self._cwidg. grid(row=1,column=0,sticky='news')
self._scrx. grid(row=2,column=0,sticky='ew')
self._scry. grid(row=1,column=1,sticky='ns')
self.rowconfigure(1,weight=1)
self.columnconfigure(0,weight=1)
self._canvas.draw()
def _change_size(self):
newsize = askfloat('Size','Input new size in inches')
if newsize is None:
return
w = newsize
h = newsize/1.8
self._figure.set_figwidth(w)
self._figure.set_figheight(h)
self._canvas.draw()
root = tk.Tk()
plt = InteractivePlot(root,width=400,height=400)
plt.pack(fill=tk.BOTH,expand=True)
root.mainloop()
The main issue here is that the matplotlib figure is meant to resize with self._cwidg. Because the figure is assumed to always be of the same size as self._cwidg, matplotlib only redraws the portion of the figure which is visible in self._cwidg and the latter is not resized when the figure size changes.
A work around is to use an extra canvas self._scroll_canvas and embed self._cwidg as a window inside it. Then I modified _change_size() in the following way:
def _change_size(self):
newsize = askfloat('Size', 'Input new size in inches')
if newsize is None:
return
w = newsize
h = newsize/1.8
self._cwidg.configure(width=int(w*self._conv_ratio), height=int(h*self._conv_ratio))
self._scroll_canvas.configure(scrollregion=self._scroll_canvas.bbox("all"))
I directly resize self._cwidg which in turns resizes the figure, ensuring that every part of it is redrawn. Then I update the scrollregion. Here is the full code:
import tkinter as tk
from tkinter import ttk
from tkinter.simpledialog import askfloat
from matplotlib.figure import Figure
from matplotlib.axes import Axes
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
class InteractivePlot(tk.Frame):
def __init__(self, master, **kwargs):
super().__init__(master, **kwargs)
self._scroll_canvas = tk.Canvas(self)
self._figure = Figure(dpi=150)
self._canvas = FigureCanvasTkAgg(self._figure, master=self._scroll_canvas)
self._sizebutton = tk.Button(self, text="Size (in.)", command=self._change_size)
self._axis = self._figure.add_subplot(111)
# Plot some data just to have something to look at.
self._axis.plot([0, 1, 2, 3, 4, 5], [1, 1, 3, 3, 5, 5], label='Dummy Data')
self._cwidg = self._canvas.get_tk_widget()
self._scroll_canvas.create_window(0, 0, anchor='nw', window=self._cwidg)
self._scrx = ttk.Scrollbar(self, orient="horizontal", command=self._scroll_canvas.xview)
self._scry = ttk.Scrollbar(self, orient="vertical", command=self._scroll_canvas.yview)
self._scroll_canvas.configure(yscrollcommand=self._scry.set, xscrollcommand=self._scrx.set)
self._sizebutton.grid(row=0, column=0, sticky='w')
self._scroll_canvas.grid(row=1, column=0, sticky='news')
self._scrx.grid(row=2, column=0, sticky='ew')
self._scry.grid(row=1, column=1, sticky='ns')
self.rowconfigure(1, weight=1)
self.columnconfigure(0, weight=1)
self._canvas.draw()
wi = self._figure.get_figwidth()
wp = self._cwidg.winfo_reqwidth(),
self._conv_ratio = wp / wi # get inch to pixel conversion factor
self._scroll_canvas.configure(width=wp, height=self._cwidg.winfo_reqheight())
self._scroll_canvas.configure(scrollregion=self._scroll_canvas.bbox("all"))
def _change_size(self):
newsize = askfloat('Size', 'Input new size in inches')
if newsize is None:
return
w = newsize
h = newsize/1.8
self._cwidg.configure(width=int(w*self._conv_ratio), height=int(h*self._conv_ratio))
self._scroll_canvas.configure(scrollregion=self._scroll_canvas.bbox("all"))
root = tk.Tk()
plt = InteractivePlot(root, width=400, height=400)
plt.pack(fill=tk.BOTH, expand=True)
root.mainloop()
Thanks to j_4321's answer, and after digging around in the source code for the FigureCanvasTk object, I came up with the following solution that does everything I need it to.
It looks like what the canvas object does is use tkinter.Canvas.create_image to generate a new image of the figure every time the canvas widget is resized, keeping the DPI of the figure constant and setting the width and height of the figure to keep it the same size as the canvas widget. Since I want the canvas widget to resize to the figure, I calculate the width and height from the figure properties and then pass a custom Event object to FigureCanvasTk.resize, the callback function for the canvas widget's <Configure> event.
The last trick is to set the scrollregion of the canvas to be only the size of the last canvas item created. It looks like previous iterations of the figure are not deleted from the canvas (seems like a memory leak?), so if you try to set the scrollregion to Canvas.bbox('all'), it will set the scrollregion to the size of the largest version of the figure, not the current version of the figure.
Here is the full example code:
import tkinter as tk
from tkinter import ttk
from tkinter.simpledialog import askfloat
from matplotlib.figure import Figure
from matplotlib.axes import Axes
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
class InteractivePlot(tk.Frame):
def __init__(self,master,**kwargs):
super().__init__(master,**kwargs)
self._figure = Figure(dpi=150)
self._canvas = FigureCanvasTkAgg(self._figure, master=self)
buttonframe = tk.Frame(self)
self._sizebutton = tk.Button(buttonframe,text="Size (in.)", command=self._change_size)
self._dpibutton = tk.Button(buttonframe,text="DPI", command=self._change_dpi)
self._axis = self._figure.add_subplot(111)
# Plot some data just to have something to look at.
self._axis.plot([0,1,2,3,4,5],[1,1,3,3,5,5],label='Dummy Data')
self._cwidg = self._canvas.get_tk_widget()
self._scrx = ttk.Scrollbar(self,orient="horizontal", command=self._cwidg.xview)
self._scry = ttk.Scrollbar(self,orient="vertical", command=self._cwidg.yview)
self._cwidg.configure(yscrollcommand=self._scry.set, xscrollcommand=self._scrx.set)
self._cwidg.bind("<Configure>",self._refresh)
self._sizebutton.grid(row=0,column=0,sticky='w')
self._dpibutton.grid(row=0,column=1,sticky='w')
buttonframe.grid(row=0,column=0,columnspan=2,sticky='W')
self._cwidg. grid(row=1,column=0,sticky='news')
self._scrx. grid(row=2,column=0,sticky='ew')
self._scry. grid(row=1,column=1,sticky='ns')
self.rowconfigure(1,weight=1)
self.columnconfigure(0,weight=1)
# Refresh the canvas to show the new plot
self._canvas.draw()
# Figure size change button callback
def _change_size(self):
newsize = askfloat('Size','Input new size in inches')
if newsize is None:
return
w = newsize
h = newsize/1.8
self._figure.set_figwidth(w)
self._figure.set_figheight(h)
self._refresh()
# Figure DPI change button callback
def _change_dpi(self):
newdpi = askfloat('DPI', 'Input a new DPI for the figure')
if newdpi is None:
return
self._figure.set_dpi(newdpi)
self._refresh()
# Refresh function to make the figure canvas widget display the entire figure
def _refresh(self,event=None):
# Get the width and height of the *figure* in pixels
w = self._figure.get_figwidth()*self._figure.get_dpi()
h = self._figure.get_figheight()*self._figure.get_dpi()
# Generate a blank tkinter Event object
evnt = tk.Event()
# Set the "width" and "height" values of the event
evnt.width = w
evnt.height = h
# Set the width and height of the canvas widget
self._cwidg.configure(width=w,height=h)
self._cwidg.update_idletasks()
# Pass the generated event object to the FigureCanvasTk.resize() function
self._canvas.resize(evnt)
# Set the scroll region to *only* the area of the last canvas item created.
# Otherwise, the scrollregion will always be the size of the largest iteration
# of the figure.
self._cwidg.configure(scrollregion=self._cwidg.bbox(self._cwidg.find_all()[-1]))
root = tk.Tk()
plt = InteractivePlot(root,width=400,height=400)
plt.pack(fill=tk.BOTH,expand=True)
root.mainloop()
I am trying to design a python gui to be able to assess impacts of motion by plotting brain slices, the framewise displacement timeseries, and different outputs of motion detection algorithms. I want to be able to slide through each of the brain volumes individually (180 volumes per scan) so that I can compare the FD timecourse to what the actual brain data looks like.
I've been using tkinter and I can plot several slices of one brain volume, but I'm having updating volume that is selected. I've tried creating buttons to advance and go back, and also using a tkinter Scale.
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
from matplotlib.figure import Figure
from matplotlib import pyplot as plt
import os
import nibabel
from nilearn import plotting
from nilearn import image
from matplotlib.widgets import Slider, Button, RadioButtons
data_path = os.getcwd()
file='sub-HV01baseline_task-EA1_bold.nii'
file_path = os.path.join(data_path,file)
EA1=nibabel.load(file_path)
struct_arr2 = EA1.get_data()
vol=1
import tkinter as tk
from tkinter import ttk
fig = plt.Figure(figsize=(10,5), dpi=100)
class App:
def __init__(self, master):
self.event_num = 1
frame = tk.Frame(master)
frame.pack()
self.txt = tk.Entry(frame,width=10)
self.txt.pack(side="bottom")
self.lbl = tk.Label(frame, text="FD Value")
self.lbl.pack(side="bottom")
self.btn = tk.Button(frame, text = "Update",command=self.clicked)
self.btn.pack(side="bottom")
self.txt.focus()
self.var =tk.IntVar(frame)
self.var.set(0)
self.vol_scale=tk.Scale(frame,from_=0, to=180,orient="horizontal",sliderlength=20,command=self.show_slices(fig))
self.increase_btn = tk.Button(frame, text = "Increase",command=self.show_slices(fig))
self.increase_btn.pack(side="bottom")
self.vol_scale.pack(side="bottom")
#self.spin = tk.Spinbox(frame, from_=0, to=180, width=5, textvariable=self.var)
#self.spin.pack(side="bottom")
self.canvas = FigureCanvasTkAgg(fig,master=master)
self.canvas.get_tk_widget().pack(side=tk.TOP)
def clicked(self):
res = "FD = " + self.txt.get()
self.lbl.configure(text = res)
def show_slices(self,fig):
vol = self.vol_scale.get()
slice_0 = struct_arr2[:, :, 10,vol]
slice_1 = struct_arr2[:, : , 15,vol]
slice_2 = struct_arr2[:, :, 20,vol]
slice_3 = struct_arr2[:, :, 25,vol]
slice_4 = struct_arr2[:, : , 30,vol]
slices=[slice_0, slice_1, slice_2, slice_3, slice_4]
axes = fig.subplots(1, len(slices))
#svol = Slider(axes, 'Vol', 0, 180, valinit=0, valstep=1)
fig.subplots_adjust(hspace=0, wspace=0)
for i, slice in enumerate(slices):
axes[i].xaxis.set_major_locator(plt.NullLocator())
axes[i].yaxis.set_major_locator(plt.NullLocator())
axes[i].imshow(slice.T, origin="lower")
root=tk.Tk()
app = App(root)
root.mainloop()
Currently I'm getting an error that "App has no attribute 'vol_scale'" even though I've defined it above.