tkinter menubar disappears when matplotlib figure opens - python

I created a tkinter frame with menus. The frame contains a button that generates a matplotlib figure window. When the figure window opens, all of the menus for the frame disappear (at least on a mac).
A minimal code example is shown below.
Any ideas would be much appreciated. Thanks!
#!/usr/bin/python
import matplotlib.pyplot as plt
import sys
if sys.version_info[0] < 3:
import Tkinter as tk
else:
import tkinter as tk
class a_window_with_a_menubar:
def __init__(self):
self.root = tk.Tk()
self.new_fig_button = tk.Button(master = self.root, text="Make figure", command = self.make_figure)
self.new_fig_button.grid(row = 0, column = 0, sticky = tk.W)
def make_figure(self):
self.fig1 = plt.figure(facecolor = 'white')
self.ax1 = self.fig1.add_subplot(111)
self.ax1.cla()
plt.show()
win = a_window_with_a_menubar()
tk.mainloop()
Note: the menus disappear whether I use the default tkinter menus or create my own.

Related

How to display 2d list as a table in tkinter on a new window?

I am trying to display a 2d list as CSV like structure in a new tkinter window
here is my code
import tkinter as tk
import requests
import pandas as pd
import numpy as np
from tkinter import messagebox
from finta import TA
from math import exp
import xlsxwriter
from tkinter import *
from tkinter.ttk import *
import yfinance as yf
import csv
from operator import itemgetter
def Createtable(root,rows):
# code for creating table
newWindow = Toplevel(root)
newWindow.title("New Window")
# sets the geometry of toplevel
newWindow.geometry("600x600")
for i in range(len(rows)):
for j in range(3):
self.e = Entry(root, width=20, fg='blue',
font=('Arial',16,'bold'))
self.e.grid(row=i, column=j)
self.e.insert(END, rows[i][j])
Label(newWindow, text ="Results").pack()
This is my code for display the 2d list in a new window, here rows is the 2d list
This is how I am calling the function
def clicked():
selected_stocks=[]
converted_tickers=[]
selected = box.curselection()
for idx in selected:
selected_stocks.append(box.get(idx))
for i in selected_stocks:
converted_tickers.append(name_to_ticker[i])
rows=compute(converted_tickers)
Createtable(app,rows)
btn = Button(app, text = 'Submit',command = clicked)
btn.pack(side = 'bottom')
rows works, I printed it out seperately and confirmed.
When I execute the program this is the error I receive
Exception in Tkinter callback
Traceback (most recent call last):
File "/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/tkinter/__init__.py", line 1883, in __call__
return self.func(*args)
File "pair.py", line 144, in clicked
Createtable(app,rows)
File "pair.py", line 31, in Createtable
self.e = Entry(root, width=20, fg='blue',
File "/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/tkinter/ttk.py", line 669, in __init__
Widget.__init__(self, master, widget or "ttk::entry", kw)
File "/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/tkinter/ttk.py", line 557, in __init__
tkinter.Widget.__init__(self, master, widgetname, kw=kw)
File "/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/tkinter/__init__.py", line 2567, in __init__
self.tk.call(
_tkinter.TclError: unknown option "-fg"
I looked up the error and it says that this happens when you are using both old and new version of tkinter. I am not able to understand this as I am not using tk and ttk seperately anywhere in the Createtable function.
How can I solve this issue to display the 2d list on the new window?
For a reference I am attaching a screenshot here
UPDATE: Updated code as per comment
1)I commented out from tkinter.ttk import *
2)I also changed the Createtable function into a class like this
class Table:
def __init__(self,root,rows):
# code for creating table
newWindow = Toplevel(root)
newWindow.title("New Window")
# sets the geometry of toplevel
newWindow.geometry("600x600")
for i in range(len(rows)):
for j in range(3):
self.e=Entry(root, width=20, fg='blue',
font=('Arial',16,'bold'))
self.e.grid(row=i, column=j)
self.e.insert(END, rows[i][j])
Label(newWindow, text ="Results").pack()
Now, two errors are happening
1.The 2d list is showing on top of the main window instead of the new window.
2.After removing ttk the words on top of the button have become white for some reason. You can see in the picture attached below ( compare with the old picture, you will see "Submit" has become white)
How do I solve these?
Attaching picture for reference
UPDATE 2: minimum reproducible code
import tkinter as tk
import requests
import pandas as pd
import numpy as np
from tkinter import messagebox
from finta import TA
from math import exp
import xlsxwriter
from tkinter import *
import tkinter.ttk as ttk
import yfinance as yf
import csv
from operator import itemgetter
class Table:
def __init__(self,root,rows):
# code for creating table
newWindow = Toplevel(root)
newWindow.title("New Window")
# sets the geometry of toplevel
newWindow.geometry("600x600")
for i in range(len(rows)):
for j in range(3):
self.e=Entry(newWindow)
self.e.grid(row=i, column=j)
self.e.insert(END, rows[i][j])
Label(newWindow, text ="Results").pack()
#######################LIST TO DISPLAY##############
final_sorted_list=['Allahabad Bank', 'Andhra Bank', 'Axis Bank Limited','Bank of Baroda','Bank of India Limited']
########TKINTER##############################
app = Tk()
app.title('Test')
app.geometry("500x800")
def clicked():
rows=[['Stock 1', 'Stock 2', 'Value'], ['AXISBANK.NS', 'MAHABANK.NS', 81.10000000000001], ['AXISBANK.NS', 'BANKINDIA.NS', 82.3], ['BANKBARODA.NS', 'MAHABANK.NS', 84.8], ['MAHABANK.NS', 'CANBK.NS', 85.5], ['BANKBARODA.NS', 'BANKINDIA.NS', 90.4], ['BANKINDIA.NS', 'CANBK.NS', 90.9], ['AXISBANK.NS', 'CANBK.NS', 91.5], ['AXISBANK.NS', 'BANKBARODA.NS', 93.30000000000001], ['BANKINDIA.NS', 'MAHABANK.NS', 95.8], ['BANKBARODA.NS', 'CANBK.NS', 97.6]]
Table(app,rows)
print("Finished")
box = Listbox(app, selectmode=MULTIPLE, height=4)
for val in final_sorted_list:
box.insert(tk.END, val)
box.pack(padx=5,pady=8,side=LEFT,fill=BOTH,expand=True)
scrollbar = Scrollbar(app)
scrollbar.pack(side = RIGHT, fill = BOTH)
box.config(yscrollcommand = scrollbar.set)
# scrollbar.config(command = box.yview)
btn = ttk.Button(app, text = 'Submit',command = clicked)
btn.pack(side = 'bottom')
exit_button = ttk.Button(app, text='Close', command=app.destroy)
exit_button.pack(side='top')
clear_button = ttk.Button(app, text='Clear Selection',command=lambda: box.selection_clear(0, 'end'))
clear_button.pack(side='top')
app.mainloop()
If you run this, you get a frontend like in the picture above. You don't need to select anything as a sample result already has been hard coded in rows. The data in rows needs to be displayed in another window as a table. To recreate the error (new window not popping up) - you can just click on the "Submit" button, you don't need to select anything from the list.
The problem update 2 is that you are using pack and grid in the same window. When I click submit the new window comes up (I couldn't reproduce that problem) but the word "results" is missing. This is because you are trying to use pack and grid on the same window. To fix this, show "results" using grid like this:
class Table:
def __init__(self,root,rows):
# code for creating table
newWindow = Toplevel(root)
newWindow.title("New Window")
# sets the geometry of toplevel
newWindow.geometry("600x600")
Label(newWindow, text ="Results").grid(row = 0, column = 0, columnspan = 3)
for i in range(len(rows)):
for j in range(3):
self.e=Entry(newWindow)
self.e.grid(row=i+1, column=j)
self.e.insert(END, rows[i][j])
Because the "results" label is now in column 1 I've had to change self.e.grid to use row=i+1 so it displays properly. I also set the columnspan of the "results" label to 3 so it will be centered.
I've organized your imports. This should remove naming conflicts.
Note: You will have to declare tkinter objects with tk. and ttk objects with ttk. This should also help to remove naming conflicts.
import tkinter as tk
from tkinter import ttk
from tkinter import messagebox
import requests
import csv
import pandas as pd
import numpy as np
import xlsxwriter
import yfinance as yf
from finta import TA
from math import exp
from operator import itemgetter

Behavior of passed subplot reference different in direct reference and as list

I am trying to write a function to update a matplotlib chart following a combobox selection. I don't understand a difference in behavior. In the full program and in the streamlined version below, I create a subplot "a" and can pass it to a function ("tester") and it will modify the subplots attributes as expected. But if I pass the subplot as an entry in a lambda function to a Comboboxselected entry, it does not work. Why the difference?
When the following is run, the x-axis of the plot reads "XAXIS RESET BY TESTER", as expected given the call to tester(). When a new combobox entry is selected, update_chart() prints the choice in the combobox correctly, prints the accompanying test parameter "ZERO" correctly, but the x-axis on the chart does not change. Why the difference in behaviors?
The following stand-alone code reproduces the problem:
# -*- coding: utf-8 -*-
from tkinter import ttk
import tkinter as tk
from tkinter.scrolledtext import ScrolledText
import matplotlib
matplotlib.use("TkAgg")
from matplotlib.backends.backend_tkagg import (FigureCanvasTkAgg,
NavigationToolbar2TkAgg)
from matplotlib.figure import Figure
def make_chart_page(nb, chartPage):
labelFrameChart = tk.LabelFrame(chartPage, text="Chart control:")
labelFrameChart.pack(side="left", fill="y", padx=5, pady=5)
comboChart = ttk.Combobox(labelFrameChart, values=["Foos", "Bars", "Widgets"], width=25)
comboChart.pack(anchor="w", padx=5)
f = Figure(figsize=(7,5), dpi=100)
a = f.add_subplot(111)
canvas = FigureCanvasTkAgg(f, chartPage)
canvas.show()
canvas.get_tk_widget().pack(side=tk.BOTTOM, fill=tk.BOTH, expand=True)
canvas._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand=True)
data = [ "ZERO", a ]
comboChart.bind("<<ComboboxSelected>>", lambda event, args=data: callback(event, args))
tester(a)
def tester(a):
a.set_xlabel("XAXIS RESET BY TESTER")
def update_chart(combo, var0, chart):
print ("Combobox choice is: ", combo)
print ("Args[0] is: ", var0)
chart.set_xlabel("Xaxis reset by Update_Chart")
def callback(eventObject, args):
update_chart(eventObject.widget.get(), args[0], args[1])
def demo():
root = tk.Tk()
nb = ttk.Notebook(root)
chartPage = ttk.Frame(nb)
make_chart_page(nb, chartPage)
nb.add(chartPage, text='Charts')
nb.pack(expand=1, fill="both")
root.mainloop()
if __name__ == "__main__":
demo()
Thank you,
Randy
You simply need to redraw the canvas again by calling canvas.draw_idle().
def make_chart_page(nb, chartPage):
...
global canvas
canvas = FigureCanvasTkAgg(f, chartPage)
...
def update_chart(combo, var0, chart):
print("Combobox choice is: ", combo)
print("Args[0] is: ", var0)
chart.set_xlabel("Xaxis reset by Update_Chart")
canvas.draw_idle()

