You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Matplotlib将Job Size列堆叠为单柱并设置内存相关Y轴范围?

问题描述

现有示例DataFrame及系统参数如下:

Job Number  Job Size  Arrival Time  Runtime  Finish Time  Wait Time
0           1       135           112     1252         1364          0
1           2        43           112     1252         2616       1252
2           3       123           112     1252         3868       2504
OS Size: 24
Memory Size:  1024

需求:使用Matplotlib将Job Size列的内容堆叠为单个柱状图,以Memory Size为Y轴上限、0为下限,同时在图中体现OS Size。预期效果如下:

___ 1024
24 (OS Size)
135
43
123
___ 0

尝试用ax.set_ylim = (0, memory_size)设置Y轴范围无效,当前Y轴最大值为所有Job Size的总和。相关Python GUI代码如下:

def mainInput1_window(self):
    self.clearWidgets()
    self.basicWidgetList = []

    self.entry_frame = Frame(root, bg="black")
    self.entry_frame.place(x=50, y=110)
    self.basicWidgetList.append(self.entry_frame)

    # 添加顶部图片
    self.header = Button(root, image=image_4, bg="#000000", relief="flat", borderwidth=0)
    self.header.place(x=0, y=0)
    self.basicWidgetList.append(self.header)

    self.os_sizeLBL = Label(root, text="OS Size", font=('Poppins', 18, 'bold'), bg="#d3a075", height=2, width=19, borderwidth=4, relief="raised")
    self.os_sizeLBL.place(x=90, y=180)
    self.basicWidgetList.append(self.os_sizeLBL)

    self.entry_os_size = Entry(root, font=('Poppins', 30, 'bold'), justify="center", width=18)
    self.entry_os_size.place(x=390, y=180)
    self.basicWidgetList.append(self.entry_os_size)

    self.memory_sizeLBL = Label(root, text="Memory Size", font=('Poppins', 18, 'bold'), bg="#ecd3bf", height=2, width=19, borderwidth=4, relief="raised")
    self.memory_sizeLBL.place(x=90, y=250)
    self.basicWidgetList.append(self.memory_sizeLBL)

    self.entry_memory_size = Entry(root, font=('Poppins', 30, 'bold'), justify="center", width = 18)
    self.entry_memory_size.place(x=390, y=250)
    self.basicWidgetList.append(self.entry_memory_size)

    self.job_numLBL = Label(root, text="Number of Jobs", font=('Poppins', 18, 'bold'), bg="#eec894", height=2, width=19, borderwidth=4, relief="raised")
    self.job_numLBL.place(x=90, y=320)
    self.basicWidgetList.append(self.job_numLBL)

    self.entry_job_num = Entry(root, font=('Poppins', 30, 'bold'), justify="center", width = 18)
    self.entry_job_num.place(x=390, y=320)
    self.basicWidgetList.append(self.entry_job_num)

    self.job_entries = []
    self.job_frame = Frame(self.entry_frame, bg="black")
    self.job_frame.grid(row=6, columnspan=5)
    self.basicWidgetList.append(self.job_frame)

    self.set_jobs_button = Button(root, text="Set Jobs", command=self.create_job_entries, font=('Poppins', 18, 'bold'), width=20, bg="#b8804c", height=2, borderwidth=4, relief="sunken")
    self.set_jobs_button.place(x=330, y=450)
    self.basicWidgetList.append(self.set_jobs_button)

    self.back_button = Button(root, text="Back", command = self.mainscreen,
        font =('Poppins', 18, 'bold'), fg = 'white', width=20, bg = '#4b3621', height = 2, borderwidth = 4, relief = 'sunken')

    self.back_button.place(x = 328, y = 550)
    self.basicWidgetList.append(self.back_button)

def hide_initial_inputs(self):
    self.os_sizeLBL.place_forget()
    self.entry_os_size.place_forget()
    self.memory_sizeLBL.place_forget()
    self.entry_memory_size.place_forget()
    self.job_numLBL.place_forget()
    self.entry_job_num.place_forget()
    self.set_jobs_button.place_forget()
    self.header.place_forget()

