1

I met one question about how to retrieve a specific patch object from subplot axes. For instance, I create a subplot which contains many patches.Polygon objects(I assign each one different label) in a function. After I add this subplot to a figure by add_subplot, it seems that I lost access to those patches.Polygon objects I created inside the subplot. I know that I can get the objects by using findobj() method, however, it only returns the type of the object and its memory address. I can change the facecolor of all of the objects but what I really need is to access one specific object by name or label, such as to change the color of one patches.Ploygon instead of all of them. I appreciate it if someone knows how to achieve that. My script is attached below.

import matplotlib.pyplot as plt
import matplotlib.patches as patches
import matplotlib.gridspec as gridspec
import numpy as np


def drawEI(ax) :
    # draw Endcap
    x0 = np.array([0.5, 0.5, 0.5, 0.5])
    y0 = np.array([0.5, 0.5, 0.5, 0.5])

    step = 0.08

    xy = np.zeros((2, 4))
    print(xy)
    # theta = [for i in range(16) : ]
    theta = [345, 15, 30, 60, 75, 105, 120, 150, 165, 195, 210, 240, 255, 285, 300, 330]
    nSector = 16
    nEta = 4

    chamberPlot = {}

    for sector in range(nSector):
        for eta in range(nEta):
            # skip eta = 3
            if eta == 2 : continue
            if eta == 3 and sector%2 == 0 : continue
            #if eta == 4 :
            # print (sector, eta)
            # x1[sector][eta] = step*(1+eta)*np.cos(np.pi*theta[sector]/360.)
            # y1[sector][eta] = step*(1+eta)*np.sin(np.pi*theta[sector]/360.)
            # create polygon coordinate by numpy
            # xy = np.arange(8).reshape(4,2)
            x1 = np.array([step * (1 + eta) * np.cos(np.pi * theta[sector] / 180.),
                           step * (2 + eta) * np.cos(np.pi * theta[sector] / 180.),
                           step * (2 + eta) * np.cos(np.pi * theta[(sector + 1) % 16] / 180.),
                           step * (1 + eta) * np.cos(np.pi * theta[(sector + 1) % 16] / 180.)])
            y1 = np.array([step * (1 + eta) * np.sin(np.pi * theta[sector] / 180.),
                           step * (2 + eta) * np.sin(np.pi * theta[sector] / 180.),
                           step * (2 + eta) * np.sin(np.pi * theta[(sector + 1) % 16] / 180.),
                           step * (1 + eta) * np.sin(np.pi * theta[(sector + 1) % 16] / 180.)])
            # print (x1+=x0,y1+=y0)
            xy[0] = x1 + x0
            xy[1] = y1 + y0
            newxy = xy.transpose()
            print(newxy.tolist())
            # ax.add_patch(patches.Polygon(xy=list(zip(np.add(x1+x0),np.add(y1+y0))), fill=False))
            index = sector + sector * eta
            print(index)
            chamberPlot[index] = patches.Polygon(newxy.tolist(), edgecolor='black', facecolor='green')
            chamberPlot[index].set_label('EI_%s_%s'%(eta,sector))
            # ax.add_patch(patches.Polygon(newxy.tolist(), edgecolor = 'black', facecolor = 'green'))
            ax.add_patch(chamberPlot[index])

    return ax

def drawEM(ax) :
    # draw Endcap
    x0 = np.array([0.5, 0.5, 0.5, 0.5])
    y0 = np.array([0.5, 0.5, 0.5, 0.5])

    step = 0.07

    xy = np.zeros((2, 4))
    print(xy)
    # theta = [for i in range(16) : ]
    theta = [345, 15, 30, 60, 75, 105, 120, 150, 165, 195, 210, 240, 255, 285, 300, 330]
    nSector = 16
    nEta = 5

    chamberPlot = {}

    for sector in range(nSector):
        for eta in range(nEta):
            # print (sector, eta)
            # x1[sector][eta] = step*(1+eta)*np.cos(np.pi*theta[sector]/360.)
            # y1[sector][eta] = step*(1+eta)*np.sin(np.pi*theta[sector]/360.)
            # create polygon coordinate by numpy
            # xy = np.arange(8).reshape(4,2)
            x1 = np.array([step * (1 + eta) * np.cos(np.pi * theta[sector] / 180.),
                           step * (2 + eta) * np.cos(np.pi * theta[sector] / 180.),
                           step * (2 + eta) * np.cos(np.pi * theta[(sector + 1) % 16] / 180.),
                           step * (1 + eta) * np.cos(np.pi * theta[(sector + 1) % 16] / 180.)])
            y1 = np.array([step * (1 + eta) * np.sin(np.pi * theta[sector] / 180.),
                           step * (2 + eta) * np.sin(np.pi * theta[sector] / 180.),
                           step * (2 + eta) * np.sin(np.pi * theta[(sector + 1) % 16] / 180.),
                           step * (1 + eta) * np.sin(np.pi * theta[(sector + 1) % 16] / 180.)])
            # print (x1+=x0,y1+=y0)
            xy[0] = x1 + x0
            xy[1] = y1 + y0
            newxy = xy.transpose()
            print(newxy.tolist())
            # ax.add_patch(patches.Polygon(xy=list(zip(np.add(x1+x0),np.add(y1+y0))), fill=False))
            index = sector + sector * eta
            print(index)
            chamberPlot[index] = patches.Polygon(newxy.tolist(), edgecolor='black', facecolor='green')
            chamberPlot[index].set_label('EM_%s_%s'%(eta,sector))
            # ax.add_patch(patches.Polygon(newxy.tolist(), edgecolor = 'black', facecolor = 'green'))
            ax.add_patch(chamberPlot[index])

    return ax

