4

I have a text and I want to make a graphic of the letter-frequency every n sentences. I have this code to make one graphic:

def graphic(dic):
    x = list(range(len(dic)))
    liste = []
    valeur = []
    for i in dic:
        liste += [(dic[i],i)]
        valeur += [dic[i]]
    liste.sort()
    liste.reverse()
    valeur.sort()
    valeur.reverse()
    my_xticks = []
    for i in liste:
        my_xticks += i[1]
    xticks(x, my_xticks)
    plot(x,valeur); show()
    return liste,valeur

It returns me this:

enter image description here

My point is, I want to use the arrows on the top of the window to change to one graphic to an other. Is this possible?

For example, I have a text with 10 sentences, and I want to make a graphic every 1 sentence. So, I'll have 10 graphics and I want to be able to navigate with the arrows but when I just call the function twice, it draws me 2 graph on the same page.

pioupiou1211
  • 353
  • 4
  • 16

2 Answers2

4

You can access the buttons and change their callbacks:

import matplotlib.pyplot as plt

def callback_left_button(event):
    ''' this function gets called if we hit the left button'''
    print('Left button pressed')


def callback_right_button(event):
    ''' this function gets called if we hit the left button'''
    print('Right button pressed')

fig = plt.figure()

toolbar_elements = fig.canvas.toolbar.children()
left_button = toolbar_elements[6]
right_button = toolbar_elements[8]

left_button.clicked.connect(callback_left_button)
right_button.clicked.connect(callback_right_button)
MaxNoe
  • 14,470
  • 3
  • 41
  • 46
  • Thanks that's exactly what I needed ! – pioupiou1211 Mar 05 '16 at 15:42
  • 1
    Depending on the maplotlib backend, the call to `children()` will be different. For example, with `tkinter`, the right function to call is `winfo_children()` Actually, the calls to `left_button.clicked.connect(...)` are not possible with `tkinter` backend. @MaxNoe, which backend are you using ? – matthieu Oct 29 '18 at 12:32
  • I think this was the qt backend – MaxNoe Oct 29 '18 at 12:49
  • I copy paste the code above and indeed, I have : 'NavigationIPy' object has no attribute 'children' . So now I'm wondering if I can change this line of code to make it work ? Or if unfortunatly I have to go through broader changes. I'm using Jupyter classic notebook, python 3, windows 10, best regards. – CechMS May 08 '21 at 05:20
1

Here's a way to do this without referring to a specific backend (i.e. it should be portable). The idea is that matplotlib defines a somewhat vague interface for backends to implement. This interface is the class NavigationToolbar2 below (github source; possible linux source directory: /usr/lib/python3/dist-packages/matplotlib/backend_bases.py). This interface uses a _nav_stack object of type Stack from cbook. This stack keeps information about different panning information, and when something changes the toolbar calls a function _update_view and redraws the canvas. By creating our own Stack, supplying (a proxy to) it to the toolbar, and overwriting _update_view, we can make the toolbar do what we'd like.

import matplotlib.backend_bases
import matplotlib.pyplot as plt
from numpy.random import random

# this is the data structure is used by NavigationToolbar2
# to switch between different pans.  We'll make the figure's 
# toolbar hold a proxy to such an object

from matplotlib.cbook import Stack

class StackProxy:
  def __init__(self,stack):
    self._stack = stack

  def __call__(self):
    return self._stack.__call__()

  def __len__(self):
    return self._stack.__len__()

  def __getitem__(self,ind):
    return self._stack.__getitem__(ind)

  def nop(self): pass

  # prevent modifying the stack
  def __getattribute__(self,name):
    if name == '_stack':
      return object.__getattribute__(self,'_stack')
    if name in ['push','clear','remove']:
      return object.__getattribute__(self,'nop')
    else:
      return object.__getattribute__(self._stack,name)

stack = Stack()

for data in [[random(10),random(10)] for _ in range(5)]:
  stack.push(data)

def redraw(*args):
  plt.clf()
  plt.scatter(*stack())   # stack() returns the currently pointed to item
  plt.gcf().canvas.draw()

fig = plt.gcf()
toolbar = fig.canvas.toolbar
toolbar._update_view = redraw.__get__(toolbar)
stackProxy = StackProxy(stack)
toolbar._nav_stack = stackProxy

redraw()
plt.show()

Previously, I was modifying some base classes of the buttons, but since then I've learned about some object oriented techniques of python and found this to be a much better solution.

Nathan Chappell
  • 2,099
  • 18
  • 21