def mr_init_frame(self): self.measurement_report_btn.SetDefault() self.update_layout() config.defaults.update({ "position.reportframe.x": self.GetDisplay().ClientArea[0] + 40, "position.reportframe.y": self.GetDisplay().ClientArea[1] + 60, "size.reportframe.w": self.ClientSize[0], "size.reportframe.h": self.ClientSize[1] }) if (hascfg("position.reportframe.x") and hascfg("position.reportframe.y") and hascfg("size.reportframe.w") and hascfg("size.reportframe.h")): self.SetSaneGeometry(int(getcfg("position.reportframe.x")), int(getcfg("position.reportframe.y")), int(getcfg("size.reportframe.w")), int(getcfg("size.reportframe.h"))) else: self.Center()
def mr_show_trc_controls(self): shown = self.apply_trc_ctrl.IsShown() enable6 = (shown and bool(getcfg("measurement_report.apply_trc"))) show = shown and (self.mr_trc_ctrl.GetSelection() == 2 or getcfg("show_advanced_options")) self.panel.Freeze() self.mr_trc_ctrl.Enable(enable6) self.mr_trc_ctrl.Show(shown) self.mr_trc_gamma_label.Enable(enable6) self.mr_trc_gamma_label.Show(show) self.mr_trc_gamma_ctrl.Enable(enable6) self.mr_trc_gamma_ctrl.Show(show) self.mr_trc_gamma_type_ctrl.Enable(enable6) self.mr_black_output_offset_label.Enable(enable6 and self.XYZbpout > [0, 0, 0]) self.mr_black_output_offset_label.Show(show and self.XYZbpout > [0, 0, 0]) self.mr_black_output_offset_ctrl.Enable(enable6 and self.XYZbpout > [0, 0, 0]) self.mr_black_output_offset_ctrl.Show(show and self.XYZbpout > [0, 0, 0]) self.mr_black_output_offset_intctrl.Enable( enable6 and self.XYZbpout > [0, 0, 0]) self.mr_black_output_offset_intctrl.Show(show and self.XYZbpout > [0, 0, 0]) self.mr_black_output_offset_intctrl_label.Enable( enable6 and self.XYZbpout > [0, 0, 0]) self.mr_black_output_offset_intctrl_label.Show( show and self.XYZbpout > [0, 0, 0]) self.mr_trc_gamma_type_ctrl.Show(show and self.XYZbpout > [0, 0, 0]) self.panel.Layout() self.panel.Thaw()
def _setup(self): self.logger.info("-" * 80) self.is_measuring = False self.keepGoing = True self.last_error = None self.index = -1 self.index_max = -1 self.last_XYZ = (-1, -1, -1) self.white_XYZ = (-1, -1, -1) self.measure_count = 0 self.measured = [] self.finished = False self.label_RGB.SetLabel(" ") self.label_XYZ.SetLabel(" ") self.panel_RGB.SetBackgroundColour(BGCOLOUR) self.panel_RGB.Refresh() self.panel_RGB.Update() self.panel_XYZ.SetBackgroundColour(BGCOLOUR) self.panel_XYZ.Refresh() self.panel_XYZ.Update() self.label_index.SetLabel(" ") self.enable_btns(False) self.measure_auto_cb.SetValue(bool(getcfg("untethered.measure.auto"))) self.finish_btn.Disable() if self.grid.GetNumberRows(): self.grid.DeleteRows(0, self.grid.GetNumberRows()) # Set position x = getcfg("position.progress.x") y = getcfg("position.progress.y") self.SetSaneGeometry(x, y)
def Show(self, show=True): if show: self.show_controls() if hasattr(self, "measure_darken_background_cb"): self.measure_darken_background_cb.SetValue( bool(int(getcfg("measure.darken_background")))) if self.Parent and hasattr(self.Parent, "display_ctrl"): display_no = self.Parent.display_ctrl.GetSelection() else: display_no = getcfg('display.number') - 1 if display_no < 0 or display_no > wx.Display.GetCount() - 1: display_no = 0 else: display_no = get_display_number(display_no) x, y = wx.Display(display_no).Geometry[:2] self.SetPosition((x, y)) # place measure frame on correct display self.place_n_zoom( *floatlist(getcfg("dimensions.measureframe").split(","))) self.display_no = wx.Display.GetFromWindow(self) elif self.IsShownOnScreen(): setcfg("dimensions.measureframe", self.get_dimensions()) if self.Parent and hasattr(self.Parent, "get_set_display"): self.Parent.get_set_display() if isinstance(self, wx.Dialog): if show: self.ShowModal() else: if self.IsModal(): self.EndModal(wx.ID_OK) else: wx.Dialog.Hide(self) else: wx.Frame.Show(self, show)
def mr_update_trc_controls(self): self.mr_update_trc_control() self.mr_trc_gamma_ctrl.SetValue( str(getcfg("measurement_report.trc_gamma"))) self.mr_trc_gamma_type_ctrl.SetSelection(self.trc_gamma_types_ba[ getcfg("measurement_report.trc_gamma_type")]) outoffset = int(getcfg("measurement_report.trc_output_offset") * 100) self.mr_black_output_offset_ctrl.SetValue(outoffset) self.mr_black_output_offset_intctrl.SetValue(outoffset)
def OnMove(self, event): if self.IsShownOnScreen() and not self.IsIconized() and \ (not self.GetParent() or not self.GetParent().IsShownOnScreen()): prev_x = getcfg("position.progress.x") prev_y = getcfg("position.progress.y") x, y = self.GetScreenPosition() if x != prev_x or y != prev_y: setcfg("position.progress.x", x) setcfg("position.progress.y", y)
def use_simulation_profile_ctrl_handler(self, event, update_trc=True): if event: setcfg("measurement_report.use_simulation_profile", int(self.simulation_profile_cb.GetValue())) sim_profile = self.get_simulation_profile() enable = False if sim_profile: self.set_simulate_whitepoint() if ("rTRC" in sim_profile.tags and "gTRC" in sim_profile.tags and "bTRC" in sim_profile.tags and sim_profile.tags.rTRC == sim_profile.tags.gTRC == sim_profile.tags.bTRC and isinstance(sim_profile.tags.rTRC, ICCP.CurveType)): tf = sim_profile.tags.rTRC.get_transfer_function(outoffset=1.0) if update_trc or self.XYZbpin == self.XYZbpout: # Use only BT.1886 black output offset or not setcfg( "measurement_report.apply_black_offset", int(tf[0][1] not in (-240, -709) and (not tf[0][0].startswith("Gamma") or tf[1] < .95) and self.XYZbpin != self.XYZbpout)) if update_trc: # Use BT.1886 gamma mapping for SMPTE 240M / Rec. 709 TRC setcfg( "measurement_report.apply_trc", int(tf[0][1] in (-240, -709) or (tf[0][0].startswith("Gamma") and tf[1] >= .95))) # Set gamma to profile gamma if single gamma profile if tf[0][0].startswith("Gamma") and tf[1] >= .95: if not getcfg("measurement_report.trc_gamma.backup", False): # Backup current gamma setcfg("measurement_report.trc_gamma.backup", getcfg("measurement_report.trc_gamma")) setcfg("measurement_report.trc_gamma", round(tf[0][1], 2)) # Restore previous gamma if not single gamma profile elif getcfg("measurement_report.trc_gamma.backup", False): setcfg("measurement_report.trc_gamma", getcfg("measurement_report.trc_gamma.backup")) setcfg("measurement_report.trc_gamma.backup", None) self.mr_update_trc_controls() enable = (tf[0][1] not in (-240, -709) and self.XYZbpin != self.XYZbpout) elif update_trc: enable = self.XYZbpin != self.XYZbpout setcfg("measurement_report.apply_black_offset", int(enable)) setcfg("measurement_report.apply_trc", 0) self.apply_black_offset_ctrl.Enable(bool(sim_profile) and enable) self.mr_update_main_controls()
def get_argyll_util(name, paths=None): """ Find a single Argyll utility. Return the full path. """ cfg_argyll_dir = getcfg("argyll.dir") if not paths: paths = getenvu("PATH", os.defpath).split(os.pathsep) argyll_dir = (cfg_argyll_dir or "").rstrip(os.path.sep) if argyll_dir: if argyll_dir in paths: paths.remove(argyll_dir) paths = [argyll_dir] + paths cache_key = os.pathsep.join(paths) exe = argyll_utils.get(cache_key, {}).get(name, None) if exe: return exe elif verbose >= 4: safe_print("Info: Searching for", name, "in", os.pathsep.join(paths)) for path in paths: for altname in argyll_altnames.get(name, []): exe = which(altname + exe_ext, [path]) if exe: break if exe: break if verbose >= 4: if exe: safe_print("Info:", name, "=", exe) else: safe_print("Info:", "|".join(argyll_altnames[name]), "not found in", os.pathsep.join(paths)) if exe: if not cache_key in argyll_utils: argyll_utils[cache_key] = {} argyll_utils[cache_key][name] = exe return exe
def mr_set_filebrowse_paths(self): for which in ("simulation", "devlink", "output"): self.set_profile_ctrl_path(which) chart = getcfg("measurement_report.chart") if not chart or not os.path.isfile(chart): chart = config.defaults["measurement_report.chart"] setcfg("measurement_report.chart", chart) self.mr_set_testchart(chart, load=False)
def mr_trc_gamma_ctrl_handler(self, event): try: v = float(self.mr_trc_gamma_ctrl.GetValue().replace(",", ".")) if (v < config.valid_ranges["measurement_report.trc_gamma"][0] or v > config.valid_ranges["measurement_report.trc_gamma"][1]): raise ValueError() except ValueError: wx.Bell() self.mr_trc_gamma_ctrl.SetValue( str(getcfg("measurement_report.trc_gamma"))) else: if str(v) != self.mr_trc_gamma_ctrl.GetValue(): self.mr_trc_gamma_ctrl.SetValue(str(v)) if v != getcfg("measurement_report.trc_gamma"): setcfg("measurement_report.trc_gamma", v) self.mr_update_trc_control() self.mr_show_trc_controls() event.Skip()
def fetch_plants_table(): query_fmt = "SELECT {} FROM plants WHERE STATE IN {} OR ORISPL_CODE IN {}" conn = mysql.connector.connect(**config.getcfg()) query_text = query_fmt.format(", ".join(USEFUL_COLS), WIEB_STATES, TX_PLANTS) return pd.read_sql(query_text, conn) \ .groupby("orispl_code") \ .agg(aggregators) \ .rename(columns={"year": "years"}) \ .drop(["orispl_code"], axis=1)
def getcode(): """ Get language code from config """ lcode = getcfg("lang") if not lcode in ldict: # fall back to default lcode = defaults["lang"] if not lcode in ldict: # fall back to english lcode = "en" return lcode
def mr_black_output_offset_ctrl_handler(self, event): if event.GetId() == self.mr_black_output_offset_intctrl.GetId(): self.mr_black_output_offset_ctrl.SetValue( self.mr_black_output_offset_intctrl.GetValue()) else: self.mr_black_output_offset_intctrl.SetValue( self.mr_black_output_offset_ctrl.GetValue()) v = self.mr_black_output_offset_ctrl.GetValue() / 100.0 if v != getcfg("measurement_report.trc_output_offset"): setcfg("measurement_report.trc_output_offset", v) self.mr_update_trc_control()
def zoommax_handler(self, event): if debug: safe_print("[D] measureframe_zoommax_handler") display_client_rect = self.get_display()[2] if debug: safe_print("[D] display_client_rect:", display_client_rect) display_client_size = display_client_rect[2:] if debug: safe_print("[D] display_client_size:", display_client_size) size = self.GetSize() if debug: safe_print(" size:", size) if max(size) >= max(display_client_size) - 50: dim = getcfg("dimensions.measureframe.unzoomed") self.place_n_zoom(*floatlist(dim.split(","))) else: setcfg("dimensions.measureframe.unzoomed", self.get_dimensions()) self.place_n_zoom(x=.5, y=.5, scale=50.0)
def fetch_plant_data(orispl_code): query_text = """ SELECT adddate(`op_date`, interval `op_hour` hour) as `datetime`, SUM(`gload` * `op_time`) as `gload`, SUM(`so2_mass`) as `so2_mass`, SUM(`nox_mass`) as `nox_mass`, SUM(`co2_mass`) as `co2_mass`, SUM(`heat_input`) as `heat_input` FROM `data` WHERE `orispl_code` = {} GROUP BY `datetime` """.format(orispl_code) conn = mysql.connector.connect(**config.getcfg()) return pd.read_sql(query_text, conn, index_col='datetime')
def show_rgb(self, rgb): if getcfg("patterngenerator.use_video_levels"): minv = 16 maxv = 235 else: minv = 0 maxv = 255 rgb = tuple(minv + v * (maxv - minv) for v in rgb) floor = tuple(int(math.floor(v)) for v in rgb) ceil = tuple(int(math.ceil(v)) for v in rgb) if floor != ceil: # Dither using simple ordered pattern safe_print("Dither 8 bit %.6f %.6f %.6f -> %i %i %i | %i %i %i" % (rgb + floor + ceil)) img = wx.EmptyImage(*self.ClientSize, clear=False) buf = img.GetDataBuffer() buflen = len(buf) # Intervals in pixels per each R, G and B intervals = tuple( (buflen / (buflen * (rgb[i] - floor[i])) if rgb[i] - floor[i] else 0) for i in range(3)) safe_print("Intervals %.6f %.6f %.6f" % intervals) floorbytes = tuple(chr(v) for v in floor) ceilbytes = tuple(chr(v) for v in ceil) n = 0 # XXX: Generating the dithered image can take nontrivial amounts # of time if the image is large ts = time.time() for i, byte in enumerate(buf): m = intervals[i % 3] if m and n % m < 1: color = ceilbytes else: color = floorbytes buf[i] = color[i % 3] if i % 3 == 2: n += 1 safe_print("Generating dithered image took %.3fs" % (time.time() - ts)) bmp = img.ConvertToBitmap() else: # Exact safe_print("Exact 8 bit %.6f %.6f %.6f" % rgb) bmp = wx.EmptyBitmapRGBA(*tuple(self.ClientSize) + floor, alpha=255) self.panel.SetBitmap(bmp) self.panel.Refresh() if self.Parent: self.Parent.worker._patterngenerator_wait = False
def Show(self, show=True): if show: display_no = getcfg("display.number") - 1 if display_no < 0 or display_no > wx.Display.GetCount() - 1: display_no = 0 else: display_no = get_display_number(display_no) x, y, w, h = wx.Display(display_no).ClientArea # Place frame on correct display self.SetPosition((x, y)) self.SetSize((w, h)) self.disable_buttons() wx.CallAfter(self.Maximize) wx.Frame.Show(self, show) self.panels[0].SetFocus()
def check_argyll_bin(paths=None): """ Check if the Argyll binaries can be found. """ prev_dir = None for name in argyll_names: exe = get_argyll_util(name, paths) if not exe: if name in argyll_optional: continue return False cur_dir = os.path.dirname(exe) if prev_dir: if cur_dir != prev_dir: if name in argyll_optional: if verbose: safe_print("Warning: Optional Argyll " "executable %s is not in the same " "directory as the main executables " "(%s)." % (exe, prev_dir)) else: if verbose: safe_print("Error: Main Argyll " "executable %s is not in the same " "directory as the other executables " "(%s)." % (exe, prev_dir)) return False else: prev_dir = cur_dir if verbose >= 3: safe_print("Argyll binary directory:", cur_dir) if debug: safe_print("[D] check_argyll_bin OK") if debug >= 2: if not paths: paths = getenvu("PATH", os.defpath).split(os.pathsep) argyll_dir = (getcfg("argyll.dir") or "").rstrip(os.path.sep) if argyll_dir: if argyll_dir in paths: paths.remove(argyll_dir) paths = [argyll_dir] + paths safe_print("[D] Searchpath:\n ", "\n ".join(paths)) # Fedora doesn't ship Rec709.icm config.defaults["3dlut.input.profile"] = get_data_path(os.path.join("ref", "Rec709.icm")) or \ get_data_path(os.path.join("ref", "sRGB.icm")) or "" config.defaults["testchart.reference"] = get_data_path( os.path.join("ref", "ColorChecker.cie")) or "" config.defaults["gamap_profile"] = get_data_path( os.path.join("ref", "sRGB.icm")) or "" return True
def mr_update_trc_control(self): if (getcfg("measurement_report.trc_gamma_type") == "B" and getcfg("measurement_report.trc_output_offset") == 0 and getcfg("measurement_report.trc_gamma") == 2.4): self.mr_trc_ctrl.SetSelection(1) # BT.1886 elif (getcfg("measurement_report.trc_gamma_type") == "b" and getcfg("measurement_report.trc_output_offset") == 1 and getcfg("measurement_report.trc_gamma") == 2.2): self.mr_trc_ctrl.SetSelection(0) # Pure power gamma 2.2 else: self.mr_trc_ctrl.SetSelection(2) # Custom
def fetch_all_supplemental_plant_details(): """Returns the maximum gload served and total co2 emitted by each plant. Maximum gload is defined here as the maximum over all hourly intervals of the sum of gloads for all units in a plant. In other words, it is possible that each unit in a plant served a higher load at some point during the data collection period, but at no point during the data collection period did the plant as a whole register a higher gload. """ conditions = ["ORISPL_CODE IN " + str(TX_PLANTS)] for state in WIEB_STATES: conditions.append("STATE='%s'" % state) df = pd.DataFrame() conn = mysql.connector.connect(**config.getcfg()) for condition in conditions: print("On query with condition", condition) df = df.append(fetch_plant_capacity_and_co2(conn, condition)) return df
def chart_btn_handler(self, event): if self.Parent: parent = self.Parent else: parent = self chart = getcfg("measurement_report.chart") if not hasattr(parent, "tcframe"): parent.tcframe = TestchartEditor( parent, path=chart, cfg="measurement_report.chart", parent_set_chart_methodname="mr_set_testchart") elif (not hasattr(parent.tcframe, "ti1") or chart != parent.tcframe.ti1.filename): parent.tcframe.tc_load_cfg_from_ti1(None, chart, "measurement_report.chart", "mr_set_testchart") setcfg("tc.show", 1) parent.tcframe.Show() parent.tcframe.Raise()
def get_display(display_no=0): if _displays is None: enumerate_displays() # Translate from Argyll display index to enumerated display index # using the coordinates and dimensions from config import getcfg, is_virtual_display if is_virtual_display(display_no): return try: argyll_display = getcfg("displays")[display_no] except IndexError: return else: if argyll_display.endswith(" [PRIMARY]"): argyll_display = " ".join(argyll_display.split(" ")[:-1]) for display in _displays: desc = display.get("description") if desc: geometry = "".join(desc.split("@ ")[-1:]) if argyll_display.endswith("@ " + geometry): return display
def measure_darken_background_ctrl_handler(self, event): if self.measure_darken_background_cb.GetValue() and \ getcfg("measure.darken_background.show_warning"): dlg = ConfirmDialog(self, msg=lang.getstr("measure.darken_background." "warning"), ok=lang.getstr("ok"), cancel=lang.getstr("cancel"), bitmap=geticon(32, "dialog-warning")) chk = wx.CheckBox(dlg, -1, lang.getstr("dialog.do_not_show_again")) dlg.Bind(wx.EVT_CHECKBOX, self.measure_darken_background_warning_handler, id=chk.GetId()) dlg.sizer3.Add(chk, flag=wx.TOP | wx.ALIGN_LEFT, border=12) dlg.sizer0.SetSizeHints(dlg) dlg.sizer0.Layout() rslt = dlg.ShowModal() if rslt == wx.ID_CANCEL: self.measure_darken_background_cb.SetValue(False) setcfg("measure.darken_background", int(self.measure_darken_background_cb.GetValue()))
def __init__(self, parent, cgats, worker=None): """ Init new CCXPlot window. parent Parent window (only used for error dialogs) cgats A CCMX/CCSS CGATS instance worker Worker instance """ self.is_ccss = cgats[0].type == "CCSS" desc = cgats.get_descriptor() if cgats.filename: fn, ext = os.path.splitext(os.path.basename(cgats.filename)) else: fn = desc if self.is_ccss: ext = ".ccss" else: ext = ".ccmx" desc = lang.getstr(ext[1:] + "." + fn, default=desc) if self.is_ccss: ccxx_type = "spectral" else: ccxx_type = "matrix" title = "%s: %s" % (lang.getstr(ccxx_type), desc) if self.is_ccss: # Convert to TI3 so we can get XYZ from spectra for coloring temp = worker.create_tempdir() if isinstance(temp, Exception): show_result_dialog(temp, parent) else: basename = make_filename_safe(desc) temp_path = os.path.join(temp, basename + ".ti3") cgats[0].type = "CTI3" cgats[0].DEVICE_CLASS = "DISPLAY" cgats.write(temp_path) temp_out_path = os.path.join(temp, basename + ".CIE.ti3") result = worker.exec_cmd(get_argyll_util("spec2cie"), [temp_path, temp_out_path], capture_output=True) if isinstance(result, Exception) or not result: show_result_dialog( UnloggedError(result or "".join(worker.errors)), parent) worker.wrapup(False) else: try: cgats = CGATS.CGATS(temp_out_path) except Exception as exception: show_result_dialog(exception, parent) finally: worker.wrapup(False) data_format = cgats.queryv1("DATA_FORMAT") data = cgats.queryv1("DATA") XYZ_max = 0 self.samples = [] if self.is_ccss: x_min = cgats.queryv1("SPECTRAL_START_NM") x_max = cgats.queryv1("SPECTRAL_END_NM") bands = cgats.queryv1("SPECTRAL_BANDS") lores = bands <= 40 if lores: # Interpolate if lores # 1nm intervals steps = int(x_max - x_min) + 1 safe_print("Up-interpolating", bands, "spectral bands to", steps) step = (x_max - x_min) / (steps - 1.) else: step = (x_max - x_min) / (bands - 1.) y_min = 0 y_max = 1 Y_max = 0 for i, sample in data.items(): # Get nm and spectral power values = [] x = x_min for k in data_format.values(): if k.startswith("SPEC_"): y = sample[k] y_min = min(y, y_min) y_max = max(y, y_max) if lores: values.append(y) else: values.append((x, y)) x += step if lores: # Interpolate if lores. Use Catmull-Rom instead of # PolySpline as we want curves to go through points exactly numvalues = len(values) interp = ICCP.CRInterpolation(values) values = [] for i in range(steps): values.append( (x, interp(i / (steps - 1.) * (numvalues - 1.)))) x += step # Get XYZ for colorization XYZ = [] for component in "XYZ": label = "XYZ_" + component if label in sample: v = sample[label] XYZ_max = max(XYZ_max, v) if label == "XYZ_Y": Y_max = max(Y_max, v) XYZ.append(v) self.samples.append((XYZ, values, {})) Plot = plot.PolyLine Plot._attributes["width"] = 1 else: # CCMX cube_size = 2 x_min = 0 y_min = 0 mtx = colormath.Matrix3x3( [[sample[k] for k in data_format.values()] for sample in data.values()]) imtx = mtx.inverted() # Get XYZ that colorimeter would measure without matrix (sRGB ref, # so not accurate, but useful for visual representation which is all # we care about here) if cube_size == 2: scale = 1 x_max = 100 * scale y_max = x_max * (74.6 / 67.4) if sys.platform != "win32": x_center = x_max / 2. else: x_center = x_max / 2. - 2.5 y_center = y_max / 2. x_center *= scale y_center *= scale pos2rgb = [((x_center - 23.7, y_center - 13.7), (0, 0, 1)), ((x_center, y_center + 27.3), (0, 1, 0)), ((x_center + 23.7, y_center - 13.7), (1, 0, 0)), ((x_center - 23.7, y_center + 13.7), (0, 1, 1)), ((x_center, y_center - 27.3), (1, 0, 1)), ((x_center + 23.7, y_center + 13.7), (1, 1, 0)), ((x_center, y_center), (1, 1, 1))] attrs_c = {'size': 10} attrs_r = {'size': 5} else: x_max = 100 y_max = 100 y = -5 pos2rgb = [] for R in range(cube_size): for G in range(cube_size): x = -5 y += 10 for B in range(cube_size): x += 10 pos2rgb.append(((x, y), (v / (cube_size - 1.0) for v in (R, G, B)))) attrs_c = {'marker': 'square', 'size': 10} attrs_r = {'marker': 'square', 'size': 5} Y_max = (imtx * colormath.get_whitepoint("D65"))[1] for i, ((x, y), (R, G, B)) in enumerate(pos2rgb): XYZ = list(colormath.RGB2XYZ(R, G, B)) X, Y, Z = imtx * XYZ XYZ_max = max(XYZ_max, X, Y, Z) self.samples.append(([X, Y, Z], [(x, y)], attrs_c)) self.samples.append((XYZ, [(x, y)], attrs_r)) Plot = plot.PolyMarker if self.is_ccss: # Protect against division by zero when range is zero if not x_max - x_min: x_min = 350.0 x_max = 750.0 if not y_max - y_min: y_min = 0.0 y_max = 10.0 y_zero = 0 self.ccxx_axis_x = (math.floor(x_min / 50.) * 50, math.ceil(x_max / 50.) * 50) self.spec_x = (self.ccxx_axis_x[1] - self.ccxx_axis_x[0]) / 50. graph_range = nicenum(y_max - y_zero, False) d = nicenum(graph_range / (NTICK - 1.0), True) self.spec_y = math.ceil(y_max / d) self.ccxx_axis_y = (math.floor(y_zero / d) * d, self.spec_y * d) else: self.ccxx_axis_x = (math.floor(x_min / 20.) * 20, math.ceil(x_max / 20.) * 20) self.ccxx_axis_y = (math.floor(y_min), math.ceil(y_max)) self.gfx = [] for XYZ, values, attrs in self.samples: if len(XYZ) == 3: # Got XYZ if attrs.get("size") > 11.25: # Colorimeter XYZ if Y_max > 1: # Colorimeter brighter than ref XYZ[:] = [v / Y_max for v in XYZ] else: # Colorimeter dimmer than ref XYZ[:] = [v * Y_max for v in XYZ] else: # Ref XYZ if Y_max > 1: # Colorimeter brighter than ref XYZ[:] = [v / Y_max for v in XYZ] RGB = tuple( int(v) for v in colormath.XYZ2RGB(*XYZ, scale=255, round_=True)) else: RGB = (153, 153, 153) self.gfx.append(Plot(values, colour=wx.Colour(*RGB), **attrs)) if self.is_ccss: # Add a few points at the extremes to define a bounding box self.gfx.append( plot.PolyLine( [(self.ccxx_axis_x[0], self.ccxx_axis_y[0]), (self.ccxx_axis_x[1], self.ccxx_axis_y[1] - y_min)], colour=wx.Colour(0, 0, 0, 0))) ref = cgats.queryv1("REFERENCE") if ref: ref = get_canonical_instrument_name(safe_unicode(ref, "UTF-8")) if not self.is_ccss: observers_ab = {} for observer in config.valid_values["observer"]: observers_ab[observer] = lang.getstr("observer." + observer) x_label = [lang.getstr("matrix")] x_label.extend(["%9.6f %9.6f %9.6f" % tuple(row) for row in mtx]) if ref: ref_observer = cgats.queryv1("REFERENCE_OBSERVER") if ref_observer: ref += ", " + observers_ab.get(ref_observer, ref_observer) x_label.append("") x_label.append(ref) fit_method = cgats.queryv1("FIT_METHOD") if fit_method == "xy": fit_method = lang.getstr("ccmx.use_four_color_matrix_method") elif fit_method: fit_method = lang.getstr("perceptual") fit_de00_avg = cgats.queryv1("FIT_AVG_DE00") if not isinstance(fit_de00_avg, float): fit_de00_avg = None fit_de00_max = cgats.queryv1("FIT_MAX_DE00") if not isinstance(fit_de00_max, float): fit_de00_max = None if fit_method: x_label.append(fit_method) fit_de00 = [] if fit_de00_avg: fit_de00.append( "ΔE*00 %s %.4f" % (lang.getstr("profile.self_check.avg"), fit_de00_avg)) if fit_de00_max: fit_de00.append( "ΔE*00 %s %.4f" % (lang.getstr("profile.self_check.max"), fit_de00_max)) if fit_de00: x_label.append("\n".join(fit_de00)) x_label = "\n".join(x_label) else: x_label = "" if ref: x_label += ref + ", " x_label += "%.1fnm, %i-%inm" % ((x_max - x_min) / (bands - 1.0), x_min, x_max) scale = max(getcfg("app.dpi") / config.get_default_dpi(), 1) style = wx.DEFAULT_FRAME_STYLE wx.Frame.__init__(self, None, -1, title, style=style) self.SetIcons(config.get_icon_bundle([256, 48, 32, 16], appname)) self.SetBackgroundColour(BGCOLOUR) self.Sizer = wx.GridSizer(1, 1, 0, 0) bg = wx.Panel(self) bg.SetBackgroundColour(BGCOLOUR) bg.Sizer = wx.BoxSizer(wx.VERTICAL) self.canvas = canvas = LUTCanvas(bg) if self.is_ccss: bg.MinSize = (513 * scale, 557 * scale) btnsizer = wx.BoxSizer(wx.HORIZONTAL) bg.Sizer.Add(btnsizer, flag=wx.EXPAND | wx.TOP | wx.RIGHT | wx.LEFT, border=16) self.toggle_btn = FlatShadedButton(bg, -1, label=lang.getstr("spectral")) btnsizer.Add(self.toggle_btn, 1) self.Sizer.Add(bg, 1, flag=wx.EXPAND) bg.Sizer.Add(canvas, 1, flag=wx.EXPAND) else: self.Sizer.Add(bg, flag=wx.ALIGN_CENTER) canvas_w = 240 * scale canvas.MinSize = (canvas_w, canvas_w * (74.6 / 67.4)) bg.Sizer.Add(canvas, flag=wx.ALIGN_CENTER) label = wx.StaticText(bg, -1, x_label.replace("&", "&&"), style=wx.ALIGN_CENTRE_HORIZONTAL) label.SetForegroundColour(FGCOLOUR) label.SetMaxFontSize(11) bg.Sizer.Add(label, flag=wx.ALIGN_CENTER | wx.ALL & ~wx.TOP, border=16 * scale) canvas.SetBackgroundColour(BGCOLOUR) canvas.SetEnableCenterLines(False) canvas.SetEnableDiagonals(False) canvas.SetEnableGrid(True) canvas.enableTicks = (True, True) canvas.tickPen = wx.Pen(GRIDCOLOUR, canvas._pointSize[0]) canvas.SetEnablePointLabel(False) canvas.SetEnableTitle(True) canvas.SetForegroundColour(FGCOLOUR) canvas.SetGridColour(GRIDCOLOUR) canvas.canvas.BackgroundColour = BGCOLOUR if self.is_ccss: canvas.HandCursor = wx.StockCursor(wx.CURSOR_SIZING) canvas.SetCursor(canvas.HandCursor) else: canvas.canvas.Unbind(wx.EVT_LEFT_DCLICK) canvas.SetEnableDrag(False) canvas.SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT)) canvas.SetXSpec('none') canvas.SetYSpec('none') # CallAfter is needed under GTK as usual wx.CallAfter(self.draw_ccxx) if self.is_ccss: self.Bind(wx.EVT_KEY_DOWN, self.key_handler) for child in self.GetAllChildren(): child.Bind(wx.EVT_KEY_DOWN, self.key_handler) child.Bind(wx.EVT_MOUSEWHEEL, self.OnWheel) self.toggle_btn.Bind(wx.EVT_BUTTON, self.toggle_draw) self.Bind(wx.EVT_SIZE, self.OnSize) else: bg.Sizer.Add((0, 16)) self.Sizer.SetSizeHints(self) self.Sizer.Layout()
def parse_txt(self, txt): if not txt: return self.logger.info("%r" % txt) data_len = len(self.cgats[0].DATA) if (self.grid.GetNumberRows() < data_len): self.index = 0 self.index_max = data_len - 1 self.grid.AppendRows(data_len - self.grid.GetNumberRows()) for i in self.cgats[0].DATA: self.grid.SetRowLabelValue(i, "%i" % (i + 1)) row = self.cgats[0].DATA[i] RGB = [] for j, label in enumerate("RGB"): value = int(round(row["RGB_%s" % label] / 100.0 * 255)) self.grid.SetCellValue(row.SAMPLE_ID - 1, j, "%i" % value) RGB.append(value) self.grid.SetCellBackgroundColour(row.SAMPLE_ID - 1, 3, wx.Colour(*RGB)) if "Connecting to the instrument" in txt: self.Pulse(lang.getstr("instrument.initializing")) if "Spot read needs a calibration" in txt: self.is_measuring = False if "Spot read failed" in txt: self.last_error = txt if "Result is XYZ:" in txt: self.last_error = None if getcfg("measurement.play_sound"): self.measurement_sound.safe_play() # Result is XYZ: d.dddddd d.dddddd d.dddddd, D50 Lab: d.dddddd d.dddddd d.dddddd XYZ = re.search( "XYZ:\s+(-?\d+(?:\.\d+)?)\s+(-?\d+(?:\.\d+)?)\s+(-?\d+(?:\.\d+)?)", txt) if not XYZ: return XYZ = [float(v) for v in XYZ.groups()] row = self.cgats[0].DATA[self.index] if (row["RGB_R"] == 100 and row["RGB_G"] == 100 and row["RGB_B"] == 100): # White if XYZ[1] > 0: self.cgats[0].add_keyword("LUMINANCE_XYZ_CDM2", "%.6f %.6f %.6f" % tuple(XYZ)) self.white_XYZ = XYZ Lab1 = colormath.XYZ2Lab(*self.last_XYZ) Lab2 = colormath.XYZ2Lab(*XYZ) delta = colormath.delta(*Lab1 + Lab2) if debug or test or verbose > 1: safe_print("Last recorded Lab: %.4f %.4f %.4f" % Lab1) safe_print("Current Lab: %.4f %.4f %.4f" % Lab2) safe_print("Delta E to last recorded Lab: %.4f" % delta["E"]) safe_print("Abs. delta L to last recorded Lab: %.4f" % abs(delta["L"])) safe_print("Abs. delta C to last recorded Lab: %.4f" % abs(delta["C"])) if (delta["E"] > getcfg("untethered.min_delta") or (abs(delta["L"]) > getcfg("untethered.min_delta.lightness") and abs(delta["C"]) < getcfg("untethered.max_delta.chroma"))): self.measure_count += 1 if self.measure_count == 2: if getcfg("measurement.play_sound"): self.commit_sound.safe_play() self.measure_count = 0 # Reset row label self.grid.SetRowLabelValue(self.index, "%i" % (self.index + 1)) # Update CGATS query = self.cgats[0].queryi({ "RGB_R": row["RGB_R"], "RGB_G": row["RGB_G"], "RGB_B": row["RGB_B"] }) for i in query: index = query[i].SAMPLE_ID - 1 if index not in self.measured: self.measured.append(index) if index == self.index + 1: # Increment the index if we have consecutive patches self.index = index query[i]["XYZ_X"], query[i]["XYZ_Y"], query[i][ "XYZ_Z"] = XYZ if getcfg("untethered.measure.auto"): self.show_RGB(False, False) self.show_XYZ() Lab, color = self.get_Lab_RGB() for i in query: row = query[i] self.grid.SetCellBackgroundColour( query[i].SAMPLE_ID - 1, 4, wx.Colour(*color)) for j in range(3): self.grid.SetCellValue(query[i].SAMPLE_ID - 1, 5 + j, "%.2f" % Lab[j]) self.grid.MakeCellVisible(self.index, 0) self.grid.ForceRefresh() if len(self.measured) == data_len: self.finished = True self.finish_btn.Enable() else: # Jump to the next or previous unmeasured patch, if any index = self.index for i in range(self.index + 1, data_len): if (getcfg("untethered.measure.auto") or not i in self.measured): self.index = i break if self.index == index: for i in range(self.index - 1, -1, -1): if not i in self.measured: self.index = i break if self.index != index: # Mark the row containing the next/previous patch self.grid.SetRowLabelValue( self.index, "\u25ba %i" % (self.index + 1)) self.grid.MakeCellVisible(self.index, 0) if "key to take a reading" in txt and not self.last_error: if getcfg("untethered.measure.auto") and self.is_measuring: if not self.finished and self.keepGoing: self.measure() else: self.enable_btns() else: show_XYZ = self.index in self.measured delay = getcfg("untethered.measure.manual.delay") * 1000 wx.CallLater(delay, self.show_RGB, not show_XYZ) if show_XYZ: wx.CallLater(delay, self.show_XYZ) wx.CallLater(delay, self.enable_btns)
def get_sound_on_off_btn_bitmap(self): if getcfg("measurement.play_sound"): bitmap = geticon(16, "sound_volume_full") else: bitmap = geticon(16, "sound_off") return bitmap
def measurement_play_sound_handler(self, event): setcfg("measurement.play_sound", int(not (bool(getcfg("measurement.play_sound"))))) self.set_sound_on_off_btn_bitmap()
self.instrument_place_on_screen_msg = False self.instrument_sensor_position_msg = False self.is_ambient_measuring = False self.subprocess = Subprocess() self.subprocess_abort = False def abort_subprocess(self): self.safe_send("Q") def safe_send(self, bytes): print("*** Sending %r" % bytes) self.subprocess.send(bytes) return True config.initcfg() print("untethered.min_delta", getcfg("untethered.min_delta")) print("untethered.min_delta.lightness", getcfg("untethered.min_delta.lightness")) print("untethered.max_delta.chroma", getcfg("untethered.max_delta.chroma")) lang.init() lang.update_defaults() app = BaseApp(0) app.TopWindow = UntetheredFrame(start_timer=False) testchart = getcfg("testchart.file") if os.path.splitext(testchart)[1].lower() in (".icc", ".icm"): try: testchart = ICCP.ICCProfile(testchart).tags.targ except: pass try: app.TopWindow.cgats = CGATS.CGATS(testchart)
def pool_slice(func, data_in, args=(), kwds={}, num_workers=None, thread_abort=None, logfile=None, num_batches=1, progress=0): """ Process data in slices using a pool of workers and return the results. The individual worker results are returned in the same order as the original input data, irrespective of the order in which the workers finished (FIFO). Progress percentage is written to optional logfile using a background thread that monitors a queue. Note that 'func' is supposed to periodically check thread_abort.event which is passed as the first argument to 'func', and put its progress percentage into the queue which is passed as the second argument to 'func'. """ from config import getcfg if num_workers is None: num_workers = cpu_count() num_workers = max(min(int(num_workers), len(data_in)), 1) max_workers = getcfg("multiprocessing.max_cpus") if max_workers: num_workers = min(num_workers, max_workers) if num_workers == 1 or not num_batches: # Splitting the workload into batches only makes sense if there are # multiple workers num_batches = 1 chunksize = float(len(data_in)) / (num_workers * num_batches) if chunksize < 1: num_batches = 1 chunksize = float(len(data_in)) / num_workers if num_workers > 1: Pool = NonDaemonicPool manager = mp.Manager() if thread_abort is not None and not isinstance(thread_abort.event, mp.managers.EventProxy): # Replace the event with a managed instance that is compatible # with pool event = thread_abort.event thread_abort.event = manager.Event() if event.is_set(): thread_abort.event.set() else: event = None Queue = manager.Queue else: # Do it all in in the main thread of the current instance Pool = FakePool manager = None Queue = FakeQueue if thread_abort is not None: thread_abort_event = thread_abort.event else: thread_abort_event = None progress_queue = Queue() if logfile: def progress_logger(num_workers, progress=0.0): eof_count = 0 prevperc = -1 while progress < 100 * num_workers: try: inc = progress_queue.get(True, 0.1) if isinstance(inc, Exception): raise inc progress += inc except Empty: continue except IOError: break except EOFError: eof_count += 1 if eof_count == num_workers: break perc = round(progress / num_workers) if perc > prevperc: logfile.write("\r%i%%" % perc) prevperc = perc threading.Thread(target=progress_logger, args=(num_workers * num_batches, progress * num_workers * num_batches), name="ProcessProgressLogger").start() pool = Pool(num_workers) results = [] start = 0 for batch in range(num_batches): for i in range(batch * num_workers, (batch + 1) * num_workers): end = int(math.ceil(chunksize * (i + 1))) results.append( pool.apply_async( WorkerFunc(func, batch == num_batches - 1), (data_in[start:end], thread_abort_event, progress_queue) + args, kwds)) start = end # Get results exception = None data_out = [] for result in results: result = result.get() if isinstance(result, Exception): exception = result continue data_out.append(result) pool.close() pool.join() if manager: # Need to shutdown manager so it doesn't hold files in use if event: # Restore original event if thread_abort.event.is_set(): event.set() thread_abort.event = event manager.shutdown() if exception: raise exception return data_out
def __init__(self, parent=None, handler=None, keyhandler=None, start_timer=True, rows=None, cols=None): if not rows: rows = getcfg("uniformity.rows") if not cols: cols = getcfg("uniformity.cols") BaseFrame.__init__(self, parent, wx.ID_ANY, lang.getstr("report.uniformity"), style=wx.DEFAULT_FRAME_STYLE | wx.TAB_TRAVERSAL, name="displayuniformityframe") self.SetIcons(get_icon_bundle([256, 48, 32, 16], appname)) self.SetBackgroundColour(BGCOLOUR) self.sizer = wx.GridSizer(rows, cols, 0, 0) self.SetSizer(self.sizer) self.rows = rows self.cols = cols self.colors = (wx.WHITE, wx.Colour(192, 192, 192), wx.Colour(128, 128, 128), wx.Colour(64, 64, 64)) self.labels = {} self.panels = [] self.buttons = [] for index in range(rows * cols): panel = wx_Panel(self, style=wx.BORDER_SIMPLE) panel.SetBackgroundColour(BGCOLOUR) sizer = wx.BoxSizer(wx.VERTICAL) panel.SetSizer(sizer) self.panels.append(panel) button = FlatShadedNumberedButton( panel, label=lang.getstr("measure"), bitmap=getbitmap("theme/icons/10x10/record"), index=index) button.Bind(wx.EVT_BUTTON, self.measure) self.buttons.append(button) label = wx.StaticText(panel) label.SetForegroundColour(wx.WHITE) self.labels[index] = label sizer.Add(label, 1, wx.ALIGN_CENTER) sizer.Add(button, 0, wx.ALIGN_BOTTOM | wx.ALIGN_CENTER | wx.BOTTOM | wx.LEFT | wx.RIGHT, border=8) self.sizer.Add(panel, 1, wx.EXPAND) self.disable_buttons() self.keyhandler = keyhandler self.id_to_keycode = {} if sys.platform == "darwin": # Use an accelerator table for tab, space, 0-9, A-Z, numpad, # navigation keys and processing keys keycodes = [wx.WXK_TAB, wx.WXK_SPACE] keycodes.extend(list(range(ord("0"), ord("9")))) keycodes.extend(list(range(ord("A"), ord("Z")))) keycodes.extend(numpad_keycodes) keycodes.extend(nav_keycodes) keycodes.extend(processing_keycodes) for keycode in keycodes: self.id_to_keycode[wx.Window.NewControlId()] = keycode accels = [] for id, keycode in self.id_to_keycode.items(): self.Bind(wx.EVT_MENU, self.key_handler, id=id) accels.append((wx.ACCEL_NORMAL, keycode, id)) if keycode == wx.WXK_TAB: accels.append((wx.ACCEL_SHIFT, keycode, id)) self.SetAcceleratorTable(wx.AcceleratorTable(accels)) else: self.Bind(wx.EVT_CHAR_HOOK, self.key_handler) # Event handlers self.Bind(wx.EVT_CLOSE, self.OnClose, self) self.Bind(wx.EVT_MOVE, self.OnMove, self) self.timer = wx.Timer(self) if handler: self.Bind(wx.EVT_TIMER, handler, self.timer) self.Bind(wx.EVT_WINDOW_DESTROY, self.OnDestroy, self) # Final initialization steps self.logger = get_file_logger("uniformity") self._setup() self.Show() if start_timer: self.start_timer()