How to uncheck a checkbox after the close_event of figure in Python

This is my first question, so please let me know if I make any faux pas.
I'd like to have a check-box that creates and destroys a plot. If that plot is closed using the window's "x", then I'd like the check-box to be unchecked.
I've got it all working except the unchecking upon figure closing.
It looks like my close function is getting called, but it crashes when I try to manipulate the check-box.
from tkFont import *
from Tkinter import *
import matplotlib.pyplot as plt
import numpy as np
class Application(Frame):
def velocity_cb(self):
if self.velocity_var.get()==1:
print "selected Velocity checkbox"
x = np.arange(100.)
self.figureHandles[1] = plt.figure("Velocity")
# set the close event to run my function
self.figureHandles[1].canvas.mpl_connect('close_event', self.handle_close)
plt.plot(x,x)
plt.show()
else:
print "deselected"
plt.close(self.figureHandles[1])
def handle_close(self,event):
#print self.ground_var.get()
print "X'd velocity window"
#print self.figureHandles[1]
#plt.close(self.figureHandles[1])
self.velocity_var.set(0)
def createWidgets(self):
self.figureHandles = [0]*13
self.velocity_var = IntVar()
self.velocity = Checkbutton(self, text="Velocity", variable=self.velocity_var, command=self.velocity_cb)
self.velocity.grid(row=6,column=0,sticky='W')
self.velocity.var = self.velocity_var
def __init__(self,master=None):
Frame.__init__(self,master)
self.pack()
self.createWidgets()
if __name__=='__main__':
root = Tk()
app = Application(master = root)
app.mainloop()

