1

I have seen multiple, multiple questions about how to stop a matplotlib figure from staying open and blocking other code from working. None of the accepted answers have worked for me. I have tried:

plot.show(block=False) plot.close() plot.close('all') plot.close(fig)

The only thing that works is plot.show(block=False), however it closes the graph as soon as it is open.

Next, I tried using plot.show(block=False) followed by a request for input using input("Press enter to continue") to delay the closing of the plot. This only resulted in a blank figure being displayed, which does actually close when I hit enter.

Normally, just closing the plot window with the 'x' button does the trick just fine. However, in this case when I close the window the program just hangs until I tell my IDE to shut it down.

EDIT: I have also tried the solution given in the comments. It is the worst of all the offendors because it renders my computer frozen unless I force close the plot.

Here is the code that is producing the behavior:

import birth_names as bn
import matplotlib.pyplot as plt


    def plotRankAndScores(name, gender):
        files = bn.getPaths()
        x1, y1 = bn.getAllRanks(name, gender, files)
        x2, y2 = bn.getAllScores(name, gender, files)
        ave = bn.getAverageRank(name, gender, select=False, filez=files)

        fig2, (ax3, ax4) = plt.subplots(2, 1, sharex='all', figsize=(10, 10))
        plt.xlabel("Year")
        ax3.plot(x1, y1, 'b')
        ax3.set_ylabel("Rank")
        ax3.axhline(y1.mean(), label='average = {}'.format(ave), linestyle='--', color='red')
        ax3.legend()
        ax4.plot(x2, y2, 'b')
        ax4.set_ylabel("Number of Births")
        ax4.axhline(y2.mean(), label='average = {}'.format(y2.mean()), linestyle='--', color='red')
        ax4.legend()
        plt.suptitle("Name Rank and Number of Births by Year")
        plt.show(block=False)


    if __name__ == '__main__':
        plotRankAndScores("Wesley", "M")
        input("Press Enter to end display")
        print("All Done!")

And, in case the problem is not related to my use of matplotlib, I will provide the code for birth_names.py. I have deleted all the functions that birthplots.py does not use. Unfortunately it is still a long list of functions:

import tkinter as tk
import os
import csv
import numpy as np


def parseFileYear(file_name):
    """
    :param file_name: file name in the format yobXXXX.csv where XXXX is the four digit year.
    :return: the year
    """
    return file_name[3:7]


def totalScore(path):
    """
    Reads the CSV file at location 'path' and prints out the number of boys and girls born
    along with the number of unique boy names and unique girl names.
    :param path: The CSV file containing birth name data
    :return:
    """
    boys = 0
    girls = 0
    boyNames = 0
    girlNames = 0

    with open(path, 'r') as csvFile:
        reader = csv.reader(csvFile)
        for row in reader:
            if row[1] == "M":
                boys += int(row[2])
                boyNames += 1
            else:
                girls += int(row[2])
                girlNames += 1

    totalNames = boyNames + girlNames
    print("{} boys were born with {} unique names".format(boys, boyNames))
    print("{} girls were born with {} unique names".format(girls, girlNames))
    print("Total unique names: ", totalNames, '\n')    


def getScore(year, name, gender):
    file_name = "yob{}.csv".format(year)
    curr_path = os.path.dirname(__file__)
    abs_path = curr_path + "/us_babynames_by_year/{}".format(file_name)

    with open(abs_path, 'r') as CSVdat:
        for row in csv.reader(CSVdat):
            if row[1] == gender and row[0] == name:
                return row[2]

    return -1


def getRank(year, name, gender):
    """
    This method takes a year, name and gender and returns the rank of that name in the given year. If the name does not
    exist, the method returns -1
    :param year:
    :param name:
    :param gender:
    :return:
    """
    file_name = "yob{}.csv".format(year)
    curr_path = os.path.dirname(__file__)
    abs_path = curr_path + "/us_babynames_by_year/{}".format(file_name)

    last_count = 0
    rank = 0
    name_found = False
    with open(abs_path, 'r') as CSVdat:
        for row in csv.reader(CSVdat):
            if row[1] == gender:
                name_count = int(row[2])

                if name_count == last_count:
                    last_count = name_count
                else:
                    rank += 1
                    last_count = name_count
                if row[0] == name:
                    name_found = True
                    break
    if not name_found:
        return -1
    return rank


def getAverageRank(name, gender, select=True, filez=None):
    """
    return the average rank of a given name over the selected files. If using
    this method in conjunction with others that are analyzing multiple CSV
    files, it is prudent to use select=False, and pass an iterable containing
    the absolute paths of the files being looked at. This will save you from
    dealing with multiple pop-up file selection dialogues.
    :param name:
    :param gender:
    :param select: default True. If True, pop up file selection dialogue.
    If false, the files argument must not be empty.
    :param files: default None. An iterable containing the absolute paths
    to the CSV files being analyzed. This parameter is required if param
    select is False.
    :return:
    """
    if select:
        files = getPaths()
    else:
        files = filez

    name_found = False
    rank_sum = 0
    count = len(files)
    for file in files:
        file_name = os.path.basename(file)
        year = parseFileYear(file_name)
        rank = getRank(year, name, gender)
        if rank != -1:
            name_found = True
            rank_sum += rank

    if name_found:
        return rank_sum / count
    return -1


def getAllRanks(name, gender, files):
    """
    Returns a numpy 2-D array containing the year and rank pairs. Useful for plotting visualizations
    :param name:
    :param gender:
    :param files:
    :return:
    """
    dim = len(files)
    year_data = np.zeros(dim, dtype=int)
    rank_data = np.zeros(dim, dtype=int)
    for i, file in enumerate(files):
        file_name = os.path.basename(file)
        year = parseFileYear(file_name)
        rank = getRank(year, name, gender)
        if rank == -1: # clean -1 from data (case of name not found in file)
            rank = 0
        year_data[i] = year
        rank_data[i] = rank

    return year_data, rank_data


def getPaths():
    root = tk.Tk()
    root.withdraw()
    return tk.filedialog.askopenfilenames()


def getAllScores(name, gender, files):
    dim = len(files)
    year_data = np.zeros(dim, dtype=int)
    score_data = np.zeros(dim, dtype=int)
    for i, file in enumerate(files):
        year = parseFileYear(os.path.basename(file))
        score = getScore(year, name, gender)
        year_data[i] = year
        score_data[i] = score

    return year_data, score_data
rocksNwaves
  • 5,331
  • 4
  • 38
  • 77

0 Answers0