def create_job_entries(self):
    try:
        num_jobs = int(self.entry_job_num.get())
        for widget in self.job_frame.winfo_children():
            widget.destroy()
        self.job_entries.clear()

        self.hide_initial_inputs()

        for i in range(num_jobs):
            Label(self.job_frame, text=f"Job {i + 1}", font=('Poppins', 10, 'bold'), bg="#000000", fg="#FFFFFF", width=9, relief="flat").grid(row=i, column=0)
            Label(self.job_frame, text=f"Job Size:", font=('Poppins', 9, 'bold'), bg="#D3A075", width=12, relief="groove").grid(row=i, column=1, padx=0)
            entry_job_size = Entry(self.job_frame, font=('Poppins', 10, 'bold'), justify="center", width=15)
            entry_job_size.grid(row=i, column=2, padx=20)
            Label(self.job_frame, text=f" Arrival Time (ms):", font=('Poppins', 9, 'bold'), bg="#ECD3BF", width=15, relief="groove").grid(row=i, column=3, padx=0)
            entry_arrival = Entry(self.job_frame, font=('Poppins', 10, 'bold'), justify="center", width=15)
            entry_arrival.grid(row=i, column=4, padx=20)
            Label(self.job_frame, text=f"Run Time (ms):", font=('Poppins', 9, 'bold'), bg="#EEC894", width=15, relief="groove").grid(row=i, column=5, padx=0)
            entry_runtime = Entry(self.job_frame, font=('Poppins', 10, 'bold'), justify="center", width=15)
            entry_runtime.grid(row=i, column=6, padx=20)
            self.job_entries.append((entry_job_size, entry_arrival, entry_runtime))

        # 创建计算按钮
        self.compute_button = Button(root, text="Show Summary Table and Memory Map", command=self.summarytable, font=('Poppins', 18, 'bold'), width=30, bg="#b8804c", height=1, borderwidth=4, relief="sunken")
        self.compute_button.place(x=260, y=30)
        self.basicWidgetList.append(self.compute_button)

    except ValueError:
        messagebox.showerror("Error", "Please enter a valid number of jobs.")


def summarytable(self):
    try:
        os_size = int(self.entry_os_size.get())
        memory_size = int(self.entry_memory_size.get())
        jobs = []

        for entry_job_size, entry_arrival, entry_runtime in self.job_entries:
            job_size = int(entry_job_size.get())
            arrival_time = int(entry_arrival.get())
            runtime = int(entry_runtime.get())
            jobs.append({"Job Number": len(jobs) + 1, "Job Size": job_size, "Arrival Time": arrival_time, "Runtime": runtime})

        # 按到达时间排序任务
        jobs = sorted(jobs, key=lambda x: x["Arrival Time"])

        current_time = 0
        for job in jobs:
            job["Finish Time"] = max(current_time, job["Arrival Time"]) + job["Runtime"]
            job["Wait Time"] = max(current_time - job["Arrival Time"], 0)
            current_time = job["Finish Time"]

        # 创建DataFrame
        df = pd.DataFrame(jobs)
        self.display_table(df)
        print(df)

    except ValueError:
        messagebox.showerror("Error", "Please enter valid integers for OS size, Memory size, and job details.")

def display_table(self, df):
    self.clearWidgets()
    self.basicWidgetList = []

    self.sumtablelabel = Label(root, text="Summary Table", font=('Poppins', 18, 'bold'), bg="#ecd3bf", height=2, width=19,
                                borderwidth=4, relief="raised")
    self.sumtablelabel.place(x=140, y=15)
    self.basicWidgetList.append(self.sumtablelabel)

    self.memmaplabel = Label(root, text="Memory Map", font=('Poppins', 18, 'bold'), bg="#ecd3bf", height=2,
                               width=19,
                               borderwidth=4, relief="raised")
    self.memmaplabel.place(x=600, y=15)
    self.basicWidgetList.append(self.memmaplabel)
    # 添加表头
    headers = ["Job Number", "Job Size", "Arrival Time", "Runtime", "Finish Time", "Wait Time"]
    for col_num, header in enumerate(headers):
        header_label = Label(root, text=header, font=('Poppins', 10, 'bold'), bg="#eec894", width=10, borderwidth=4, relief="raised")
        header_label.grid(row=0, column=col_num, padx=(7,3), pady= (85,1))
        self.basicWidgetList.append(header_label)

    # 添加表格内容
    for row_num, row in df.iterrows():
        for col_num, value in enumerate(row):
            cell_label = Label(root, text=value, font=('Poppins', 10, 'bold'), bg="#FFFFFF", width=10, borderwidth=4, relief="raised")
            cell_label.grid(row=row_num + 1, column=col_num, padx=(7,3))
            self.basicWidgetList.append(cell_label)
