Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hiding figure popup window when using Tkinter #304

Closed
KohliBuilt opened this issue Dec 27, 2020 · 12 comments
Closed

Hiding figure popup window when using Tkinter #304

KohliBuilt opened this issue Dec 27, 2020 · 12 comments
Labels
question Further information is requested

Comments

@KohliBuilt
Copy link

KohliBuilt commented Dec 27, 2020

First off, I love mplfinance and thanks for all your work!

I just started putting together a GUI to help me quickly review financial charts. I keep getting the figure window in addition to the plot being embedded in the GUI, as you can see below. Is there anyway to prevent the Figure 1 window from being presented?

image

Parts of my code:

from tkinter import Tk,Label,Menu,filedialog,Button,Frame
import pandas as pd
import KohliFunctions as kb
import json
import mplfinance as mpf
from matplotlib.figure import Figure

import matplotlib as matplotlib
matplotlib.use("TkAgg")
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg

def getCandlestickChart():
    chartData = DataSet.historicalData
    symbol = DataSet.symbol
    mc = mpf.make_marketcolors(up='#7DFFB8', #Light Green
                               down='#FF7979', #Light Red
                               edge='black',
                               wick='black',
                               volume='in')
    
    s  = mpf.make_mpf_style(base_mpl_style="seaborn", 
                            mavcolors=["lightseagreen","orange"],
                            facecolor = "#F9FBFD",
                            gridcolor = "#F2F2F2",
                            gridstyle = "--",
                            marketcolors=mc)
    
    fig, ax = mpf.plot(chartData,type='candle',
                      style = s,
                      tight_layout=True,
                      volume=True,   
                      title = f"2 Month Rolling - {symbol}",
                      figratio=(12,6),
                      returnfig=True
                      )
    
    
    canvas = FigureCanvasTkAgg(fig, master=middleFrame)   
    canvas.draw()
    canvas.get_tk_widget().grid(row=0, column=0)


#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
window = Tk()
window.geometry('1200x900') #Window Size

topFrame = Frame(window, width=1180, height= 175, bg='white')
topFrame.grid(row=0, column=0, padx=10, pady=5)
topFrame.grid_propagate(False)

middleFrame = Frame(window, width=1100, height= 400, bg='white')
middleFrame.grid(row=1, column=0, padx=10, pady=5)

bottomFrame = Frame(window, width=1100, height= 200)
bottomFrame.grid(row=2, column=0, padx=10, pady=5)

window.mainloop()
@KohliBuilt KohliBuilt added the question Further information is requested label Dec 27, 2020
@DanielGoldfarb
Copy link
Collaborator

I am not very familiar with using tkinter. In theory, because you have specified returnfig=True, then mpf.plot() will not call .show() and so I would expect that "the Figure 1 window" would not appear.

Perhaps someone with more experience using matplotlib with tkinter (for example @tacaswell or someone else within the @matplotlib/community) will have some insights into what is causing the Figure 1 window to be drawn.

For me to try to determine what's going on, I would need to see all of your code. For example, where does getCandlestickChart() get called?

@DanielGoldfarb
Copy link
Collaborator

Just to clarify "to see all of your code" ... I don't really need all of it, but I do need a complete set of code to minimally reproduce the issue if you would like me to look into it.

@KohliBuilt
Copy link
Author

Thanks for your quick reply!

I put together a sample code that you should be able to just copy, paste and run.

I also noticed that with tight_layout=True & figscale=0.50 some of the plot is cutoff. Is this normal?
image

Sample Code:

import mplfinance as mpf
import pandas as pd
import matplotlib as matplotlib
matplotlib.use("TkAgg")

from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from tkinter import Tk,Button,Frame

def getCandlestickChart():
    mc = mpf.make_marketcolors(up='#7DFFB8', #Light Green
                               down='#FF7979', #Light Red
                               edge='black',
                               wick='black',
                               volume='in')
    
    s  = mpf.make_mpf_style(base_mpl_style="seaborn", 
                            mavcolors=["lightseagreen","orange"],
                            facecolor = "#F9FBFD",
                            gridcolor = "#F2F2F2",
                            gridstyle = "--",
                            marketcolors=mc)
    
    fig, ax = mpf.plot(chartData,type='candle',
                      style = s,
                      tight_layout=True, 
                      title = "Test Plot",
                      figratio=(12,6),
                      figscale=0.50,
                      returnfig=True
                      )
    
    
    canvas = FigureCanvasTkAgg(fig, master=graphFrame)   
    canvas.draw()
    canvas.get_tk_widget().grid(row=0, column=0)

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#Generate a dataset, "chartData"
d = {'date': ["2020-04-16","2020-04-17","2020-04-20"],
     'open': [18.03, 18.53,18.57],
     'high': [18.73,18.53,18.57],
     'low': [16.73,17.5,17.257],
     'close': [16.94,18.36,17.71],
     }
df = pd.DataFrame(data=d)
chartData = df.sort_values(by='date')
chartData['date'] = pd.to_datetime(chartData['date'],format='%Y-%m-%d')
chartData = chartData.set_index('date')

#Setup Tkinter and frames ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
window = Tk()
window.title("Plot Test") #Window Title
window.geometry('600x390') #Window Size
#Graph frame
graphFrame = Frame(window, width=600, height= 300, bg='white')
graphFrame.grid(row=0, column=0, padx=10, pady=5)
#Button Frame
bottomFrame = Frame(window, width=600, height= 75)
bottomFrame.grid(row=2, column=0, padx=10, pady=5)

