如何用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
相关产品推荐
相关产品推荐

