emayecs | 5656b2b | 2021-08-04 12:44:13 -0400 | [diff] [blame] | 1 | #!/usr/bin/env python3 |
emayecs | 5966a53 | 2021-07-29 10:07:02 -0400 | [diff] [blame] | 2 | # |
| 3 | #----------------------------------------------------------- |
emayecs | 1474831 | 2021-08-05 14:21:26 -0400 | [diff] [blame] | 4 | # Parameter editing for the characterization tool |
emayecs | 5966a53 | 2021-07-29 10:07:02 -0400 | [diff] [blame] | 5 | #----------------------------------------------------------- |
| 6 | # Written by Tim Edwards |
| 7 | # efabless, inc. |
| 8 | # March 28, 2017 |
| 9 | # Version 0.1 |
| 10 | #-------------------------------------------------------- |
| 11 | |
| 12 | import os |
| 13 | import re |
| 14 | import tkinter |
| 15 | from tkinter import ttk |
| 16 | |
| 17 | class Condition(object): |
| 18 | def __init__(self, parent = None): |
| 19 | self.min = tkinter.StringVar(parent) |
| 20 | self.typ = tkinter.StringVar(parent) |
| 21 | self.max = tkinter.StringVar(parent) |
| 22 | self.step = tkinter.StringVar(parent) |
| 23 | self.steptype = tkinter.StringVar(parent) |
| 24 | self.unit = tkinter.StringVar(parent) |
| 25 | self.condition = tkinter.StringVar(parent) |
| 26 | self.display = tkinter.StringVar(parent) |
| 27 | |
| 28 | class Limit(object): |
| 29 | def __init__(self, parent = None): |
| 30 | self.target = tkinter.StringVar(parent) |
| 31 | self.penalty = tkinter.StringVar(parent) |
| 32 | self.calc = tkinter.StringVar(parent) |
| 33 | self.limit = tkinter.StringVar(parent) |
| 34 | |
| 35 | class EditParam(tkinter.Toplevel): |
| 36 | """Characterization tool electrical parameter editor.""" |
| 37 | |
| 38 | def __init__(self, parent=None, fontsize = 11, *args, **kwargs): |
| 39 | '''See the __init__ for Tkinter.Toplevel.''' |
| 40 | tkinter.Toplevel.__init__(self, parent, *args, **kwargs) |
| 41 | |
| 42 | s = ttk.Style() |
| 43 | s.configure('normal.TButton', font=('Helvetica', fontsize), border = 3, relief = 'raised') |
| 44 | s.configure('bg.TFrame', background='gray40') |
| 45 | self.parent = parent |
| 46 | self.withdraw() |
| 47 | self.title('Electrical parameter editor') |
| 48 | self.sframe = tkinter.Frame(self) |
| 49 | self.sframe.grid(column = 0, row = 0, sticky = "news") |
| 50 | |
| 51 | # Keep current parameter |
| 52 | self.param = None |
| 53 | |
| 54 | #------------------------------------------------------------- |
| 55 | # Add the entries that are common to all electrical parameters |
| 56 | |
| 57 | self.selmethod = tkinter.StringVar(self) |
| 58 | self.display = tkinter.StringVar(self) |
| 59 | self.unit = tkinter.StringVar(self) |
| 60 | self.minrec = Limit(self) |
| 61 | self.typrec = Limit(self) |
| 62 | self.maxrec = Limit(self) |
| 63 | self.cond = [] |
| 64 | |
| 65 | #-------------------------------------------------------- |
| 66 | |
| 67 | self.bbar = ttk.Frame(self) |
| 68 | self.bbar.grid(column = 0, row = 2, sticky = "news") |
| 69 | |
| 70 | self.bbar.apply_button = ttk.Button(self.bbar, text='Apply', |
| 71 | command=self.apply, style = 'normal.TButton') |
| 72 | self.bbar.apply_button.grid(column=0, row=0, padx = 5) |
| 73 | |
| 74 | self.bbar.close_button = ttk.Button(self.bbar, text='Close', |
| 75 | command=self.close, style = 'normal.TButton') |
| 76 | self.bbar.close_button.grid(column=1, row=0, padx = 5) |
| 77 | |
| 78 | self.rowconfigure(0, weight = 1) |
| 79 | self.rowconfigure(1, weight = 0) |
| 80 | self.columnconfigure(0, weight = 1) |
| 81 | |
| 82 | self.protocol("WM_DELETE_WINDOW", self.close) |
| 83 | |
| 84 | def grid_configure(self, padx, pady): |
| 85 | return |
| 86 | |
| 87 | def redisplay(self): |
| 88 | return |
| 89 | |
| 90 | def populate(self, param): |
| 91 | # Remove all existing contents |
| 92 | for widget in self.sframe.winfo_children(): |
| 93 | widget.destroy() |
| 94 | |
| 95 | # Add major frames |
| 96 | |
| 97 | frame1 = ttk.Frame(self.sframe) |
| 98 | frame1.grid(column = 0, row = 0, sticky = 'news') |
| 99 | frame2 = ttk.Frame(self.sframe) |
| 100 | frame2.grid(column = 0, row = 1, sticky = 'news') |
| 101 | frame3 = ttk.Frame(self.sframe) |
| 102 | frame3.grid(column = 0, row = 2, sticky = 'news') |
| 103 | |
| 104 | # The Conditions area is the one that grows |
| 105 | self.sframe.rowconfigure(2, weight=1) |
| 106 | self.sframe.columnconfigure(0, weight=1) |
| 107 | |
| 108 | ttk.Separator(frame3, orient='horizontal').grid(row = 0, column = 0, |
| 109 | sticky = 'news') |
| 110 | |
| 111 | # The conditions list can get very big, so build out a |
| 112 | # scrolled canvas. |
| 113 | |
| 114 | frame3.canvas = tkinter.Canvas(frame3) |
| 115 | frame3.canvas.grid(row = 1, column = 0, sticky = 'nswe') |
| 116 | frame3.canvas.dframe = ttk.Frame(frame3.canvas, style='bg.TFrame') |
| 117 | # Save the canvas widget, as we need to access it from places like |
| 118 | # the scrollbar callbacks. |
| 119 | self.canvas = frame3.canvas |
| 120 | # Place the frame in the canvas |
| 121 | frame3.canvas.create_window((0,0), window=frame3.canvas.dframe, |
| 122 | anchor="nw") |
| 123 | # Make sure the main window resizes, not the scrollbars. |
| 124 | frame3.rowconfigure(1, weight=1) |
| 125 | frame3.columnconfigure(0, weight = 1) |
| 126 | # X scrollbar for conditions list |
| 127 | main_xscrollbar = ttk.Scrollbar(frame3, orient = 'horizontal') |
| 128 | main_xscrollbar.grid(row = 2, column = 0, sticky = 'nswe') |
| 129 | # Y scrollbar for conditions list |
| 130 | main_yscrollbar = ttk.Scrollbar(frame3, orient = 'vertical') |
| 131 | main_yscrollbar.grid(row = 1, column = 1, sticky = 'nswe') |
| 132 | # Attach console to scrollbars |
| 133 | frame3.canvas.config(xscrollcommand = main_xscrollbar.set) |
| 134 | main_xscrollbar.config(command = frame3.canvas.xview) |
| 135 | frame3.canvas.config(yscrollcommand = main_yscrollbar.set) |
| 136 | main_yscrollbar.config(command = frame3.canvas.yview) |
| 137 | |
| 138 | # Make sure that scrollwheel pans the window |
| 139 | frame3.canvas.bind_all("<Button-4>", self.on_mousewheel) |
| 140 | frame3.canvas.bind_all("<Button-5>", self.on_mousewheel) |
| 141 | |
| 142 | # Set up configure callback |
| 143 | frame3.canvas.dframe.bind("<Configure>", self.frame_configure) |
| 144 | |
| 145 | # Get list of methods from testbench folder |
| 146 | dspath = os.path.split(self.parent.cur_datasheet)[0] |
| 147 | tbpath = dspath + '/testbench' |
| 148 | tbfiles = os.listdir(tbpath) |
| 149 | methods = [] |
| 150 | for spifile in tbfiles: |
| 151 | if os.path.splitext(spifile)[1] == '.spi': |
| 152 | methods.append(os.path.splitext(spifile)[0].upper()) |
| 153 | |
| 154 | # Get list of pins from parent datasheet |
| 155 | dsheet = self.parent.datatop['data-sheet'] |
| 156 | pins = dsheet['pins'] |
| 157 | pinlist = [] |
| 158 | for pin in pins: |
| 159 | pinlist.append(pin['name']) |
| 160 | pinlist.append('(none)') |
| 161 | |
| 162 | # Add common elements |
| 163 | frame1.ldisplay = ttk.Label(frame1, text='Description:', |
| 164 | style= 'blue.TLabel', anchor = 'e') |
| 165 | frame1.lmethod = ttk.Label(frame1, text='Method:', |
| 166 | style= 'blue.TLabel', anchor = 'e') |
| 167 | frame1.lunit = ttk.Label(frame1, text='Unit:', |
| 168 | style= 'blue.TLabel', anchor = 'e') |
| 169 | |
| 170 | # If 'pin' exists (old style), append it to 'condition' and remove. |
| 171 | if 'pin' in param: |
| 172 | if 'method' in param and ':' not in param['method']: |
| 173 | param['method'] += ':' + param['pin'] |
| 174 | param.pop('pin', 0) |
| 175 | |
| 176 | # Find method and apply to OptionMenu |
| 177 | if 'method' in param: |
| 178 | self.selmethod.set(param['method']) |
| 179 | else: |
| 180 | self.selmethod.set('(none)') |
| 181 | |
| 182 | |
| 183 | frame1.display = ttk.Entry(frame1, textvariable = self.display) |
| 184 | frame1.method = ttk.OptionMenu(frame1, self.selmethod, self.selmethod.get(), *methods) |
| 185 | frame1.unit = ttk.Entry(frame1, textvariable = self.unit) |
| 186 | |
| 187 | frame1.ldisplay.grid(column = 0, row = 0, sticky = 'news', padx=5, pady=5) |
| 188 | frame1.display.grid(column = 1, row = 0, sticky = 'news', padx=5, pady=3) |
| 189 | frame1.lmethod.grid(column = 0, row = 1, sticky = 'news', padx=5, pady=5) |
| 190 | frame1.method.grid(column = 1, row = 1, sticky = 'news', padx=5, pady=3) |
| 191 | frame1.lunit.grid(column = 0, row = 2, sticky = 'news', padx=5, pady=5) |
| 192 | frame1.unit.grid(column = 1, row = 2, sticky = 'news', padx=5, pady=3) |
| 193 | |
| 194 | frame1.columnconfigure(0, weight = 0) |
| 195 | frame1.columnconfigure(1, weight = 1) |
| 196 | |
| 197 | frame1.display.delete(0, 'end') |
| 198 | if 'display' in param: |
| 199 | frame1.display.insert(0, param['display']) |
| 200 | else: |
| 201 | frame1.display.insert(0, '(none)') |
| 202 | |
| 203 | frame1.unit.delete(0, 'end') |
| 204 | if 'unit' in param: |
| 205 | frame1.unit.insert(0, param['unit']) |
| 206 | else: |
| 207 | frame1.unit.insert(0, '(none)') |
| 208 | |
| 209 | ttk.Separator(frame1, orient='horizontal').grid(row = 4, column = 0, |
| 210 | columnspan = 2, sticky = 'nsew') |
| 211 | |
| 212 | # Calculation types |
| 213 | calctypes = ["min", "max", "avg", "diffmin", "diffmax", "(none)"] |
| 214 | limittypes = ["above", "below", "exact", "legacy", "(none)"] |
| 215 | |
| 216 | # Add min/typ/max (To-do: Add plot) |
| 217 | |
| 218 | frame2min = ttk.Frame(frame2, borderwidth = 2, relief='groove') |
| 219 | frame2min.grid(row = 0, column = 0, padx = 2, pady = 2, sticky = 'news') |
| 220 | ttk.Label(frame2min, text="Minimum:", style = 'blue.TLabel', |
| 221 | anchor = 'w').grid(row = 0, column = 0, padx = 5, |
| 222 | sticky = 'news') |
| 223 | if 'min' in param: |
| 224 | minrec = param['min'] |
| 225 | else: |
| 226 | minrec = {} |
| 227 | ttk.Label(frame2min, text="Target:", anchor = 'e', |
| 228 | style = 'normal.TLabel').grid(row = 1, column = 0, padx = 5, |
| 229 | sticky = 'news') |
| 230 | frame2min.tmin = ttk.Entry(frame2min, textvariable = self.minrec.target) |
| 231 | frame2min.tmin.grid(row = 1, column = 1, padx = 5, sticky = 'news') |
| 232 | frame2min.tmin.delete(0, 'end') |
| 233 | if 'target' in minrec: |
| 234 | frame2min.tmin.insert(0, minrec['target']) |
| 235 | ttk.Label(frame2min, text="Penalty:", anchor = 'e', |
| 236 | style = 'normal.TLabel').grid(row = 2, column = 0, padx = 5, |
| 237 | sticky = 'news') |
| 238 | frame2min.pmin = ttk.Entry(frame2min, textvariable = self.minrec.penalty) |
| 239 | frame2min.pmin.grid(row = 2, column = 1, padx = 5, sticky = 'news') |
| 240 | frame2min.pmin.delete(0, 'end') |
| 241 | if 'penalty' in minrec: |
| 242 | frame2min.pmin.insert(0, minrec['penalty']) |
| 243 | if 'calc' in minrec: |
| 244 | calcrec = minrec['calc'] |
| 245 | try: |
| 246 | calctype, limittype = calcrec.split('-') |
| 247 | except ValueError: |
| 248 | calctype = calcrec |
| 249 | if calctype == 'min': |
| 250 | limittype = 'above' |
| 251 | elif calctype == 'max': |
| 252 | limittype = 'below' |
| 253 | elif calctype == 'avg': |
| 254 | limittype = 'exact' |
| 255 | elif calctype == 'diffmin': |
| 256 | limittype = 'above' |
| 257 | elif calctype == 'diffmax': |
| 258 | limittype = 'below' |
| 259 | else: |
| 260 | limittype = '(none)' |
| 261 | else: |
| 262 | calctype = 'min' |
| 263 | limittype = 'above' |
| 264 | |
| 265 | ttk.Label(frame2min, text="Calculation:", anchor = 'e', |
| 266 | style = 'normal.TLabel').grid(row = 3, column = 0, padx = 5, |
| 267 | sticky = 'news') |
| 268 | self.cmin = tkinter.StringVar(self) |
| 269 | self.cmin.set(calctype) |
| 270 | frame2min.cmin = ttk.OptionMenu(frame2min, self.cmin, calctype, *calctypes) |
| 271 | frame2min.cmin.grid(row = 3, column = 1, padx = 5, sticky = 'news') |
| 272 | ttk.Label(frame2min, text="Limit:", anchor = 'e', |
| 273 | style = 'normal.TLabel').grid(row = 4, column = 0, padx = 5, |
| 274 | sticky = 'news') |
| 275 | self.lmin = tkinter.StringVar(self) |
| 276 | self.lmin.set(limittype) |
| 277 | frame2min.lmin = ttk.OptionMenu(frame2min, self.lmin, limittype, *limittypes) |
| 278 | frame2min.lmin.grid(row = 4, column = 1, padx = 5, sticky = 'news') |
| 279 | |
| 280 | frame2typ = ttk.Frame(frame2, borderwidth = 2, relief='groove') |
| 281 | frame2typ.grid(row = 0, column = 1, padx = 2, pady = 2, sticky = 'news') |
| 282 | ttk.Label(frame2typ, text="Typical:", style = 'blue.TLabel', |
| 283 | anchor = 'w').grid(row = 0, column = 0, padx = 5, |
| 284 | sticky = 'news') |
| 285 | if 'typ' in param: |
| 286 | typrec = param['typ'] |
| 287 | else: |
| 288 | typrec = {} |
| 289 | ttk.Label(frame2typ, text="Target:", anchor = 'e', |
| 290 | style = 'normal.TLabel').grid(row = 1, column = 0, padx = 5, |
| 291 | sticky = 'news') |
| 292 | frame2typ.ttyp = ttk.Entry(frame2typ, textvariable = self.typrec.target) |
| 293 | frame2typ.ttyp.grid(row = 1, column = 1, padx = 5, sticky = 'news') |
| 294 | frame2typ.ttyp.delete(0, 'end') |
| 295 | if 'target' in typrec: |
| 296 | frame2typ.ttyp.insert(0, typrec['target']) |
| 297 | ttk.Label(frame2typ, text="Penalty:", anchor = 'e', |
| 298 | style = 'normal.TLabel').grid(row = 2, column = 0, padx = 5, |
| 299 | sticky = 'news') |
| 300 | frame2typ.ptyp = ttk.Entry(frame2typ, textvariable = self.typrec.penalty) |
| 301 | frame2typ.ptyp.grid(row = 2, column = 1, padx = 5, sticky = 'news') |
| 302 | frame2typ.ptyp.delete(0, 'end') |
| 303 | if 'penalty' in typrec: |
| 304 | frame2typ.ptyp.insert(0, typrec['penalty']) |
| 305 | if 'calc' in typrec: |
| 306 | calcrec = typrec['calc'] |
| 307 | try: |
| 308 | calctype, limittype = calcrec.split('-') |
| 309 | except ValueError: |
| 310 | calctype = calcrec |
| 311 | if calctype == 'min': |
| 312 | limittype = 'above' |
| 313 | elif calctype == 'max': |
| 314 | limittype = 'below' |
| 315 | elif calctype == 'avg': |
| 316 | limittype = 'exact' |
| 317 | elif calctype == 'diffmin': |
| 318 | limittype = 'above' |
| 319 | elif calctype == 'diffmax': |
| 320 | limittype = 'below' |
| 321 | else: |
| 322 | limittype = '(none)' |
| 323 | else: |
| 324 | calctype = 'avg' |
| 325 | limittype = 'exact' |
| 326 | |
| 327 | ttk.Label(frame2typ, text="Calculation:", anchor = 'e', |
| 328 | style = 'normal.TLabel').grid(row = 3, column = 0, padx = 5, |
| 329 | sticky = 'news') |
| 330 | self.ctyp = tkinter.StringVar(self) |
| 331 | self.ctyp.set(calctype) |
| 332 | frame2typ.ctyp = ttk.OptionMenu(frame2typ, self.ctyp, calctype, *calctypes) |
| 333 | frame2typ.ctyp.grid(row = 3, column = 1, padx = 5, sticky = 'news') |
| 334 | ttk.Label(frame2typ, text="Limit:", anchor = 'e', |
| 335 | style = 'normal.TLabel').grid(row = 4, column = 0, padx = 5, |
| 336 | sticky = 'news') |
| 337 | self.ltyp = tkinter.StringVar(self) |
| 338 | self.ltyp.set(limittype) |
| 339 | frame2typ.ltyp = ttk.OptionMenu(frame2typ, self.ltyp, limittype, *limittypes) |
| 340 | frame2typ.ltyp.grid(row = 4, column = 1, padx = 5, sticky = 'news') |
| 341 | |
| 342 | frame2max = ttk.Frame(frame2, borderwidth = 2, relief='groove') |
| 343 | frame2max.grid(row = 0, column = 2, padx = 2, pady = 2, sticky = 'news') |
| 344 | ttk.Label(frame2max, text="Maximum:", style = 'blue.TLabel', |
| 345 | anchor = 'w').grid(row = 0, column = 0, padx = 5, |
| 346 | sticky = 'news') |
| 347 | if 'max' in param: |
| 348 | maxrec = param['max'] |
| 349 | else: |
| 350 | maxrec = {} |
| 351 | ttk.Label(frame2max, text="Target:", anchor = 'e', |
| 352 | style = 'normal.TLabel').grid(row = 1, column = 0, padx = 5, |
| 353 | sticky = 'news') |
| 354 | frame2max.tmax = ttk.Entry(frame2max, textvariable = self.maxrec.target) |
| 355 | frame2max.tmax.grid(row = 1, column = 1, padx = 5, sticky = 'news') |
| 356 | frame2max.tmax.delete(0, 'end') |
| 357 | if 'target' in maxrec: |
| 358 | frame2max.tmax.insert(0, maxrec['target']) |
| 359 | ttk.Label(frame2max, text="Penalty:", anchor = 'e', |
| 360 | style = 'normal.TLabel').grid(row = 2, column = 0, padx = 5, |
| 361 | sticky = 'news') |
| 362 | frame2max.pmax = ttk.Entry(frame2max, textvariable = self.maxrec.penalty) |
| 363 | frame2max.pmax.grid(row = 2, column = 1, padx = 5, sticky = 'news') |
| 364 | frame2max.pmax.delete(0, 'end') |
| 365 | if 'penalty' in maxrec: |
| 366 | frame2max.pmax.insert(0, maxrec['penalty']) |
| 367 | if 'calc' in maxrec: |
| 368 | calcrec = maxrec['calc'] |
| 369 | try: |
| 370 | calctype, limittype = calcrec.split('-') |
| 371 | except ValueError: |
| 372 | calctype = calcrec |
| 373 | if calctype == 'min': |
| 374 | limittype = 'above' |
| 375 | elif calctype == 'max': |
| 376 | limittype = 'below' |
| 377 | elif calctype == 'avg': |
| 378 | limittype = 'exact' |
| 379 | elif calctype == 'diffmin': |
| 380 | limittype = 'above' |
| 381 | elif calctype == 'diffmax': |
| 382 | limittype = 'below' |
| 383 | else: |
| 384 | limittype = '(none)' |
| 385 | else: |
| 386 | calctype = 'max' |
| 387 | limittype = 'below' |
| 388 | |
| 389 | ttk.Label(frame2max, text="Calculation:", anchor = 'e', |
| 390 | style = 'normal.TLabel').grid(row = 3, column = 0, padx = 5, |
| 391 | sticky = 'news') |
| 392 | self.cmax = tkinter.StringVar(self) |
| 393 | self.cmax.set(calctype) |
| 394 | frame2max.cmax = ttk.OptionMenu(frame2max, self.cmax, calctype, *calctypes) |
| 395 | frame2max.cmax.grid(row = 3, column = 1, padx = 5, sticky = 'news') |
| 396 | ttk.Label(frame2max, text="Limit:", anchor = 'e', |
| 397 | style = 'normal.TLabel').grid(row = 4, column = 0, padx = 5, |
| 398 | sticky = 'news') |
| 399 | self.lmax = tkinter.StringVar(self) |
| 400 | self.lmax.set(limittype) |
| 401 | frame2max.lmax = ttk.OptionMenu(frame2max, self.lmax, limittype, *limittypes) |
| 402 | frame2max.lmax.grid(row = 4, column = 1, padx = 5, sticky = 'news') |
| 403 | |
| 404 | dframe = frame3.canvas.dframe |
| 405 | |
| 406 | ttk.Label(dframe, text="Conditions:", style = 'blue.TLabel', |
| 407 | anchor='w').grid(row = 0, column = 0, padx = 5, sticky = 'news', columnspan = 5) |
| 408 | |
| 409 | # Add conditions |
| 410 | |
| 411 | condtypes = ["VOLTAGE", "DIGITAL", "CURRENT", "RISETIME", "FALLTIME", |
| 412 | "RESISTANCE", "CAPACITANCE", "TEMPERATURE", "FREQUENCY", |
| 413 | "CORNER", "SIGMA", "ITERATIONS", "(none)"] |
| 414 | |
| 415 | steptypes = ['linear', 'log', '(none)'] |
| 416 | |
| 417 | n = 0 |
| 418 | r = 1 |
| 419 | self.crec = [] |
| 420 | self.cond = [] |
| 421 | for cond in param['conditions']: |
| 422 | # If over 5 columns of conditions, create a new row. |
| 423 | if n >= 5: |
| 424 | r += 1 |
| 425 | n = 0 |
| 426 | # New column |
| 427 | frame3c = ttk.Frame(dframe, borderwidth = 2, relief='groove') |
| 428 | frame3c.grid(row = r, column = n, padx = 2, pady = 2, sticky = 'news') |
| 429 | |
| 430 | crec = Condition(self) |
| 431 | # Condition description |
| 432 | ttk.Label(frame3c, text='Description:', style='normal.TLabel', |
| 433 | anchor='e').grid(row = 0, column = 0, padx = 5, sticky = 'news') |
| 434 | c1 = ttk.Entry(frame3c, textvariable = crec.display) |
| 435 | c1.grid(row = 0, column = 1, padx = 5, sticky = 'news') |
| 436 | c1.delete(0, 'end') |
| 437 | if 'display' in cond: |
| 438 | c1.insert(0, cond['display']) |
| 439 | else: |
| 440 | c1.insert(0, '(none)') |
| 441 | # Condition type (pulldown menu) |
| 442 | if 'condition' in cond: |
| 443 | crec.condition.set(cond['condition']) |
| 444 | else: |
| 445 | crec.condition.set('(none)') |
| 446 | ttk.Label(frame3c, text='Condition:', style='normal.TLabel', |
| 447 | anchor='e').grid(row = 1, column = 0, padx = 5, sticky = 'news') |
| 448 | c2 = ttk.OptionMenu(frame3c, crec.condition, crec.condition.get(), *condtypes) |
| 449 | c2.grid(row = 1, column = 1, padx = 5, sticky = 'news') |
| 450 | # Condition unit |
| 451 | ttk.Label(frame3c, text='Unit:', style='normal.TLabel', |
| 452 | anchor='e').grid(row = 3, column = 0, padx = 5, sticky = 'news') |
| 453 | c4 = ttk.Entry(frame3c, textvariable = crec.unit) |
| 454 | c4.grid(row = 3, column = 1, padx = 5, sticky = 'news') |
| 455 | c4.delete(0, 'end') |
| 456 | if 'unit' in cond: |
| 457 | c4.insert(0, cond['unit']) |
| 458 | else: |
| 459 | c4.insert(0, '(none)') |
| 460 | # Condition min |
| 461 | ttk.Label(frame3c, text='Minimum:', style='normal.TLabel', |
| 462 | anchor='e').grid(row = 4, column = 0, padx = 5, sticky = 'news') |
| 463 | c5 = ttk.Entry(frame3c, textvariable = crec.min) |
| 464 | c5.grid(row = 4, column = 1, padx = 5, sticky = 'news') |
| 465 | c5.delete(0, 'end') |
| 466 | if 'min' in cond: |
| 467 | c5.insert(0, cond['min']) |
| 468 | else: |
| 469 | c5.insert(0, '(none)') |
| 470 | # Condition typ |
| 471 | ttk.Label(frame3c, text='Typical:', style='normal.TLabel', |
| 472 | anchor='e').grid(row = 5, column = 0, padx = 5, sticky = 'news') |
| 473 | c6 = ttk.Entry(frame3c, textvariable = crec.typ) |
| 474 | c6.grid(row = 5, column = 1, padx = 5, sticky = 'news') |
| 475 | c6.delete(0, 'end') |
| 476 | if 'typ' in cond: |
| 477 | c6.insert(0, cond['typ']) |
| 478 | else: |
| 479 | c6.insert(0, '(none)') |
| 480 | # Condition max |
| 481 | ttk.Label(frame3c, text='Maximum:', style='normal.TLabel', |
| 482 | anchor='e').grid(row = 6, column = 0, padx = 5, sticky = 'news') |
| 483 | c7 = ttk.Entry(frame3c, textvariable = crec.max) |
| 484 | c7.grid(row = 6, column = 1, padx = 5, sticky = 'news') |
| 485 | c7.delete(0, 'end') |
| 486 | if 'max' in cond: |
| 487 | c7.insert(0, cond['max']) |
| 488 | else: |
| 489 | c7.insert(0, '(none)') |
| 490 | # Condition steptype |
| 491 | ttk.Label(frame3c, text='Step type:', style='normal.TLabel', |
| 492 | anchor='e').grid(row = 7, column = 0, padx = 5, sticky = 'news') |
| 493 | c8 = ttk.OptionMenu(frame3c, crec.steptype, crec.steptype.get(), *steptypes) |
| 494 | c8.grid(row = 7, column = 1, padx = 5, sticky = 'news') |
| 495 | if 'linstep' in cond: |
| 496 | crec.steptype.set('linear') |
| 497 | elif 'logstep' in cond: |
| 498 | crec.steptype.set('log') |
| 499 | else: |
| 500 | crec.steptype.set('(none)') |
| 501 | # Condition step |
| 502 | ttk.Label(frame3c, text='Step:', style='normal.TLabel', |
| 503 | anchor='e').grid(row = 8, column = 0, padx = 5, sticky = 'news') |
| 504 | c9 = ttk.Entry(frame3c, textvariable = crec.step) |
| 505 | c9.grid(row = 8, column = 1, padx = 5, sticky = 'news') |
| 506 | c9.delete(0, 'end') |
| 507 | if 'linstep' in cond: |
| 508 | c9.insert(0, cond['linstep']) |
| 509 | elif 'logstep' in cond: |
| 510 | c9.insert(0, cond['logstep']) |
| 511 | else: |
| 512 | c9.insert(0, '(none)') |
| 513 | |
| 514 | n += 1 |
| 515 | self.cond.append(crec) |
| 516 | # Condition remove |
| 517 | c10 = ttk.Button(frame3c, text='Remove', style='normal.TButton', |
| 518 | command = lambda cond=cond: self.remove_condition(cond)) |
| 519 | c10.grid(row = 9, column = 1, padx = 5, sticky = 'news') |
| 520 | |
| 521 | # Add 'add condition' button |
| 522 | dframe.bcond = ttk.Button(dframe, text="Add Condition", |
| 523 | style = 'blue.TButton', command = self.add_condition) |
| 524 | if n >= 5: |
| 525 | dframe.bcond.grid(row = r + 1, column = 0, padx = 5, pady = 3, sticky = 'nsw') |
| 526 | else: |
| 527 | dframe.bcond.grid(row = r, column = n, padx = 5, pady = 3, sticky = 'new') |
| 528 | |
| 529 | # Set the current parameter |
| 530 | self.param = param |
| 531 | |
| 532 | def on_mousewheel(self, event): |
| 533 | if event.num == 5: |
| 534 | self.canvas.yview_scroll(1, "units") |
| 535 | elif event.num == 4: |
| 536 | self.canvas.yview_scroll(-1, "units") |
| 537 | |
| 538 | def frame_configure(self, event): |
| 539 | self.update_idletasks() |
| 540 | self.canvas.configure(scrollregion=self.canvas.bbox("all")) |
| 541 | |
| 542 | def add_condition(self): |
| 543 | # Add a new condition |
| 544 | newcond = {} |
| 545 | newcond['condition'] = '(none)' |
| 546 | self.param['conditions'].append(newcond) |
| 547 | self.populate(self.param) |
| 548 | |
| 549 | def remove_condition(self, cond): |
| 550 | # Remove and existing condition |
| 551 | condlist = self.param['conditions'] |
| 552 | eidx = condlist.index(cond) |
| 553 | condlist.pop(eidx) |
| 554 | self.populate(self.param) |
| 555 | |
| 556 | def apply(self): |
| 557 | # Apply the values back to the parameter record |
| 558 | self.param['method'] = self.selmethod.get() |
| 559 | unit = self.unit.get() |
| 560 | if not (unit == '(none)' or unit == ''): |
| 561 | self.param['unit'] = unit |
| 562 | display = self.display.get() |
| 563 | if not (display == '(none)' or display == ''): |
| 564 | self.param['display'] = display |
| 565 | targmin = self.minrec.target.get() |
| 566 | if not (targmin == '(none)' or targmin == ''): |
| 567 | pmin = {} |
| 568 | pmin['target'] = targmin |
| 569 | pmin['penalty'] = self.minrec.penalty.get() |
| 570 | cmin = self.minrec.calc.get() |
| 571 | if not (cmin == '(none)' or cmin == ''): |
| 572 | lmin = self.minrec.limit.get() |
| 573 | if not (lmin == '(none)' or lmin == ''): |
| 574 | pmin['calc'] = cmin + '-' + lmin |
| 575 | else: |
| 576 | pmin['calc'] = cmin |
| 577 | self.param['min'] = pmin |
| 578 | targtyp = self.typrec.target.get() |
| 579 | if not (targtyp == '(none)' or targtyp == ''): |
| 580 | ptyp= {} |
| 581 | ptyp['target'] = targtyp |
| 582 | ptyp['penalty'] = self.typrec.penalty.get() |
| 583 | ctyp = self.typrec.calc.get() |
| 584 | if not (ctyp == '(none)' or ctyp == ''): |
| 585 | ltyp = self.typrec.limit.get() |
| 586 | if not (ltyp == '(none)' or ltyp == ''): |
| 587 | ptyp['calc'] = ctyp + '-' + ltyp |
| 588 | else: |
| 589 | ptyp['calc'] = ctyp |
| 590 | self.param['typ'] = ptyp |
| 591 | targmax = self.maxrec.target.get() |
| 592 | if not (targmax == '(none)' or targmax == ''): |
| 593 | pmax= {} |
| 594 | pmax['target'] = targmax |
| 595 | pmax['penalty'] = self.maxrec.penalty.get() |
| 596 | cmax = self.maxrec.calc.get() |
| 597 | if not (cmax == '(none)' or cmax == ''): |
| 598 | lmax = self.maxrec.limit.get() |
| 599 | if not (lmax == '(none)' or lmax == ''): |
| 600 | pmax['calc'] = cmax + '-' + lmax |
| 601 | else: |
| 602 | pmax['calc'] = cmax |
| 603 | self.param['max'] = pmax |
| 604 | |
| 605 | condlist = [] |
| 606 | for crec in self.cond: |
| 607 | cond = {} |
| 608 | cname = crec.condition.get() |
| 609 | if cname == '(none)' or cname == '': |
| 610 | continue |
| 611 | cond['condition'] = cname |
| 612 | display = crec.display.get() |
| 613 | if not (display == '(none)' or display == ''): |
| 614 | cond['display'] = display |
| 615 | min = crec.min.get() |
| 616 | if not (min == '(none)' or min == ''): |
| 617 | cond['min'] = min |
| 618 | typ = crec.typ.get() |
| 619 | if not (typ == '(none)' or typ == ''): |
| 620 | cond['typ'] = typ |
| 621 | max = crec.max.get() |
| 622 | if not (max == '(none)' or max == ''): |
| 623 | cond['max'] = max |
| 624 | unit = crec.unit.get() |
| 625 | if not (unit == '(none)' or unit == ''): |
| 626 | cond['unit'] = unit |
| 627 | steptype = crec.steptype.get() |
| 628 | step = crec.step.get() |
| 629 | if not (step == '(none)' or step == ''): |
| 630 | if steptype == 'linear': |
| 631 | cond['linstep'] = step |
| 632 | elif steptype == 'log': |
| 633 | cond['logstep'] = step |
| 634 | condlist.append(cond) |
| 635 | self.param['conditions'] = condlist |
| 636 | |
| 637 | self.parent.create_datasheet_view() |
| 638 | return |
| 639 | |
| 640 | def close(self): |
| 641 | # pop down settings window |
| 642 | self.withdraw() |
| 643 | |
| 644 | def open(self): |
| 645 | # pop up settings window |
| 646 | self.deiconify() |
| 647 | self.lift() |