示例#1
0
    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()
示例#2
0
 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)
示例#5
0
 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)
示例#7
0
 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()
示例#8
0
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
示例#9
0
 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)
示例#10
0
 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()
示例#11
0
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)
示例#12
0
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
示例#13
0
 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()
示例#14
0
 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)
示例#15
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')
示例#16
0
 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
示例#17
0
 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()
示例#18
0
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
示例#19
0
 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
示例#21
0
 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
示例#23
0
 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()))
示例#24
0
    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)
示例#29
0
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
示例#30
0
    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()