matplotlib's zoom functionality inside a tkinter canvas

I have been trying to transfer some scripts behind a GUI (using Tkinter) and so far have made it thus far that any data that is opened is shown in a Tkinter canvas (using matplotlib to draw it).
The only problem that I have with this is that the standard zoom/scrolling that are in matplotlib (using left mouse button to 'move' the plot and right mouse button to 'zoom') are not accessible in the canvas, basically the functionality of the '4 pointed cross' in the matplotlib plot window.
I think this would require creating my own handlers but I would assume that there has to be a way to use the default handlers of matplotlib? I have also looked at 'scrolling' canvas options as mentioned in this question but those only seem to change the size of the plot area instead of zooming in/out on the data, also I do not want to add any further buttons just to manipulate the plot area.
The bare minimum code that I have currently:
#! /usr/bin/env python
from Tkinter import *
import matplotlib.pyplot as plt
import matplotlib.backends.backend_tkagg as tkagg
import tkFileDialog
class App():
def __init__(self,master):
# VARIABLES
self.inputFile = ""
self.fig = plt.Figure()
self.canvas = tkagg.FigureCanvasTkAgg(self.fig, master = master)
self.canvas.get_tk_widget().pack()
self.canvas.draw()
# FRAME
frame = Frame(master)
master.title("MassyTools 0.1.1 (Alpha)")
# VARIABLE ENTRIES
# BUTTONS
# MENU
menu = Menu(root)
root.config(menu = menu)
filemenu = Menu(menu)
menu.add_cascade(label="File", menu=filemenu)
filemenu.add_command(label="Open Input File", command = self.openFile)
calibmenu = Menu(menu)
menu.add_cascade(label="Calibrate",menu=calibmenu)
calibmenu.add_command(label="Open Calibration File", command = self.openCalibrationFile)
calibmenu.add_command(label="Calibrate", command = self.calibrateData)
def openFile(self):
file_path = tkFileDialog.askopenfilename()
setattr(self,'inputFile',file_path)
data = self.readData()
self.plotData(data)
def openCalibrationFile(self):
print "Place holder for selection of the calibration file"
def calibrateData(self):
print "Place holder for actual calibration"
def readData(self):
x_array = []
y_array = []
with open(self.inputFile,'r') as fr:
for line in fr:
line = line.rstrip('\n')
values = line.split()
x_array.append(float(values[0]))
y_array.append(float(values[1]))
return zip(x_array,y_array)
def plotData(self,data):
x_array = []
y_array = []
for i in data:
x_array.append(i[0])
y_array.append(i[1])
self.fig.clear()
self.axes = self.fig.add_subplot(111)
self.line, = self.axes.plot(x_array,y_array)
self.canvas.draw()
# Stuff that is not being used now but can be useful
"""def openFile(self,number):
name = tkFileDialog.askopenfilename()
ops = {
1: 'deglycoData',
2: 'peptideFile',
3: 'mzML'
}
setattr(self,ops[number],name)
"""
# End of 'stuff'
root = Tk()
app = App(root)
root.mainloop()
So you can affix a NavigationToolbar2TkAgg object to your canvas that will give you all the normal matplotlib methods and tools.
import matplotlib.backends.backend_tkagg as tkagg
# canvas is your canvas, and root is your parent (Frame, TopLevel, Tk instance etc.)
tkagg.NavigationToolbar2TkAgg(canvas, root)
A good example of its usage can be found here: Updating a graphs coordinates in matplotlib.
And an example of how to add custom methods to it can be found here (see class NavSelectToolbar(NavigationToolbar2TkAgg)).

