Python Forum
[Tkinter] Annotating live tkinter graph with matplotlib annotations
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
[Tkinter] Annotating live tkinter graph with matplotlib annotations
#1
    Hi, this is my first post, I can assure you I have blown up ChatGpt to get this figured out, and tried some tutorials without this combination, but kept getting errors.

As the picture I am posting indicates, I am trying to have my semi-live tkinter DOGE graph have simple annotations such in the small example to indicate where certain chart patterns form, but I haven't gotten it to work on the tkinter windows, only the Figure 1 standard still window from matplotlib has been able to have these annotations put on it. I have dumbed it down to where it only has to put a single indicator on one or multiple obvious points but it seems that tkinter can not accept the same types of annotations as the matplotlib window. Is there a workaround, or am I getting something wrong? Thanks.

import matplotlib.pyplot as plt
import mplfinance as mpf
import pandas as pd
import time
import robin_stocks.robinhood as r
import requests
from datetime import datetime
import pytz
import tkinter as tk
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import os

# Initialize global variables
program_start_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
doge_data = pd.DataFrame()  # Initialize empty DataFrame for doge_data
current_price = None  # Initialize current price

def read_credentials():
    credentials_path = os.path.join(os.path.dirname(__file__), "credentials.txt")
    with open(credentials_path, "r") as file:
        username, password = file.read().strip().split("\n")
    return username, password

def login_to_robinhood():
    username, password = read_credentials()
    try:
        r.login(username, password)
        print("Logged in successfully")
        return True
    except Exception as e:
        print("Login failed:", e)
        return False

def fetch_current_price():
    try:
        price_data = r.crypto.get_crypto_quote('DOGE')
        price = float(price_data['mark_price']) if price_data['mark_price'] else None
        #print("Current DOGE price:", price)
        return price
    except Exception as e:
        print(f"Error fetching current price: {e}")
        return None

def fetch_historical_data():
    end_time = int(time.time())
    start_time = end_time - 86400  # 24 hours ago

    url = 'https://min-api.cryptocompare.com/data/v2/histominute'
    params = {
        'fsym': 'DOGE',
        'tsym': 'USD',
        'limit': 60,
        'toTs': end_time
    }
    
    response = requests.get(url, params=params)
    if response.status_code == 200:
        data = response.json()['Data']['Data']
        df = pd.DataFrame(data)
        df['time'] = pd.to_datetime(df['time'], unit='s')
        df.set_index('time', inplace=True)
        
        if 'volumefrom' in df.columns:
            df.rename(columns={'volumefrom': 'volume'}, inplace=True)
        df = df[['open', 'high', 'low', 'close', 'volume']]
        
        tz = pytz.timezone('America/Chicago')
        df.index = df.index.tz_localize('UTC').tz_convert(tz)
        
        return df
    else:
        print("Error fetching historical data:", response.status_code)
        return pd.DataFrame()

def plot_doge_data():
    global doge_data, current_price  # Declare as global to access and modify the variables

    # Initialize doge_data and current_price
    doge_data = fetch_historical_data()
    current_price = fetch_current_price()
    
    if not doge_data.empty and current_price is not None:
        # Create a Tkinter window
        root = tk.Tk()
        root.title("DOGE Candlestick Chart")

        # Create a figure for the plot and embed it in the Tkinter window
        fig, ax = plt.subplots(figsize=(10, 6))  # Set figure size
        canvas = FigureCanvasTkAgg(fig, master=root)
        canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True)

        # Keep the window in the background
        root.wm_attributes("-topmost", False)

        # Main update loop for plotting
        def update_plot():
            global doge_data, current_price  # Use global to modify the variables defined in the outer function
            
            ax.clear()  # Clear previous plot to refresh
            
            # Plot using mplfinance
            mpf.plot(
                doge_data,
                type='candle',
                style='charles',
                ax=ax,
                ylabel='Price (USD)',
                volume=False,
                show_nontrading=False
            )

            # Update title and subtitle with current data
            current_time_str = pd.Timestamp.now(tz='America/Chicago').strftime("%Y-%m-%d %H:%M:%S CST")
            ax.set_title(f'DOGE Candlestick Chart - Current Price: ${current_price:,.4f}', fontsize=10, pad=20)
            fig.suptitle(f'Program Start: {program_start_time}\nCurrent Time: {current_time_str}', fontsize=8, y=0.92)
            
            canvas.draw()  # Update the plot
            
            # Refresh data
            current_price = fetch_current_price()  # Update price
            doge_data = fetch_historical_data()    # Refresh data with latest trades
            
            root.after(6000, update_plot)  # Schedule next update after 200 ms

        update_plot()  # Start the update loop
        root.mainloop()  # Start the Tkinter event loop

if __name__ == "__main__":
    if login_to_robinhood():
        plot_doge_data()
   
Reply
#2
I figured out a way using transparency. I'll paste this from ChatGPT, I've got work to do!