def drawEO(ax) :
    # draw Endcap
    x0 = np.array([0.5, 0.5, 0.5, 0.5])
    y0 = np.array([0.5, 0.5, 0.5, 0.5])

    step = 0.07

    xy = np.zeros((2, 4))
    print(xy)
    # theta = [for i in range(16) : ]
    theta = [345, 15, 30, 60, 75, 105, 120, 150, 165, 195, 210, 240, 255, 285, 300, 330]
    nSector = 16
    nEta = 6

    chamberPlot = {}

    for sector in range(nSector):
        for eta in range(nEta):
            # print (sector, eta)
            # x1[sector][eta] = step*(1+eta)*np.cos(np.pi*theta[sector]/360.)
            # y1[sector][eta] = step*(1+eta)*np.sin(np.pi*theta[sector]/360.)
            # create polygon coordinate by numpy
            # xy = np.arange(8).reshape(4,2)
            x1 = np.array([step * (1 + eta) * np.cos(np.pi * theta[sector] / 180.),
                           step * (2 + eta) * np.cos(np.pi * theta[sector] / 180.),
                           step * (2 + eta) * np.cos(np.pi * theta[(sector + 1) % 16] / 180.),
                           step * (1 + eta) * np.cos(np.pi * theta[(sector + 1) % 16] / 180.)])
            y1 = np.array([step * (1 + eta) * np.sin(np.pi * theta[sector] / 180.),
                           step * (2 + eta) * np.sin(np.pi * theta[sector] / 180.),
                           step * (2 + eta) * np.sin(np.pi * theta[(sector + 1) % 16] / 180.),
                           step * (1 + eta) * np.sin(np.pi * theta[(sector + 1) % 16] / 180.)])
            # print (x1+=x0,y1+=y0)
            xy[0] = x1 + x0
            xy[1] = y1 + y0
            newxy = xy.transpose()
            print(newxy.tolist())
            # ax.add_patch(patches.Polygon(xy=list(zip(np.add(x1+x0),np.add(y1+y0))), fill=False))
            index = sector + sector * eta
            print(index)
            chamberPlot[index] = patches.Polygon(newxy.tolist(), edgecolor='black', facecolor='green')
            chamberPlot[index].set_label('EO_%s_%s'%(eta,sector))
            # ax.add_patch(patches.Polygon(newxy.tolist(), edgecolor = 'black', facecolor = 'green'))
            ax.add_patch(chamberPlot[index])

    return ax



fig = plt.figure(constrained_layout=False)

spec2 = gridspec.GridSpec(ncols=2, nrows=2, figure=fig)

axEM = fig.add_subplot(spec2[1,0])
axEM.set_xticklabels([])
axEM.set_yticklabels([])
axEM.axis('off')

axEM = drawEM(axEM)

axEO = fig.add_subplot(spec2[0,1])
axEO.set_xticklabels([])
axEO.set_yticklabels([])
axEO.axis('off')

axEO = drawEO(axEO)

axEI = fig.add_subplot(spec2[0,0])
axEI.set_xticklabels([])
axEI.set_yticklabels([])
axEI.axis('off')

axEI = drawEI(axEI)


objs = axEI.findobj(patches.Polygon)
print (objs)
for obj in objs :
    #print (obj.label())
    obj.set_facecolor('red')  # this is ok 

plt.subplots_adjust(wspace=0, hspace=0)
plt.show()  

My question is how can I change the face color of one specific ploygon 'BM_2_5' in subplot axEM ?

1 Answer 1

3

Using the axis object and the index of your Patch, you can achieve what you want this way:

import matplotlib.pyplot as plt
import matplotlib.patches as ptc
import numpy as np

fig, ax = plt.subplots()
pCol = ['red', 'blue']
for i in range(2):
    ax.add_patch(ptc.Polygon(np.random.rand(3, 2), fc=pCol[i]))
ax.patches[0].set_facecolor((0, 1, 0, 1))
plt.show()

enter image description here

Notice how no red polygon is plotted.

EDIT: With your comment in mind

import matplotlib.pyplot as plt
import matplotlib.patches as ptc
import numpy as np

fig, ax = plt.subplots()
pLab = ['First', 'Second']
pCol = ['red', 'blue']
for i in range(2):
    ax.add_patch(ptc.Polygon(np.random.rand(3, 2), fc=pCol[i], label=pLab[i]))
for patch in ax.patches:
    if patch.get_label() == 'First':
        patch.set_facecolor('green')
plt.show()

enter image description here

EDIT: Returning an axis from a function should be OK.

import matplotlib.pyplot as plt
import matplotlib.patches as ptc
import numpy as np


def drawPatches(ax):
    for i in range(2):
        ax.add_patch(ptc.Polygon(np.random.rand(3, 2), fc=pCol[i],
                                 label=pLab[i]))
    return ax


fig, ax = plt.subplots()
pLab = ['First', 'Second']
pCol = ['red', 'blue']
ax = drawPatches(ax)
for patch in ax.patches:
    if patch.get_label() == 'First':
        patch.set_facecolor('green')
plt.show()

enter image description here

Sign up to request clarification or add additional context in comments.

4 Comments

Thank you very much @Patol75, it is good to know by using the index to access. However, I am looking if there is a way to access the Patches by its name/label given by myself when I created them. Otherwise, someone has to know exact order of those patches in advance in order to change the properties.
thanks again. In fact, my real problem is I couldn't get the patch label out of the function. I got an empty string when I add the below two lines to my code. So this somehow turns to a question about the variable scope. I thought the axes object will still keep all information about those contained patches. ``` for patch in axEM.patches: print (patch.get_label()) ```
This loop is indeed right after axEM = drawEM(axEM)? Also, see my second edit.
you are right, I finally run through my code and got what I suppose to look. Thank you very much.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.