0

I trying a lot to place a matplotlib plot (that also uses annotation) to place inside gtk3 window. If there is no annotation, I can place the plot easily inside gtk3. But, I have messed up with how to do it while using annotation. I have tried this without much progress.

#!/usr/bin/env python3
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.figure import Figure
from numpy import arange, pi, random, linspace
import matplotlib.cm as cm
from matplotlib.backends.backend_gtk3cairo import FigureCanvasGTK3Cairo as FigureCanvas

#WINDOW to embede matplotlib
x = np.random.rand(15)
y = np.random.rand(15)
p_window = Gtk.Window()
p_window.set_default_size(750,500)
p_header = Gtk.HeaderBar()
p_window.set_titlebar(p_header)
p_header.set_subtitle("Periodic Table")
p_header.set_title("column")
p_header.set_show_close_button(True)
c = np.random.randint(1,50,size=120)
norm = plt.Normalize(1,4)
cmap = plt.cm.RdYlGn
fig,ax = plt.subplots()
sc = plt.scatter(x,y)
plt.plot(x,y)
annot = ax.annotate("", xy=(0,0), xytext=(20,20),textcoords="offset points",
                    bbox=dict(boxstyle="round", fc="w"),
                    arrowprops=dict(arrowstyle="->"))
annot.set_visible(False)


def update_annot(ind):
  pos = sc.get_offsets()[ind["ind"][0]]
  annot.xy = pos
  namel = "foo"
  vall = "bar"
  text = "{}, {}".format(namel[2:-2], vall[1:-1])
  annot.set_text(text)
  #  annot.get_bbox_patch().set_facecolor(cmap(norm(c[ind["ind"][0]])))
  annot.get_bbox_patch().set_alpha(0.4)


def hover(event):
  vis = annot.get_visible()
  if event.inaxes == ax:
    cont, ind = sc.contains(event)
    if cont:
      update_annot(ind)
      annot.set_visible(True)
      fig.canvas.draw_idle()
    else:
      if vis:
        annot.set_visible(False)
        fig.canvas.draw_idle()
fig.canvas.mpl_connect("motion_notify_event", hover)

plt.show()
sw = Gtk.ScrolledWindow()
p_window.add(sw)

canvas = FigureCanvas(fig)
canvas.set_size_request(400,400)
sw.add_with_viewport(canvas)
p_window.show_all()
Gtk.main()

After removing pyplt I have remove pyplt dependency as suggested, and got this:

#!/usr/bin/env python3
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
import numpy as np
#  import matplotlib.pyplot as plt
from matplotlib.figure import Figure
from numpy import arange, pi, random, linspace
import matplotlib.cm as cm
from matplotlib.backends.backend_gtk3cairo import FigureCanvasGTK3Cairo as FigureCanvas

#WINDOW to embede matplotlib
x = np.random.rand(15)
y = np.random.rand(15)
p_window = Gtk.Window()
p_window.set_default_size(750,500)
p_header = Gtk.HeaderBar()
p_window.set_titlebar(p_header)
p_header.set_subtitle("Periodic Table")
p_header.set_title("column")
p_header.set_show_close_button(True)
c = np.random.randint(1,50,size=120)
fig = Figure(figsize=(10,6), dpi=100)
ax = fig.add_subplot(111)
ax.set_ylabel("column")
ax.set_xlabel("Z")
sc=ax.plot(x,y, "r-o")
print(type(fig))

annot = ax.annotate("", xy=(0,0), xytext=(20,20),textcoords="offset points",
                    bbox=dict(boxstyle="round", fc="w"),
                    arrowprops=dict(arrowstyle="->"))
annot.set_visible(False)


def update_annot(ind):
  pos = sc.get_offsets()[ind["ind"][0]]
  annot.xy = pos
  namel = "foo"
  vall = "bar"
  text = "{}, {}".format(namel[2:-2], vall[1:-1])
  print(text)
  annot.set_text(text)
  #  annot.get_bbox_patch().set_facecolor(cmap(norm(c[ind["ind"][0]])))
  annot.get_bbox_patch().set_alpha(0.4)


def hover(event):
  vis = annot.get_visible()
  if event.inaxes == ax:
    cont, ind = sc.contains(event)
    if cont:
      update_annot(ind)
      annot.set_visible(True)
      fig.canvas.draw_idle()
    else:
      if vis:
        annot.set_visible(False)
        fig.canvas.draw_idle()

sw = Gtk.ScrolledWindow()
p_window.add(sw)

canvas = FigureCanvas(fig)
canvas.set_size_request(400,400)
fig.canvas.mpl_connect("motion_notify_event", hover)
sw.add_with_viewport(canvas)
p_window.show_all()
Gtk.main()

which is giving me error:

python3 pop.py 
Traceback (most recent call last):
  File "/usr/lib64/python3.6/site-packages/matplotlib/backends/backend_gtk3.py", line 268, in motion_notify_event
    FigureCanvasBase.motion_notify_event(self, x, y, guiEvent=event)
  File "/usr/lib64/python3.6/site-packages/matplotlib/backend_bases.py", line 1958, in motion_notify_event
    self.callbacks.process(s, event)
  File "/usr/lib64/python3.6/site-packages/matplotlib/cbook.py", line 549, in process
    proxy(*args, **kwargs)
  File "/usr/lib64/python3.6/site-packages/matplotlib/cbook.py", line 416, in __call__
    return mtd(*args, **kwargs)
  File "pop.py", line 51, in hover
    cont, ind = sc.contains(event)