2nd Page not showing up?

This is my code :
import sys
from tkinter import *
#first new screen
def next_screen(names):
for widget in names:
widget.place_forget()
buttonhyp = Button (text = "button1",fg = "blue",command = hypoténusegetdef())
buttonhyp.grid (row = 1,column = 2)
def forget_page1():
widgets = [mLabel1, button]
next_screen(widgets)
################################################################################
def hypténusegetdef ():
widgets1 = [buttonhyp]
nextscreen1(widgets1)
def next_screen(names):
for widget in names:
widget.place_forget()
hyplabel1 = Label (text = "This is my text")
#first page things
mGui = Tk ()
mGui.geometry("600x600+545+170")
mGui.title("MyMathDictionary")
mLabel1 = Label (text = "Welcome to MyMathDictionary. Press Next to continue.",
fg = "blue",bg = "white")
mLabel1.place (x= 150,y = 200)
button = Button (text = "Next", command = forget_page1 )
button.place(x = 275,y = 230)
mGui.mainloop()
What i'm trying to do is to open the program and get the user to click on "Next" and then to show another button which is called "button1" and when the user clicks on "button1" it shows up a text called which says "This is my text" in my code.But when i run it i click on "Next" and nothing shows up i checked and re- checked but nothing seems to work.Any help would be appreciated.
#
I am not an expert, but i will give it a try.
Firstly, import sys is not necessary. And importing all the objects from tkinter module using from tkinter import* is not recommended. You should use import tkinter or import tkinter as tk to avoid unexepcted consequences.
You have 2 functions with the same name. next_screen(names) which should not happen.
Instead of using widgets = [mLabel1, button] to hide the widgets, you should put them in a frame so that you can use winfo_children() to find all the children widgets.
You should put the parent widget name when you create buttons and labels. for example,
import tkinter as tk
root = tk.Tk()
mylabel = tk.Label(root,text='this is a label')
mylabel.pack()
root.mainloop()
In your first next_screen(names) function , you used grid method to display the button. You should not mix the grid method and place method.
This is something i came up with
import tkinter as tk
def Screen_1():
Clear(myframe)
mybutton2= tk.Button(myframe,text = "Button1", command = Screen_2)
mybutton2.pack()
def Screen_2():
Clear(myframe)
mylabel2= tk.Label(myframe,text = "This is my text",fg = "blue",bg = "white")
mylabel2.pack(side='top')
def Clear(parent):
for widget in parent.winfo_children():
widget.pack_forget()
root =tk.Tk()
myframe=tk.Frame(root)
myframe.pack()
mylabel1= tk.Label(myframe,text = "Welcome to MyMathDictionary. Press Next to continue.",fg = "blue",bg = "white")
mylabel1.pack(side='top')
mybutton1= tk.Button(myframe,text = "Next", command = Screen_1)
mybutton1.pack(side='bottom')
root.mainloop()
hope it helps!

Categories

Resources