How I Overlaid High and Low Indicators on a Moving Matplotlib Tkinter Window with Transparency

In my project, I needed to overlay indicators (like the highest and lowest points of the price data, calculated using talib) onto a live updating chart in a Tkinter window. This was a tricky task because Tkinter and Matplotlib are both dynamic (the chart needs to update in real time), and it was crucial to make the indicators update properly without disrupting the visual flow of the chart. Here’s how I accomplished it:

1. Fetching Data and Calculating Indicators with talib
I used talib to calculate various technical indicators (like RSI, MACD, etc.) based on the live price data for DOGE. One of the key indicators I wanted to display was the highest and lowest price points over a selected time period, which was calculated from the historical price data.

2. The Challenge of Moving Tkinter Matplotlib Window
Unlike a static figure (which only requires simple plotting once), a moving Tkinter window with an embedded Matplotlib chart requires frequent updates (every few seconds or minutes). This is because the chart data is being updated constantly, and every new set of data must be plotted dynamically.

Without a careful method for updating the visual elements, it would be nearly impossible to add overlays like the high and low indicators without disrupting the chart’s updates.

3. Using Transparency to Overlay Indicators
The core challenge here was ensuring that the high and low points (calculated via talib and pandas) could be overlaid on the live chart without resetting the chart’s contents every time it updates. To achieve this, I used matplotlib’s transparency (alpha) property.

The key steps were:

Plot the candlestick chart first: This was done using mplfinance, which provides the base candlestick chart.
Overlay the high and low points: These points were plotted using ax.plot, where ax is the axis of the Matplotlib figure. By setting alpha=0.7, I could make the markers semi-transparent. This transparency ensured that the markers didn’t fully obstruct the underlying candlestick chart and were still visible but didn’t dominate the visual space.

python
ax.plot(highest_time, highest_point, 'ro', label='Highest Point', markersize=10, alpha=0.7)
ax.plot(lowest_time, lowest_point, 'go', label='Lowest Point', markersize=10, alpha=0.7)

Why transparency? Transparency allowed the markers to “layer” on top of the candlestick chart and keep updating without removing the existing chart data. This was crucial because when updating the chart in Tkinter (via the after method), the entire chart would typically be redrawn. Using semi-transparent markers ensured that the updates could be done smoothly, without interfering with the chart’s regular updates.

4. How This Works with a Moving Chart
The transparency allowed me to continuously overlay these indicators on top of the chart, while the chart itself updated in real time. Without this transparency, it would be much more difficult to keep these markers visible over the live chart because each update would have to include the new indicators along with the updated candlestick data.

By updating only the markers (using ax.plot with alpha), I avoided redrawing the entire chart. This allowed the high and low points (and any future indicators) to persist on the moving window, giving the illusion that they were "attached" to the chart, even as it updated dynamically.

5. Final Result
With this setup, the Tkinter window could constantly update the chart data (thanks to mplfinance), and at the same time, the high and low indicators would persist on the chart, updating as new price data came in. The transparency ensured the overlays were visible without interrupting the real-time updating process.

This technique can be applied to any indicators or visual elements you want to overlay onto a Matplotlib chart embedded in a dynamic Tkinter window. The transparency allows you to keep everything interactive, with no interference between the base chart and the overlays, making the visualization clean and effective.
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Interaction between Matplotlib window, Python prompt and TKinter window NorbertMoussy 3 2,456 Mar-17-2024, 09:37 AM
Last Post: deanhystad
  [Tkinter] Tkinter Matplotlib Animation Graph not rendering dimidgen 3 4,144 Mar-12-2024, 02:09 PM
Last Post: deanhystad
  [Tkinter] cutomtkinter matplotlib no x,y - xaxis and x,y - labels-> only graph and grid visible dduric 0 1,113 Feb-20-2024, 07:09 PM
Last Post: dduric
  How to use rangesliders feature from matplotlib in tkinter? pymn 2 4,134 Feb-28-2022, 05:06 PM
Last Post: pymn
  Tkinter Matplotlib Nepo 1 3,392 Sep-27-2020, 10:20 AM
Last Post: Gribouillis
  Tkinter & matplotlib PillyChepper 9 8,290 Nov-23-2019, 10:36 AM
Last Post: PillyChepper
  [Tkinter] how to remove black area around the graph in tkinter ? NEL 1 2,921 Aug-03-2019, 01:48 PM
Last Post: NEL
  Axis lim and Plotting a graph in Tkinter KEDivergente 0 2,282 May-21-2019, 08:10 PM
Last Post: KEDivergente
  Dynamic graph matplotlib quant7 1 5,032 May-17-2019, 06:24 PM
Last Post: quant7
  Tkinter - Make changes to graph and update it adriancovaci 0 7,645 Apr-08-2019, 09:02 AM
Last Post: adriancovaci

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020