1+ import matplotlib .pyplot as plt
2+ import maxplotlib .subfigure .line_plot as lp
3+
4+ class Canvas :
5+ def __init__ (self , nrows = 1 , ncols = 1 , caption = None , description = None , label = None , figsize = (10 , 6 )):
6+ """
7+ Initialize the Canvas class for multiple subplots.
8+
9+ Parameters:
10+ nrows (int): Number of subplot rows. Default is 1.
11+ ncols (int): Number of subplot columns. Default is 1.
12+ figsize (tuple): Figure size.
13+ """
14+ self ._nrows = nrows
15+ self ._ncols = ncols
16+ self ._figsize = figsize
17+ self ._caption = caption
18+ self ._description = description
19+ self ._label = label
20+
21+ # Dictionary to store lines for each subplot
22+ # Key: (row, col), Value: list of lines with their data and kwargs
23+ self .subplots = {}
24+ self ._num_subplots = 0
25+
26+ self ._subplot_matrix = [[None ] * ncols for _ in range (nrows )]
27+
28+ # Property getters
29+ @property
30+ def nrows (self ):
31+ return self ._nrows
32+
33+ @property
34+ def ncols (self ):
35+ return self ._ncols
36+
37+ @property
38+ def caption (self ):
39+ return self ._caption
40+
41+ @property
42+ def description (self ):
43+ return self ._description
44+
45+ @property
46+ def label (self ):
47+ return self ._label
48+
49+ @property
50+ def figsize (self ):
51+ return self ._figsize
52+
53+ @property
54+ def subplot_matrix (self ):
55+ return self ._subplot_matrix
56+
57+ # Property setters
58+ @nrows .setter
59+ def nrows (self , value ):
60+ self ._nrows = value
61+
62+ @ncols .setter
63+ def ncols (self , value ):
64+ self ._ncols = value
65+
66+ @caption .setter
67+ def caption (self , value ):
68+ self ._caption = value
69+
70+ @description .setter
71+ def description (self , value ):
72+ self ._description = value
73+
74+ @label .setter
75+ def label (self , value ):
76+ self ._label = value
77+
78+ @figsize .setter
79+ def figsize (self , value ):
80+ self ._figsize = value
81+
82+ # Magic methods
83+ def __str__ (self ):
84+ return f"Canvas(nrows={ self .nrows } , ncols={ self .ncols } , figsize={ self .figsize } )"
85+
86+ def __repr__ (self ):
87+ return f"Canvas(nrows={ self .nrows } , ncols={ self .ncols } , caption={ self .caption } , label={ self .label } )"
88+
89+ def __getitem__ (self , key ):
90+ """Allows accessing subplots by tuple index."""
91+ row , col = key
92+ if row >= self .nrows or col >= self .ncols :
93+ raise IndexError ("Subplot index out of range" )
94+ return self ._subplot_matrix [row ][col ]
95+
96+ def __setitem__ (self , key , value ):
97+ """Allows setting a subplot by tuple index."""
98+ row , col = key
99+ if row >= self .nrows or col >= self .ncols :
100+ raise IndexError ("Subplot index out of range" )
101+ self ._subplot_matrix [row ][col ] = value
102+
103+ def add_subplot (self , col = None , row = None , label = None ):
104+ if row is None :
105+ for irow in range (self .nrows ):
106+ has_none = any (item is None for item in self ._subplot_matrix [irow ])
107+ if has_none :
108+ row = irow
109+ break
110+ assert row is not None , "Not enough rows!"
111+
112+ if col is None :
113+ for icol in range (self .ncols ):
114+ if self ._subplot_matrix [row ][icol ] is None :
115+ col = icol
116+ break
117+ assert col is not None , "Not enough columns!"
118+
119+ # Initialize the LinePlot for the given subplot position
120+ line_plot = lp .LinePlot ()
121+ self ._subplot_matrix [row ][col ] = line_plot
122+
123+ # Store the LinePlot instance by its position for easy access
124+ if label is None :
125+ self .subplots [(row , col )] = line_plot
126+ else :
127+ self .subplots [label ] = line_plot
128+ return line_plot
129+ def savefig (self , filename , backend = 'matplotlib' ):
130+ if backend == 'matplotlib' :
131+ fig = self .plot (show = False , savefig = True )
132+ fig .savefig (filename )
133+ #plt.savefig(filename)
134+ # def add_line(self, label, x_data, y_data, **kwargs):
135+
136+ def plot (self , backend = 'matplotlib' , show = True , savefig = False ):
137+ if backend == 'matplotlib' :
138+ return self .plot_matplotlib (show = show , savefig = savefig )
139+ def plot_matplotlib (self , show = True , savefig = False ):
140+ """
141+ Generate and optionally display the subplots.
142+
143+ Parameters:
144+ filename (str, optional): Filename to save the figure.
145+ show (bool): Whether to display the plot.
146+ """
147+ fig , axes = plt .subplots (self .nrows , self .ncols , figsize = self .figsize , squeeze = False )
148+
149+ for (row , col ), line_plot in self .subplots .items ():
150+ ax = axes [row ][col ]
151+ line_plot .plot (ax ) # Assuming LinePlot's `plot` method accepts an axis object
152+ ax .set_title (f"Subplot ({ row } , { col } )" )
153+
154+ # Set caption, labels, etc., if needed
155+ plt .tight_layout ()
156+
157+ if show :
158+ plt .show ()
159+ else :
160+ plt .close ()
161+ return fig
162+
163+ # def generate_matplotlib_code(self):
164+ # """Generate code for plotting the data using matplotlib."""
165+ # code = "import matplotlib.pyplot as plt\n\n"
166+ # code += f"fig, axes = plt.subplots({self.nrows}, {self.ncols}, figsize={self.figsize})\n\n"
167+ # if self.nrows == 1 and self.ncols == 1:
168+ # code += "axes = [axes] # Single subplot\n\n"
169+ # else:
170+ # code += "axes = axes.flatten()\n\n"
171+ # for idx, (subplot_idx, lines) in enumerate(self.subplots.items()):
172+ # code += f"# Subplot {subplot_idx}\n"
173+ # code += f"ax = axes[{idx}]\n"
174+ # for line in lines:
175+ # x_data = line['x']
176+ # y_data = line['y']
177+ # label = line['label']
178+ # kwargs = line.get('kwargs', {})
179+ # kwargs_str = ', '.join(f"{k}={repr(v)}" for k, v in kwargs.items())
180+ # code += f"ax.plot({x_data}, {y_data}, label={repr(label)}"
181+ # if kwargs_str:
182+ # code += f", {kwargs_str}"
183+ # code += ")\n"
184+ # code += "ax.set_xlabel('X-axis')\n"
185+ # code += "ax.set_ylabel('Y-axis')\n"
186+ # if self.nrows * self.ncols > 1:
187+ # code += f"ax.set_title('Subplot {subplot_idx}')\n"
188+ # code += "ax.legend()\n\n"
189+ # code += "plt.tight_layout()\nplt.show()\n"
190+ # return code
191+
192+ # def generate_latex_plot(self):
193+ # """Generate LaTeX code for plotting the data using pgfplots in subplots."""
194+ # latex_code = "\\begin{figure}[h!]\n\\centering\n"
195+ # total_subplots = self.nrows * self.ncols
196+ # for idx in range(total_subplots):
197+ # subplot_idx = divmod(idx, self.ncols)
198+ # lines = self.subplots.get(subplot_idx, [])
199+ # if not lines:
200+ # continue # Skip empty subplots
201+ # latex_code += "\\begin{subfigure}[b]{0.45\\textwidth}\n"
202+ # latex_code += " \\begin{tikzpicture}\n"
203+ # latex_code += " \\begin{axis}[\n"
204+ # latex_code += " xlabel={X-axis},\n"
205+ # latex_code += " ylabel={Y-axis},\n"
206+ # if self.nrows * self.ncols > 1:
207+ # latex_code += f" title={{Subplot {subplot_idx}}},\n"
208+ # latex_code += " legend style={at={(1.05,1)}, anchor=north west},\n"
209+ # latex_code += " legend entries={" + ", ".join(f"{{{line['label']}}}" for line in lines) + "}\n"
210+ # latex_code += " ]\n"
211+ # for line in lines:
212+ # options = []
213+ # kwargs = line.get('kwargs', {})
214+ # if 'color' in kwargs:
215+ # options.append(f"color={kwargs['color']}")
216+ # if 'linestyle' in kwargs:
217+ # linestyle_map = {'-': 'solid', '--': 'dashed', '-.': 'dash dot', ':': 'dotted'}
218+ # linestyle = linestyle_map.get(kwargs['linestyle'], kwargs['linestyle'])
219+ # options.append(f"style={linestyle}")
220+ # options_str = f"[{', '.join(options)}]" if options else ""
221+ # latex_code += f" \\addplot {options_str} coordinates {{\n"
222+ # for x, y in zip(line['x'], line['y']):
223+ # latex_code += f" ({x}, {y})\n"
224+ # latex_code += " };\n"
225+ # latex_code += " \\end{axis}\n"
226+ # latex_code += " \\end{tikzpicture}\n"
227+ # latex_code += "\\end{subfigure}\n"
228+ # latex_code += "\\hfill\n" if (idx + 1) % self.ncols != 0 else "\n"
229+ # latex_code += "\\caption{Multiple Subplots}\n"
230+ # latex_code += "\\end{figure}\n"
231+ # return latex_code
232+
233+
234+ if __name__ == '__main__' :
235+ c = Canvas (ncols = 2 ,nrows = 2 )
236+ sp = c .add_subplot ()
237+ sp .add_line ("Line 1" , [0 , 1 , 2 , 3 ], [0 , 1 , 4 , 9 ])
238+ c .plot ()
239+ print ('done' )
0 commit comments