#Add Button ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
btn = Button(bottomFrame, text="Plot!", command=getCandlestickChart)
btn.grid(column=0, row=0)

#Tkinter Main Loop
window.mainloop()

@DanielGoldfarb
Copy link
Collaborator

Thanks. I am able to run it, but when I click plot I get just the one plot (not the extra "Figure 1" window).

Perhaps later tonight when I have a little more time I will try running it on some of my other systems. This worked fine (no extra window) on WSL2 Ubuntu. I also have a separate Ubuntu boot (not under WSL), a Mac, and a Red Hat that I can try. Btw, what is your environment?

Regarding the tight_layout and the figscale apparently not interacting well (and thereby cutting off some of the plot), I haven't seen this but I am not surprised. Mplfinance cannot do a standard Matplotlib tight_layout (something to do with Matplotlib thinking the Axes are incompatible with tight_layout).

Therefore Mplfinance implements its own tight_layout by adjusting the padding around the Figure. It is understandable that the algorithm may not adapt well to scaling down the size of the Figure. Perhaps some day that algorithm can be improved. In the meantime, I was able to fix it by increasing the padding at the bottom using kwarg scale_padding=dict(bottom=1.75) in your code:

fig, ax = mpf.plot(chartData,type='candle',
                      style = s,
                      tight_layout=True, 
                      title = "Test Plot",
                      figratio=(12,6),
                      figscale=0.50,
                      scale_padding=dict(bottom=1.75),
                      returnfig=True
                      )

You can read more about scale_padding in this comment here.

HTH

@KohliBuilt
Copy link
Author

Thanks so much for the feedback on the scaling issue!

I had a weird feeling that this might be a system or user setting error. Guess we haven’t fully confirmed this. I am running Windows 10 with Spyder IDE. I did reset all preferences back to default and I still get the second figure window.

@DanielGoldfarb
Copy link
Collaborator

DanielGoldfarb commented Dec 28, 2020

I installed python on Windows 10. I took the above test script, write it to disk using notepad, and then from powershell I ran python on that script (I did not install Spyder). I got the same result as when I ran it on Ubuntu: no second window. Everything seems normal. Will have to try to involve someone who is more expert than I am with matplotlib backends and GUIs who may have a clue here. Obviously something environmental, but I have no idea where to even begin.

@KohliBuilt perhaps try that: Save the test script to a file and run it under PowerShell or CommandPrompt. Then depending on whether you still see the issue you will know whether it's a windows envinroment thing, or a Spyder environment thing.

@DanielGoldfarb
Copy link
Collaborator

OK. I installed Spyder and ran it from inside Spyder and indeed I am able to reproduce the problem. This is clearly something specific to Spyder. We had another issue a while back that was also specific to Spyder and while it is unlikely this is related to that, it may be worth noting that Spyder runs Ipython in pylab mode which may treat things differently. For example, in that case it was found that None was treated as False in pylab mode but as True in all other environments (or perhaps "treated as" is the wrong expression; perhaps there was a different "default" boolean value that was used when the variable in question was of NoneType).

At any rate, this is clearly Spyder related. The question is now whether we can isolate the cause and possibly patch mplfinance so that it will behave the same in Spyder as in other environments.

@DanielGoldfarb
Copy link
Collaborator

Adding the following to your script fixes the issue in Spyder:

import matplotlib.pyplot as plt
plt.ioff()

reference: https://stackoverflow.com/a/53217287/1639359

@KohliBuilt
Copy link
Author

It worked!!! @DanielGoldfarb you are amazing and I greatly appreciate all your time and support!!

@DanielGoldfarb
Copy link
Collaborator

Glad it worked!

@KohliBuilt
Copy link
Author

@DanielGoldfarb I don't think the solution presented has fully resolved our issue. I noticed that when I close the GUI window the script continues running...which leads me to believe there is still some open window or figure that is hidden. For some reason it is not being destroyed when I close the main GUI window.

Maybe there is a way to close it?

For now I am running plt.close(fig) after the mpf.plot function. However, you still see the figure window quickly pop up then get closed.

    fig, ax = mpf.plot(chartData,type='candle',
                      style = s,
                      tight_layout=True, 
                      title = "Test Plot",
                      figratio=(12,6),
                      figscale=0.50,
                      returnfig=True
                      )
    plt.close(fig)

@DanielGoldfarb
Copy link
Collaborator

DanielGoldfarb commented Dec 30, 2020

I'm really not sure what I am doing with Spyder and tkinter, since I haven't really used them before; but with a little googling and some reasoning I was able to get it working. First let me describe what I am doing and what I saw.

I am running your test program as a .py file on the left side of the Spyder interface. When I click the green triangle ("Run File") the window pops up with a button that says "Plot!". At the same time, in the IPython console I see

In [1] runfile('C:/Users/dgold/issue304.py', wdir='C:/Users/dgold')

I then click the "Plot!" button and the candlestick plot appears in the window. If I then click the "X" in the upper right hand corner, the window closes, but I do NOT see In [2]: in the IPython console as I would expect. After that, I am also unable to run the file again until I click on the "Consoles" menu and choose "Restart kernel".

The main reason for the above description is just to make sure we are running things more-or-less the same way.


I don't know how you got it working at all with plt.close() because, for me, that messes things up and I never see the candlestick plot. However I was able to get it working as follows:

  • I wrote a function called on_closing:
def on_closing():
    plt.close('all')
    window.destroy()
  • Then, just before calling window.mainloop() I added the following:
window.protocol("WM_DELETE_WINDOW",on_closing)

That seems to have done the trick. Let me know.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants