2

I have a method that uses matplotlib.pyplot.ginput() to collect a single click's location from axes, and then place an image at that location within an existing underlaid array of axes in the same figure.

The visual only updates after a second call to ginput(), meaning that its response is lagged compared to the user's clicks, and only as they are choosing the next location does the image they've just placed show. This is problematic because the random image that gets placed may influence where they want to click next.

The calling method looks something like the below, although here axes are drawn rather than images being added:

%matplotlib
from matplotlib import pyplot
import numpy
import random

dimensions = [10,10]

visual_width = 1366
visual_height = 768
print("Established screen dimensions as "+str(visual_width)+","+str(visual_height))
         
fig, axarr = pyplot.subplots(dimensions[1],dimensions[0]
                               , num="Live game visualised"
                            , figsize=(visual_width, visual_height)
                            , clear=True)
fig.set_size_inches(visual_width, visual_height, forward=False)
overax = fig.add_subplot(111)
overax.axis("off")
#remove axes for initial rendering
for horizontal in range(0, dimensions[0]):
    for vertical in range(0, dimensions[1]):
        axarr[vertical, horizontal].axis('off')
text = fig.text(0,0, "", va="bottom", ha="left")

# test repeated interactions with figure, through successively adding tiles to the play area
playing = True
while playing:
    click = pyplot.ginput()
    if not click:
        break
    
    horizontal = int(click[0][0]*dimensions[0])
    vertical = dimensions[1] - int(click[0][1]*dimensions[1])
    print(str(horizontal)+","+str(vertical))
    text.set_text(str(horizontal)+","+str(vertical))
    axarr[vertical-1, horizontal].axis("on")

My python 3 script is in jupyter notebook, but I'm popping the visuals out with the default %matplotlib backend. I'm working in Chrome on Ubuntu.

  • 2
    Show something that does run. Write a bare minimum code, unrelated to your project, that reproduces the same error. Until you've done that, how can you say that you know what the problem is, much less ask someone to fix it for you? – Mad Physicist Jun 23 '20 at 06:56
  • On it, but was hoping in the meantime someone might recognise the ginput behaviour... – delwddrylliwr Jun 23 '20 at 08:04
  • 1
    I'm not convinced that it's ginput. That's why I think you should isolate the issue correctly first – Mad Physicist Jun 23 '20 at 14:54
  • Thanks for the push @MadPhysicist. I've now cleaned it down to the above script which runs and reproduces the behaviour. – delwddrylliwr Jun 24 '20 at 15:30

2 Answers2

0

It's a hacky and unsatisfactory solution, but my workaround is to prompt a double click, and accept that the first call to ginput is the data that will be stored to the variable assigned to the second call:

%matplotlib
from matplotlib import pyplot
import numpy
import random

dimensions = [10,10]

visual_width = 1366
visual_height = 768
print("Established screen dimensions as "+str(visual_width)+","+str(visual_height))
         
fig, axarr = pyplot.subplots(dimensions[1],dimensions[0]
                               , num="Live game visualised"
                            , figsize=(visual_width, visual_height)
                            , clear=True)
fig.set_size_inches(visual_width, visual_height, forward=False)
overax = fig.add_subplot(111)
overax.axis("off")
#remove axes for initial rendering
for horizontal in range(0, dimensions[0]):
    for vertical in range(0, dimensions[1]):
        axarr[vertical, horizontal].axis('off')
text = fig.text(0,0, "", va="bottom", ha="left")

# test repeated interactions with figure, through successively adding tiles to the play area
playing = True
while playing:
    pyplot.ginput()
    click = pyplot.ginput()
    if not click:
        break
    
    horizontal = int(click[0][0]*dimensions[0])
    vertical = dimensions[1] - int(click[0][1]*dimensions[1])
    print(str(horizontal)+","+str(vertical))
    text.set_text(str(horizontal)+","+str(vertical))
    axarr[vertical-1, horizontal].axis("on")
    
    text.set_text("Double click to make your next move")   
0

I think my problem is similar with yours. Mine is enlarging an image area selected by ginput using my own method.

This is my solution using plt.ion and plt.draw, and works fine in my script. The image super resolution method is deleted.

#!/usr/bin/env python
import matplotlib.pyplot as plt
import numpy as np

import argparse
import sys

parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument(
    "-i", "--image_name", type=str, default="test.jpg", help="Input image path"
)
args = parser.parse_known_args(sys.argv[1:])[0]

plt.ion()  # works in `.py` scripts
imm = plt.imread(args.image_name)
fig = plt.figure()
plt.imshow(imm)
plt.tight_layout()
plt.show()

imms = [imm]
while True:
    aa = plt.ginput(2, timeout=-1)
    aa = np.array(aa)

    left, right = int(aa[:, 0].min()), int(aa[:, 0].max())
    top, bottom = int(aa[:, 1].min()), int(aa[:, 1].max())

    if right - left < 1 or bottom - top < 1:  # Double click to return
        if len(imms) > 1:
            imms.pop(-1)
        else:
            break
    else:
        sub_image = imms[-1][top:bottom, left:right, :]
        imms.append(sub_image)
    plt.imshow(imms[-1])
    plt.draw()  # works in `ipython` environment

In my tests:

  • Adding plt.ion without the last plt.draw still works in a .py script.
  • Adding the last plt.draw without plt.ion works in ipython environment.
leondgarse
  • 91
  • 3