AttributeError: 'list' object has no attribute 'contains'
BaRud
  • 3,055
  • 7
  • 41
  • 89
  • I think at the moment you just copied the code from [this answer](https://stackoverflow.com/a/47166787/4124317) and that's it. Did you look at any example on how to embed matplotlib into GTK at all. E.g. [this one](https://matplotlib.org/examples/user_interfaces/embedding_in_gtk.html)? – ImportanceOfBeingErnest Nov 28 '17 at 16:37
  • Please, kindly read the question. I said I can embed matplotlib while no popup and hover is involved. I have a complete working code on that, only that is not suitable as mwe. So, I have pasted a smaller example. – BaRud Nov 28 '17 at 16:57
  • Of course this is based on what you show in the question. There is not attempt *in the question* to embed the plot into GTK. – ImportanceOfBeingErnest Nov 28 '17 at 17:00
  • code updated. Hope, you will see the attempt now. – BaRud Nov 28 '17 at 17:29
  • Ok. If you want to embed the plot, what does `plt.show` do in the code (except causing trouble)? – ImportanceOfBeingErnest Nov 28 '17 at 17:40
  • plt,show() here is just to make sure that the annotation is working as intended (which is still in mpl's own window) if you close the window, then you will get the same plot in gtk3 window, where the hover is not working, – BaRud Nov 28 '17 at 17:47
  • Let me put it differently: Don't call `plt.show` in a script which is meant to embed matplotlib in a GUI! Now if you remove `plt.show` and do not get the desired output, edit the question accordingly. – ImportanceOfBeingErnest Nov 28 '17 at 17:50
  • sorry, but do you know what are you talking? the code is not going to behave differently if it remove plt.show (tested). – BaRud Nov 28 '17 at 17:58
  • by the way, title is updated, too – BaRud Nov 28 '17 at 18:00
  • Yes I know what I'm talking about and it does not make any sense at all to call `plt.show()` when embedding a plot in a GUI. Only because you don't get the desired output when removing it does not mean that is in any way correct to include it in the code. I did answer a few questions here in the past where I even advised people not to use `pyplot` at all when embedding. Maybe that should apply here as well, although it might make the code a bit longer. – ImportanceOfBeingErnest Nov 28 '17 at 18:05
  • I said in my last comment that the plt.show is for debugging only. Have you ran this code to check that it doesn't affect the final result? And please, do you have any real suggestion? – BaRud Nov 28 '17 at 18:14
  • I do not have GTK available so I cannot run anything. So suppose you have removed `plt.show()`, the GTK window is correctly popping up and the plot is shown inside the GTK window? If so, the only problem is indeed the use of pyplot. Because you create the figure via `plt.subplots()` the figure already has a canvas which is not the one you later set via `FigureCanvas(fig)`. The event is registered to the old canvas. The new `FigureCanvas` does not have that event. My real suggestion would hence indeed be: get rid of pyplot completely in your script. – ImportanceOfBeingErnest Nov 28 '17 at 18:18
  • Ok, I will try removing pyplt. But, the actual problem is the hover or the annote is not detected once the plot is opened via gtk window. Do you think that might be a pyplt issue? – BaRud Nov 28 '17 at 18:24
  • It's not a pyplot issue. The issue is that you have two canvases. The one you create via pyplot and which has the event registered to it and the one you use to embed into GTK. So once you exchange the old for the new canvas also the event is lost, hence you don't get any dynamic annotations. The aim of removing pyplot from the script is to ensure that you do not work with different canvases but only the one which will later be shown. If you register the event to that canvas it should work as expected. – ImportanceOfBeingErnest Nov 28 '17 at 18:35
  • Ok, now its clearer. But then, what is my opinion other than pyplt? I haven't used anything else ever. Any suggestion? – BaRud Nov 28 '17 at 18:42
  • Stick to the example I linked to in the first comment. It also does not use pyplot. – ImportanceOfBeingErnest Nov 28 '17 at 18:44
  • Hi, I have removed pyplt as suggested. but its giving error. can you kindly have a look? – BaRud Nov 29 '17 at 05:10
  • You changed the `scatter` plot to a line `plot`. For that you need to unpack the line plot plot, `line, =ax.plot(x,y, "r-o")` and instead ise the Line2D's features. I updated my original answer with a solution for line plots. – ImportanceOfBeingErnest Nov 29 '17 at 10:16
  • Hi, thanks for your reply. But I have fixed it and its working perfect now. – BaRud Nov 29 '17 at 10:17
  • Ok, so in order for this question to be useful for others I would suggest you remove the edit from the question (as this is only line/scatter confusion) and instead provide a working solution as answer to this question. – ImportanceOfBeingErnest Nov 29 '17 at 10:20

0 Answers0