简体   繁体   中英

How to custom fit horizontal bar plot in matplotlib?

I have been struggling with getting my matplotlib graph embedded in tkinter GUI; I could have used QTPython which looks really neat but it was too late.

This is my following code for updating graphs in tkinter GUI (it is embedded, no external window) I would like to resize the chart to fit correctly. I have so far tried the following:

1) Tight Layout : fig_findin.tight_layout() 2) Yticks Wrap True 3) I have tried different bar width from 0.1 to 0.6

My main problem is to reduce the height of the Gray background frame (the grid lines frame or subplot). this would make the title completely visible and it would look consistent with the other graph. I would also like the y_axis labels to be wrapped but still visible in the existing frame.

Here is my code for the matplotlib graphs.

def animate_artifact_graph(i):
    #######################################
    ''' GRAPH START '''
    #######################################
    ## DATA
    x_cat = [x[1] for x in db.query_artifact()]
    x_cat = x_cat[:-2]


    y_count = {}
    for i in x_cat:
        y_count[i] = 0

    artifact_name,proj_name,mod_name = artifact_type_select.get() ,proj_select.get(),proj_mod_select.get()
    if(artifact_name == 'All'):
        artifact_name = db.query_artifact_name()
    else:
        artifact_name = [artifact_name]

    observations_fr_project = db.query_closed_findings_by_project(proj_name,mod_name,artifact_name)

    title = proj_name + " - " + mod_name

    for i in observations_fr_project:
        y_count[i[2]] = y_count[i[2]] + 1

    y_count = { k:v for k, v in y_count.items() if v > 0 and k != 'RTM'}

    x = list(y_count.keys())
    y = list(y_count.values())

    # ## SHOW

    # rects = ax.patches
    # labels = [ i for i in list(y_count.values())]

    # for rect, label in zip(rects, labels):
    #     if(rect.get_height() > 0):
    #         height = rect.get_height() - 1.50
    #         width = rect.get_width() /2 
    #         ax.text(rect.get_x() + width, rect.get_y()+height-0.26,label)
    # ax.set_ylabel("Observations", fontsize=12)
    # ax.set_xlabel("Artifact Type", fontsize=12)
    ax.clear()
    now = datetime.datetime.now()
    date_display = now.strftime('%A, %d %B %Y, %H:%M')
    ax.set_title (title + "\n" + "Total number of findings per deliverable type\n(as of " + date_display + ")", fontsize=8)

    def func(pct, allvals):
        absolute = int(pct/100.*np.sum(allvals))
        return "{:.1f}%\n{:d}".format(pct, absolute)


    wedges, texts, autotexts = ax.pie(y, autopct=lambda pct: func(pct, y),
                                      textprops=dict(color="w"))

    ax.legend(wedges, x,
              title="Artifact Types",
              loc="center left",
              bbox_to_anchor=(1, 0, 0.5, 1))
    fig_artif.tight_layout()
    ax.axis('equal')  # Equal aspect ratio ensures that pie is drawn as a circle.

    #######################################
    ''' GRAPH END '''
    #######################################