解决方案

1. 修复Y轴范围设置错误

你之前的写法ax.set_ylim = (0, memory_size)是赋值操作,而set_ylim是Matplotlib轴对象的方法,必须通过调用生效:

ax.set_ylim(0, memory_size)

2. 实现内存堆叠图(嵌入Tkinter)

需要修改现有代码,添加Matplotlib绘图逻辑并嵌入到GUI的Memory Map区域:

步骤1:导入必要库

在代码开头添加:

import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import numpy as np

步骤2:保存系统参数到实例变量

修改summarytable方法,在创建DataFrame后保存os_size和memory_size:

def summarytable(self):
    try:
        os_size = int(self.entry_os_size.get())
        memory_size = int(self.entry_memory_size.get())
        # ... 原有代码 ...
        df = pd.DataFrame(jobs)
        # 保存参数供绘图使用
        self.os_size = os_size
        self.memory_size = memory_size
        self.display_table(df)
        # ... 原有代码 ...
    except ValueError:
        # ... 原有代码 ...

步骤3:在display_table中添加绘图逻辑

在display_table方法的末尾(表格绘制完成后)添加以下代码:

def display_table(self, df):
    # ... 原有表格绘制代码 ...

    # 绘制内存堆叠图
    fig, ax = plt.subplots(figsize=(5, 8))
    job_sizes = df['Job Size'].tolist()
    
    # 按预期效果顺序整理堆叠数据(从底部到顶部:最后一个Job → 第一个Job → OS → 空闲内存)
    stack_data = job_sizes[::-1] + [self.os_size]
    stack_labels = [f"Job {i+1}" for i in reversed(range(len(job_sizes)))] + ["OS Size"]
    
    # 计算空闲内存(若总占用小于内存大小)
    total_used = sum(stack_data)
    free_memory = self.memory_size - total_used
    if free_memory > 0:
        stack_data.append(free_memory)
        stack_labels.append("空闲内存")

    # 绘制单柱堆叠图
    bottom = 0
    for size, label in zip(stack_data, stack_labels):
        ax.bar(0, size, bottom=bottom, label=label)
        bottom += size

    # 设置Y轴范围(正确调用方法)
    ax.set_ylim(0, self.memory_size)
    # 设置Y轴刻度,显示各段边界值
    ax.set_yticks([0] + list(np.cumsum(stack_data)))
    ax.set_yticklabels([0] + stack_data)
    # 隐藏X轴刻度(仅需单柱子)
    ax.set_xticks([])
    # 添加图例到图外
    ax.legend(bbox_to_anchor=(1.05, 1), loc='upper left')
    # 设置图表标题
    ax.set_title("内存分配图")

    # 将图表嵌入Tkinter窗口
    canvas = FigureCanvasTkAgg(fig, master=root)
    canvas.draw()
    canvas.get_tk_widget().place(x=600, y=100)
    self.basicWidgetList.append(canvas.get_tk_widget())

效果说明

  • 堆叠柱从底部0开始,依次显示最后一个Job、前一个Job...直到第一个Job,然后是OS,最后是空闲内存
  • Y轴被严格限制在0到Memory Size之间,不会随总Job Size变化
  • 图例清晰标注各部分,Y轴刻度显示每段的大小和边界

内容的提问来源于stack exchange,提问作者Dirk

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 14:24:51