def animate_finding_graph(i):
    #######################################
    ''' GRAPH START '''
    #######################################

    ## DATA
    x_cat = [x[1] for x in db.query_finding()]
    y_count = {}

    for i in x_cat:
        y_count[i] = 0

    artifact_name,proj_name,mod_name = artifact_type_select.get() ,proj_select.get(),proj_mod_select.get()
    if(artifact_name == 'All'):
        artifact_name = db.query_artifact_name()
    else:
        artifact_name = [artifact_name]

    observations_fr_project = db.query_closed_findings_by_project(proj_name,mod_name,artifact_name)

    title = proj_name + " - " + mod_name

    for i in observations_fr_project:
        y_count[i[1]] = y_count[i[1]] + 1

    y_count = { k:v for k, v in y_count.items() if v > 0 and k != 'nan'}
    y_count = OrderedDict(sorted(y_count.items(), key=lambda kv: kv[1],reverse=True))

    if(len(list(y_count.keys())) >= 5):
        to_remove = list(y_count.keys())[5:]
        for x in to_remove:
            del y_count[x]

    x = list(y_count.values())
    y = list(y_count.keys())

    ## SHOW
    ay.clear()
    bar_width = 0.4
    ay.barh(y,x,bar_width,align='edge',color='yellow')
    ay.invert_yaxis()
    rects = ay.patches
    # print("rects",len(rects))
    labels = [ i for i in list(y_count.values())]
    # print("\n")

    for rect, label in zip(rects, labels):
        height = rect.get_height()/2
        width = rect.get_width() - 0.50
        ay.text(rect.get_x() + width, rect.get_y()+height,label,fontsize=8)

    ay.tick_params(
    axis='x',          # changes apply to the x-axis
    which='both',      # both major and minor ticks are affected
    bottom=False,      # ticks along the bottom edge are off
    top=False,         # ticks along the top edge are off
    labelbottom=False) # labels along the bottom edge are off

    ay.set_yticklabels(y,fontsize=6,wrap=True)

    for tick in ay.yaxis.get_major_ticks():
        tick.label1.set_verticalalignment('center')
    now = datetime.datetime.now()
    date_display = now.strftime('%A, %d %B %Y, %H:%M')
    ay.set_title (title + "\n" + "Top 5 Deliverables.", fontsize=6)

    #######################################
    ## ''' GRAPH END '''
    #######################################

def artifact_graph():
    canvas_artifact = FigureCanvasTkAgg(fig_artif, master=tab1_closed_observations)
    canvas_artifact.get_tk_widget().grid(row=7,column=0,padx=10,pady=5,columnspan=3)

    ani = animation.FuncAnimation(fig_artif, animate_artifact_graph,interval=500)
    canvas_artifact.draw()   

    def export_win():
        output_folder = "./Reports/" + proj_select.get() + " -- " + proj_mod_select.get()
        if not os.path.exists(output_folder):
            os.makedirs(output_folder)
        fig_artif.savefig(output_folder + "/artifact.png")

    export_artifact_graph = Button(tab1_closed_observations, text='Export', command=export_win)
    export_artifact_graph.grid(row=6,column=0,padx=70,pady=20,sticky='we',columnspan=2)

def finding_category():
    canvas_finding = FigureCanvasTkAgg(fig_findin, master=tab1_closed_observations)
    canvas_finding.get_tk_widget().grid(row=7,column=5,padx=10,pady=5,columnspan=3)

    ani = animation.FuncAnimation(fig_findin, animate_finding_graph,interval=500)
    canvas_finding.draw() 

    def export_win():
        output_folder = "./Reports/"+ proj_select.get() + " -- " + proj_mod_select.get()
        if not os.path.exists(output_folder):
            os.makedirs(output_folder)
        fig_findin.savefig(output_folder + "/finding.png")

    export_finding_graph = Button(tab1_closed_observations, text='Export', command=export_win)
    export_finding_graph.grid(row=6,column=5,padx=70,pady=20,sticky='we',columnspan=3)

This is how my graphs look :

在此处输入图片说明

i figured out how to have a snug fit but still could not make the yticks appear neatly.

The catch is to decrease bar width and hide all other y axes accessories. Adding Wrap = True and vertical aligning the labels make it look pretty.

    bar_width = 0.4
    ay.barh(y,x,bar_width,color='yellow')

    ay.tick_params(
            axis='x',          # changes apply to the x-axis
            which='both',      # both major and minor ticks are affected
            bottom=False,      # ticks along the bottom edge are off
            top=False,         # ticks along the top edge are off
            labelbottom=False
     ) 

    ay.set_yticklabels(y,fontsize=6,wrap=True)

    for tick in ay.yaxis.get_major_ticks():
        tick.label1.set_verticalalignment('center')

This is how it looks now :

在此处输入图片说明

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM