diff --git a/CHANGELOG.md b/CHANGELOG.md index 11084a0..753f5d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ This document highlights high-level changes made to this program. + Migrated CI from Travis + GitLab + Appveyor CIs to Github. + Added `pre-commit` config. + Removed now-unnecessary `build_reqs` directory; moved tests to external package. ++ Formatted code with `black`. ## 1.1.2 / 2019-10-14 diff --git a/docs/conf.py b/docs/conf.py index 944d107..366c54a 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -23,7 +23,7 @@ logging.disable(logging.CRITICAL) -sys.path.insert(0, os.path.abspath('..')) +sys.path.insert(0, os.path.abspath("..")) base_dir = os.path.join(os.path.dirname(__file__), os.pardir) about = {} @@ -35,19 +35,19 @@ # If your documentation needs a minimal Sphinx version, state it here. -needs_sphinx = '1.3' +needs_sphinx = "1.3" # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ - 'sphinx.ext.autodoc', - 'sphinx.ext.doctest', - 'sphinx.ext.todo', - 'sphinx.ext.coverage', -# 'sphinx.ext.imgmath', - 'sphinx.ext.napoleon', - 'sphinx.ext.viewcode', + "sphinx.ext.autodoc", + "sphinx.ext.doctest", + "sphinx.ext.todo", + "sphinx.ext.coverage", + # 'sphinx.ext.imgmath', + "sphinx.ext.napoleon", + "sphinx.ext.viewcode", ] # Napoleon Settings @@ -63,30 +63,30 @@ napoleon_use_rtype = True # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # # source_suffix = ['.rst', '.md'] -source_suffix = '.rst' +source_suffix = ".rst" # The master toctree document. -master_doc = 'index' +master_doc = "index" # General information about the project. -project = about['__project_name__'] -copyright = '2017, {}'.format(about['__author__']) -author = about['__author__'] +project = about["__project_name__"] +copyright = "2017, {}".format(about["__author__"]) +author = about["__author__"] # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. -version = about['__version__'] +version = about["__version__"] # The full version, including alpha/beta/rc tags. -release = about['__version__'] +release = about["__version__"] # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -98,10 +98,10 @@ # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This patterns also effect to html_static_path and html_extra_path -exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] # The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' +pygments_style = "sphinx" # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = False @@ -112,7 +112,7 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # -html_theme = 'sphinx_rtd_theme' +html_theme = "sphinx_rtd_theme" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the @@ -123,13 +123,13 @@ # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] +html_static_path = ["_static"] # -- Options for HTMLHelp output ------------------------------------------ # Output file base name for HTML help builder. -htmlhelp_basename = 'wafer_mapdoc' +htmlhelp_basename = "wafer_mapdoc" # -- Options for LaTeX output --------------------------------------------- @@ -138,15 +138,15 @@ # The paper size ('letterpaper' or 'a4paper'). # # 'papersize': 'letterpaper', - + # # The font size ('10pt', '11pt' or '12pt'). # # 'pointsize': '10pt', - + # # Additional stuff for the LaTeX preamble. # # 'preamble': '', - + # # Latex figure (float) alignment # # 'figure_align': 'htbp', @@ -156,8 +156,13 @@ # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - (master_doc, 'wafer_map.tex', 'wafer\\_map Documentation', - 'Douglas Thor', 'manual'), + ( + master_doc, + "wafer_map.tex", + "wafer\\_map Documentation", + "Douglas Thor", + "manual", + ), ] @@ -165,10 +170,7 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). -man_pages = [ - (master_doc, 'wafer_map', 'wafer_map Documentation', - [author], 1) -] +man_pages = [(master_doc, "wafer_map", "wafer_map Documentation", [author], 1)] # -- Options for Texinfo output ------------------------------------------- @@ -177,10 +179,13 @@ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - (master_doc, 'wafer_map', 'wafer_map Documentation', - author, 'wafer_map', 'One line description of project.', - 'Miscellaneous'), + ( + master_doc, + "wafer_map", + "wafer_map Documentation", + author, + "wafer_map", + "One line description of project.", + "Miscellaneous", + ), ] - - - diff --git a/src/wafer_map/__init__.py b/src/wafer_map/__init__.py index c66dc5b..beff3c6 100644 --- a/src/wafer_map/__init__.py +++ b/src/wafer_map/__init__.py @@ -9,11 +9,11 @@ ### Constants ############################################################### -#__all__ = ['wm_app', 'wm_constants', 'wm_core', 'wm_frame', 'wm_info', +# __all__ = ['wm_app', 'wm_constants', 'wm_core', 'wm_frame', 'wm_info', # 'wm_legend', 'wm_utils'] -if sys.version_info < (3, ): +if sys.version_info < (3,): PY2 = True elif sys.version_info < (2, 6): raise RuntimeError("Only Python >= 2.7 is supported.") @@ -28,8 +28,11 @@ # Fix hashing for wx.Colour # See https://groups.google.com/forum/#!topic/wxpython-dev/NLd4CZv9rII import wx + ok = getattr(wx.Colour, "__hash__") if ok is None: + def _Colour___hash(self): return hash(tuple(self.Get())) + wx.Colour.__hash__ = _Colour___hash diff --git a/src/wafer_map/example.py b/src/wafer_map/example.py index 1ce5a02..4264046 100644 --- a/src/wafer_map/example.py +++ b/src/wafer_map/example.py @@ -4,16 +4,10 @@ This module is called when running ``python -m wafer_map``. """ -# --------------------------------------------------------------------------- -### Imports -# --------------------------------------------------------------------------- -# Standard Library from __future__ import absolute_import, division, print_function, unicode_literals -# Third-Party import wx -# Package/Application from wafer_map import gen_fake_data from wafer_map import wm_core from wafer_map import wm_app @@ -46,13 +40,14 @@ def standalone_app(xyd, wafer_info): [(grid_x_1, grid_y_1, data_1), (grid_x_2, grid_y_2, data_2), ..., ] """ - wm_app.WaferMapApp(xyd, - wafer_info.die_size, - wafer_info.center_xy, - wafer_info.dia, - wafer_info.edge_excl, - wafer_info.flat_excl, - ) + wm_app.WaferMapApp( + xyd, + wafer_info.die_size, + wafer_info.center_xy, + wafer_info.dia, + wafer_info.edge_excl, + wafer_info.flat_excl, + ) def add_to_existing_app(xyd, wafer_info): @@ -76,12 +71,13 @@ class ExampleFrame(wx.Frame): """Base Frame.""" def __init__(self, title, xyd, wafer_info): - wx.Frame.__init__(self, - None, # Window Parent - wx.ID_ANY, # id - title=title, # Window Title - size=(600 + 16, 500 + 38), # Size in px - ) + wx.Frame.__init__( + self, + None, # Window Parent + wx.ID_ANY, # id + title=title, # Window Title + size=(600 + 16, 500 + 38), # Size in px + ) self.xyd = xyd self.wafer_info = wafer_info @@ -92,18 +88,20 @@ def __init__(self, title, xyd, wafer_info): self.Bind(wx.EVT_CLOSE, self.OnQuit) # Create some other dummy stuff for the example - self.listbox = wx.ListBox(self, - wx.ID_ANY, - choices=['A', 'B', 'C', 'D'], - ) + self.listbox = wx.ListBox( + self, + wx.ID_ANY, + choices=["A", "B", "C", "D"], + ) self.button = wx.Button(self, wx.ID_ANY, label="Big Button!") # Create the wafer map - self.panel = wm_core.WaferMapPanel(self, - self.xyd, - self.wafer_info, - show_die_gridlines=False, - ) + self.panel = wm_core.WaferMapPanel( + self, + self.xyd, + self.wafer_info, + show_die_gridlines=False, + ) # set our layout self.hbox = wx.BoxSizer(wx.HORIZONTAL) @@ -138,20 +136,20 @@ def discrete_data_example(xyd, wafer_info): The wafer information such as die size, diameter, etc. """ import random + bins = ["Bin1", "Bin1", "Bin1", "Bin2", "Dragons", "Bin1", "Bin2"] - discrete_xyd = [(_x, _y, random.choice(bins)) - for _x, _y, _ - in xyd] + discrete_xyd = [(_x, _y, random.choice(bins)) for _x, _y, _ in xyd] - wm_app.WaferMapApp(discrete_xyd, - wafer_info.die_size, - wafer_info.center_xy, - wafer_info.dia, - wafer_info.edge_excl, - wafer_info.flat_excl, - data_type=DataType.DISCRETE, - show_die_gridlines=False, - ) + wm_app.WaferMapApp( + discrete_xyd, + wafer_info.die_size, + wafer_info.center_xy, + wafer_info.dia, + wafer_info.edge_excl, + wafer_info.flat_excl, + data_type=DataType.DISCRETE, + show_die_gridlines=False, + ) def main(): @@ -163,5 +161,6 @@ def main(): add_to_existing_app(xyd, wafer_info) discrete_data_example(xyd, wafer_info) + if __name__ == "__main__": main() diff --git a/src/wafer_map/gen_fake_data.py b/src/wafer_map/gen_fake_data.py index 0830d62..0d1c4e1 100644 --- a/src/wafer_map/gen_fake_data.py +++ b/src/wafer_map/gen_fake_data.py @@ -5,19 +5,14 @@ Typically not used by anything but the example. It's also a pretty shitty peice of code so... ye be warned. """ -# --------------------------------------------------------------------------- -### Imports -# --------------------------------------------------------------------------- -# Standard Library from __future__ import absolute_import, division, print_function, unicode_literals import math import random -# Package/Application from wafer_map import PY2 -from . import wm_info -from . import wm_utils -from . import wm_constants as wm_const +from wafer_map import wm_info +from wafer_map import wm_utils +from wafer_map import wm_constants as wm_const # Python2 Compatibility @@ -110,42 +105,43 @@ def generate_fake_data(**kwargs): dia_list = [100, 150, 200, 210] excl_list = [0, 2.5, 5, 10] offset_list = [0, 0.5, -2, 0.24] - DEFAULT_KWARGS = {'die_x': random.uniform(5, 10), - 'die_y': random.uniform(5, 10), - 'dia': random.choice(dia_list), - 'edge_excl': random.choice(excl_list), - 'flat_excl': random.choice(excl_list), - 'x_offset': random.choice(offset_list), - 'y_offset': random.choice(offset_list), - 'grid_center': None, - 'dtype': wm_const.DataType.CONTINUOUS, - } + DEFAULT_KWARGS = { + "die_x": random.uniform(5, 10), + "die_y": random.uniform(5, 10), + "dia": random.choice(dia_list), + "edge_excl": random.choice(excl_list), + "flat_excl": random.choice(excl_list), + "x_offset": random.choice(offset_list), + "y_offset": random.choice(offset_list), + "grid_center": None, + "dtype": wm_const.DataType.CONTINUOUS, + } # parse the keyword arguements, asigning defaults if not found. for key in DEFAULT_KWARGS: if key not in kwargs: kwargs[key] = DEFAULT_KWARGS[key] - die_x = kwargs['die_x'] - die_y = kwargs['die_y'] - dia = kwargs['dia'] - edge_excl = kwargs['edge_excl'] - flat_excl = kwargs['flat_excl'] - x_offset = kwargs['x_offset'] - y_offset = kwargs['y_offset'] - grid_center = kwargs['grid_center'] - dtype = kwargs['dtype'] + die_x = kwargs["die_x"] + die_y = kwargs["die_y"] + dia = kwargs["dia"] + edge_excl = kwargs["edge_excl"] + flat_excl = kwargs["flat_excl"] + x_offset = kwargs["x_offset"] + y_offset = kwargs["y_offset"] + grid_center = kwargs["grid_center"] + dtype = kwargs["dtype"] die_size = (die_x, die_y) # Determine where our wafer edge is for the flat area - flat_y = -dia/2 # assume wafer edge at first + flat_y = -dia / 2 # assume wafer edge at first if dia in wm_const.wm_FLAT_LENGTHS: # A flat is defined by SEMI M1-0302, so we calcualte where it is - flat_y = -math.sqrt((dia/2)**2 - (wm_const.wm_FLAT_LENGTHS[dia] * 0.5)**2) + flat_y = -math.sqrt((dia / 2) ** 2 - (wm_const.wm_FLAT_LENGTHS[dia] * 0.5) ** 2) # calculate the exclusion radius^2 - excl_sqrd = (dia/2)**2 + (edge_excl**2) - (dia * edge_excl) + excl_sqrd = (dia / 2) ** 2 + (edge_excl**2) - (dia * edge_excl) # 1. Generate square grid guarenteed to cover entire wafer # We'll use 2x the wafer dia so that we can move center around a bit @@ -154,7 +150,7 @@ def generate_fake_data(**kwargs): # 2. Determine the centerpoint if grid_center is None: - grid_center = (grid_max_x/2 + x_offset, grid_max_y/2 + y_offset) + grid_center = (grid_max_x / 2 + x_offset, grid_max_y / 2 + y_offset) print("Offsets: {}".format((x_offset, y_offset))) # This could be more efficient @@ -168,44 +164,50 @@ def generate_fake_data(**kwargs): coord_die_center = (coord_die_center_x, coord_die_center_y) center_rad_sqrd = coord_die_center_x**2 + coord_die_center_y**2 die_max_sqrd = wm_utils.max_dist_sqrd(coord_die_center, die_size) -# coord_lower_left_x = coord_die_center_x - die_x/2 - coord_lower_left_y = coord_die_center_y - die_y/2 -# coord_lower_left = (coord_lower_left_x, coord_lower_left_y) - if (die_max_sqrd > excl_sqrd - or coord_lower_left_y < (flat_y + flat_excl)): + # coord_lower_left_x = coord_die_center_x - die_x/2 + coord_lower_left_y = coord_die_center_y - die_y / 2 + # coord_lower_left = (coord_lower_left_x, coord_lower_left_y) + if die_max_sqrd > excl_sqrd or coord_lower_left_y < (flat_y + flat_excl): continue else: - if dtype == 'discrete': - grid_points.append((_x, - _y, - random.choice(['a', 'b', 'c']), - # these items are for debug. -# coord_lower_left, -# center_rad_sqrd, -# coord_die_center, -# die_max_sqrd, - )) + if dtype == "discrete": + grid_points.append( + ( + _x, + _y, + random.choice(["a", "b", "c"]), + # these items are for debug. + # coord_lower_left, + # center_rad_sqrd, + # coord_die_center, + # die_max_sqrd, + ) + ) else: - grid_points.append((_x, - _y, - center_rad_sqrd, - # these items are for debug. -# coord_lower_left, -# center_rad_sqrd, -# coord_die_center, -# die_max_sqrd, - )) + grid_points.append( + ( + _x, + _y, + center_rad_sqrd, + # these items are for debug. + # coord_lower_left, + # center_rad_sqrd, + # coord_die_center, + # die_max_sqrd, + ) + ) print("\nPlotting {} die.".format(len(grid_points))) # put all the wafer info into the WaferInfo class. - wafer_info = wm_info.WaferInfo(die_size, # Die Size in (X, Y) - grid_center, # Center Coord (X, Y) - dia, # Wafer Diameter - edge_excl, # Edge Exclusion - flat_excl, # Flat Exclusion - ) + wafer_info = wm_info.WaferInfo( + die_size, # Die Size in (X, Y) + grid_center, # Center Coord (X, Y) + dia, # Wafer Diameter + edge_excl, # Edge Exclusion + flat_excl, # Flat Exclusion + ) print(wafer_info) return (wafer_info, grid_points) @@ -213,12 +215,12 @@ def generate_fake_data(**kwargs): def main(): """Run when called as a module.""" - wafer, data = generate_fake_data(dtype='discrete') + wafer, data = generate_fake_data(dtype="discrete") from pprint import pprint - pprint(data) -# print() -# pprint([_i for _i in data if _i[0] in (17, 18, 19)]) + pprint(data) + # print() + # pprint([_i for _i in data if _i[0] in (17, 18, 19)]) if __name__ == "__main__": diff --git a/src/wafer_map/wm_app.py b/src/wafer_map/wm_app.py index 34f79db..75acfe5 100644 --- a/src/wafer_map/wm_app.py +++ b/src/wafer_map/wm_app.py @@ -2,16 +2,10 @@ """ A self-contained Window for a Wafer Map. """ -# --------------------------------------------------------------------------- -### Imports -# --------------------------------------------------------------------------- -# Standard Library from __future__ import absolute_import, division, print_function, unicode_literals -# Third-Party import wx -# Package / Application from . import wm_frame from . import wm_info from . import gen_fake_data @@ -57,49 +51,52 @@ class WaferMapApp(object): ``True``. """ - def __init__(self, - xyd, - die_size, - center_xy=(0, 0), - dia=150, - edge_excl=5, - flat_excl=5, - data_type=wm_const.DataType.CONTINUOUS, - high_color=wm_const.wm_HIGH_COLOR, - low_color=wm_const.wm_LOW_COLOR, - plot_range=None, - plot_die_centers=False, - show_die_gridlines=True, - ): + def __init__( + self, + xyd, + die_size, + center_xy=(0, 0), + dia=150, + edge_excl=5, + flat_excl=5, + data_type=wm_const.DataType.CONTINUOUS, + high_color=wm_const.wm_HIGH_COLOR, + low_color=wm_const.wm_LOW_COLOR, + plot_range=None, + plot_die_centers=False, + show_die_gridlines=True, + ): self.app = wx.App() - self.wafer_info = wm_info.WaferInfo(die_size, - center_xy, - dia, - edge_excl, - flat_excl, - ) + self.wafer_info = wm_info.WaferInfo( + die_size, + center_xy, + dia, + edge_excl, + flat_excl, + ) self.xyd = xyd self.data_type = data_type self.high_color = high_color self.low_color = low_color self.plot_range = plot_range self.plot_die_centers = plot_die_centers - self.show_die_gridlines=show_die_gridlines - - self.frame = wm_frame.WaferMapWindow("Wafer Map Phoenix", - self.xyd, - self.wafer_info, - data_type=self.data_type, - high_color=self.high_color, - low_color=self.low_color, -# high_color=wx.Colour(255, 0, 0), -# low_color=wx.Colour(0, 0, 255), - plot_range=self.plot_range, - size=(600, 500), - plot_die_centers=self.plot_die_centers, - show_die_gridlines=self.show_die_gridlines, - ) + self.show_die_gridlines = show_die_gridlines + + self.frame = wm_frame.WaferMapWindow( + "Wafer Map Phoenix", + self.xyd, + self.wafer_info, + data_type=self.data_type, + high_color=self.high_color, + low_color=self.low_color, + # high_color=wx.Colour(255, 0, 0), + # low_color=wx.Colour(0, 0, 255), + plot_range=self.plot_range, + size=(600, 500), + plot_die_centers=self.plot_die_centers, + show_die_gridlines=self.show_die_gridlines, + ) self.frame.Show() self.app.MainLoop() @@ -107,42 +104,42 @@ def __init__(self, def main(): """Run when called as a module.""" - wafer_info, xyd = gen_fake_data.generate_fake_data(die_x=5.43, - die_y=6.3, - dia=150, - edge_excl=4.5, - flat_excl=4.5, - x_offset=0, - y_offset=0.5, - grid_center=(29, 21.5), - ) + wafer_info, xyd = gen_fake_data.generate_fake_data( + die_x=5.43, + die_y=6.3, + dia=150, + edge_excl=4.5, + flat_excl=4.5, + x_offset=0, + y_offset=0.5, + grid_center=(29, 21.5), + ) import random + bins = ["Bin1", "Bin1", "Bin1", "Bin2", "Dragons", "Bin1", "Bin2"] - discrete_xyd = [(_x, _y, random.choice(bins)) - for _x, _y, _ - in xyd] + discrete_xyd = [(_x, _y, random.choice(bins)) for _x, _y, _ in xyd] discrete = False dtype = wm_const.DataType.CONTINUOUS -# discrete = True # uncomment this line to use discrete data + # discrete = True # uncomment this line to use discrete data if discrete: xyd = discrete_xyd dtype = wm_const.DataType.DISCRETE - WaferMapApp(xyd, - wafer_info.die_size, - wafer_info.center_xy, - wafer_info.dia, - wafer_info.edge_excl, - wafer_info.flat_excl, - data_type=dtype, -# plot_range=(0.0, 75.0**2), - plot_die_centers=True, - ) + WaferMapApp( + xyd, + wafer_info.die_size, + wafer_info.center_xy, + wafer_info.dia, + wafer_info.edge_excl, + wafer_info.flat_excl, + data_type=dtype, + # plot_range=(0.0, 75.0**2), + plot_die_centers=True, + ) if __name__ == "__main__": main() -# pass diff --git a/src/wafer_map/wm_constants.py b/src/wafer_map/wm_constants.py index da008dc..afefaf6 100644 --- a/src/wafer_map/wm_constants.py +++ b/src/wafer_map/wm_constants.py @@ -2,33 +2,25 @@ """ Constants for the wafer_map package. """ -# --------------------------------------------------------------------------- -### Imports -# --------------------------------------------------------------------------- -# Standard Library from __future__ import absolute_import, division, print_function, unicode_literals from enum import Enum -# Third-Party import wx -# --------------------------------------------------------------------------- -### Constants -# --------------------------------------------------------------------------- # Colors wm_OOR_HIGH_COLOR = wx.Colour(255, 0, 128, 255) wm_OOR_LOW_COLOR = wx.Colour(255, 128, 0, 255) -#wm_HIGH_COLOR = wx.Colour(255, 0, 0, 255) +# wm_HIGH_COLOR = wx.Colour(255, 0, 0, 255) wm_HIGH_COLOR = wx.Colour(0, 255, 128, 255) -#wm_LOW_COLOR = wx.Colour(0, 192, 0, 255) +# wm_LOW_COLOR = wx.Colour(0, 192, 0, 255) wm_LOW_COLOR = wx.Colour(128, 0, 255, 255) wm_INVALID_COLOR = wx.Colour(255, 255, 255, 255) -wm_OUTLINE_COLOR = wx.Colour(255, 255, 0, 255) # yellow -wm_WAFER_EDGE_COLOR = wx.Colour(255, 0, 0, 255) # red -wm_WAFER_CENTER_DOT_COLOR = wx.Colour(255, 0, 0, 255) # red -wm_DIE_CENTER_DOT_COLOR = wx.Colour(255, 0, 0, 255) # red -wm_CROSSHAIR_COLOR = wx.Colour(0, 255, 255, 255) # cyan +wm_OUTLINE_COLOR = wx.Colour(255, 255, 0, 255) # yellow +wm_WAFER_EDGE_COLOR = wx.Colour(255, 0, 0, 255) # red +wm_WAFER_CENTER_DOT_COLOR = wx.Colour(255, 0, 0, 255) # red +wm_DIE_CENTER_DOT_COLOR = wx.Colour(255, 0, 0, 255) # red +wm_CROSSHAIR_COLOR = wx.Colour(0, 255, 255, 255) # cyan wm_TICK_COUNT = 11 # Continuous Data Gradient sizez in px. @@ -45,14 +37,14 @@ class NoValueEnum(Enum): def __repr__(self): - return '<%s.%s>' % (self.__class__.__name__, self.name) + return "<%s.%s>" % (self.__class__.__name__, self.name) class DataType(NoValueEnum): - CONTINUOUS = 'continuous' - DISCRETE = 'discrete' + CONTINUOUS = "continuous" + DISCRETE = "discrete" class CoordType(NoValueEnum): - ABSOLUTE = 'absolute' - RELATIVE = 'relative' + ABSOLUTE = "absolute" + RELATIVE = "relative" diff --git a/src/wafer_map/wm_core.py b/src/wafer_map/wm_core.py index 492920a..e0df9c6 100644 --- a/src/wafer_map/wm_core.py +++ b/src/wafer_map/wm_core.py @@ -1,26 +1,18 @@ # -*- coding: utf-8 -*- -# pylint: disable=E1101 -# E1101 = Module X has no Y member """ The core of ``wafer_map``. """ -# --------------------------------------------------------------------------- -### Imports -# --------------------------------------------------------------------------- -# Standard Library from __future__ import absolute_import, division, print_function, unicode_literals import math -# Third-Party import numpy as np import wx from wx.lib.floatcanvas import FloatCanvas import wx.lib.colourselect as csel -# Package / Application -from . import wm_legend -from . import wm_utils -from . import wm_constants as wm_const +from wafer_map import wm_legend +from wafer_map import wm_utils +from wafer_map import wm_constants as wm_const # Module-level TODO list. @@ -71,20 +63,21 @@ class WaferMapPanel(wx.Panel): ``data_type`` is ``discrete``. """ - def __init__(self, - parent, - xyd, - wafer_info, - data_type=wm_const.DataType.CONTINUOUS, - coord_type=wm_const.CoordType.ABSOLUTE, - high_color=wm_const.wm_HIGH_COLOR, - low_color=wm_const.wm_LOW_COLOR, - plot_range=None, - plot_die_centers=False, - discrete_legend_values=None, - show_die_gridlines=True, - discrete_legend_colors=None, - ): + def __init__( + self, + parent, + xyd, + wafer_info, + data_type=wm_const.DataType.CONTINUOUS, + coord_type=wm_const.CoordType.ABSOLUTE, + high_color=wm_const.wm_HIGH_COLOR, + low_color=wm_const.wm_LOW_COLOR, + plot_range=None, + plot_die_centers=False, + discrete_legend_values=None, + show_die_gridlines=True, + discrete_legend_colors=None, + ): wx.Panel.__init__(self, parent) ### Inputs ########################################################## @@ -107,7 +100,7 @@ def __init__(self, self.die_gridlines_bool = show_die_gridlines ### Other Attributes ################################################ - self.xyd_dict = xyd_to_dict(self.xyd) # data duplication! + self.xyd_dict = xyd_to_dict(self.xyd) # data duplication! self.drag = False self.wfr_outline_bool = True self.crosshairs_bool = True @@ -128,9 +121,10 @@ def __init__(self, def _init_ui(self): """Create the UI Elements and bind various events.""" # Create items to add to our layout - self.canvas = FloatCanvas.FloatCanvas(self, - BackgroundColor="BLACK", - ) + self.canvas = FloatCanvas.FloatCanvas( + self, + BackgroundColor="BLACK", + ) # Initialize the FloatCanvas. Needs to come before adding items! self.canvas.InitAll() @@ -179,13 +173,13 @@ def _bind_events(self): # parent panel or application can't set focus to this # panel, which prevents the EVT_MOUSEWHEEL event from firing # properly. -# self.canvas.Bind(wx.EVT_LEFT_DOWN, self.on_mouse_left_down) -# self.canvas.Bind(wx.EVT_RIGHT_DOWN, self.on_mouse_right_down) -# self.canvas.Bind(wx.EVT_LEFT_UP, self.on_mouse_left_up) -# self.canvas.Bind(wx.EVT_KEY_DOWN, self._on_key_down) + # self.canvas.Bind(wx.EVT_LEFT_DOWN, self.on_mouse_left_down) + # self.canvas.Bind(wx.EVT_RIGHT_DOWN, self.on_mouse_right_down) + # self.canvas.Bind(wx.EVT_LEFT_UP, self.on_mouse_left_up) + # self.canvas.Bind(wx.EVT_KEY_DOWN, self._on_key_down) # This is supposed to fix flicker on mouse move, but it doesn't work. -# self.Bind(wx.EVT_ERASE_BACKGROUND, None) + # self.Bind(wx.EVT_ERASE_BACKGROUND, None) # Panel Events self.Bind(csel.EVT_COLOURSELECT, self.on_color_change) @@ -203,29 +197,27 @@ def _create_legend(self): unique_items = list({_die[2] for _die in self.xyd}) else: unique_items = self.discrete_legend_values - self.legend = wm_legend.DiscreteLegend(self, - labels=unique_items, - colors=self.discrete_legend_colors, - ) + self.legend = wm_legend.DiscreteLegend( + self, + labels=unique_items, + colors=self.discrete_legend_colors, + ) else: if self.plot_range is None: - p_98 = float(wm_utils.nanpercentile([_i[2] - for _i - in self.xyd], 98)) - p_02 = float(wm_utils.nanpercentile([_i[2] - for _i - in self.xyd], 2)) + p_98 = float(wm_utils.nanpercentile([_i[2] for _i in self.xyd], 98)) + p_02 = float(wm_utils.nanpercentile([_i[2] for _i in self.xyd], 2)) data_min = min([die[2] for die in self.xyd]) data_max = max([die[2] for die in self.xyd]) self.plot_range = (data_min, data_max) self.plot_range = (p_02, p_98) - self.legend = wm_legend.ContinuousLegend(self, - self.plot_range, - self.high_color, - self.low_color, - ) + self.legend = wm_legend.ContinuousLegend( + self, + self.plot_range, + self.high_color, + self.low_color, + ) def _clear_canvas(self): """Clear the canvas.""" @@ -243,43 +235,47 @@ def draw_die(self): color = self.legend.get_color(die[2]) # Determine the die's lower-left coordinate - lower_left_coord = wm_utils.grid_to_rect_coord(die[:2], - self.die_size, - self.grid_center) + lower_left_coord = wm_utils.grid_to_rect_coord( + die[:2], self.die_size, self.grid_center + ) # Draw the die on the canvas - self.canvas.AddRectangle(lower_left_coord, - self.die_size, - LineWidth=1, - FillColor=color, - ) + self.canvas.AddRectangle( + lower_left_coord, + self.die_size, + LineWidth=1, + FillColor=color, + ) def draw_die_center(self): """Plot the die centers as a small dot.""" centers = [] for die in self.xyd: # Determine the die's lower-left coordinate - lower_left_coord = wm_utils.grid_to_rect_coord(die[:2], - self.die_size, - self.grid_center) + lower_left_coord = wm_utils.grid_to_rect_coord( + die[:2], self.die_size, self.grid_center + ) # then adjust back to the die center - lower_left_coord = (lower_left_coord[0] + self.die_size[0] / 2, - lower_left_coord[1] + self.die_size[1] / 2) - - circ = FloatCanvas.Circle(lower_left_coord, - 0.5, - FillColor=wm_const.wm_DIE_CENTER_DOT_COLOR, - ) + lower_left_coord = ( + lower_left_coord[0] + self.die_size[0] / 2, + lower_left_coord[1] + self.die_size[1] / 2, + ) + + circ = FloatCanvas.Circle( + lower_left_coord, + 0.5, + FillColor=wm_const.wm_DIE_CENTER_DOT_COLOR, + ) centers.append(circ) return FloatCanvas.Group(centers) def draw_wafer_objects(self): """Draw and add the various wafer objects.""" - self.wafer_outline = draw_wafer_outline(self.wafer_info.dia, - self.wafer_info.edge_excl, - self.wafer_info.flat_excl) + self.wafer_outline = draw_wafer_outline( + self.wafer_info.dia, self.wafer_info.edge_excl, self.wafer_info.flat_excl + ) self.canvas.AddObject(self.wafer_outline) if self.die_gridlines_bool: self.die_gridlines = draw_die_gridlines(self.wafer_info) @@ -338,7 +334,7 @@ def toggle_legend(self): """Toggle the legend on and off.""" if self.legend_bool: self.hbox.Remove(0) - self.Layout() # forces update of layout + self.Layout() # forces update of layout self.legend_bool = False else: self.hbox.Insert(0, self.legend, 0) @@ -372,20 +368,26 @@ def _on_key_down(self, event): D: Toggle die centers """ # TODO: Decide if I want to move this to a class attribute - keycodes = {wx.WXK_HOME: self.zoom_fill, # "Home - 79: self.toggle_outline, # "O" - 67: self.toggle_crosshairs, # "C" - 76: self.toggle_legend, # "L" - 68: self.toggle_die_centers, # "D" - } - -# print("panel event!") + keycodes = { + # Home + wx.WXK_HOME: self.zoom_fill, + # "O" + 79: self.toggle_outline, + # "C" + 67: self.toggle_crosshairs, + # "L" + 76: self.toggle_legend, + # "D" + 68: self.toggle_die_centers, + } + + # print("panel event!") key = event.GetKeyCode() if key in keycodes.keys(): keycodes[key]() else: -# print("KeyCode: {}".format(key)) + # print("KeyCode: {}".format(key)) pass def _on_first_paint(self, event): @@ -393,7 +395,7 @@ def _on_first_paint(self, event): # disable the handler for future paint events self.canvas.Bind(wx.EVT_PAINT, None) - #TODO: Fix a flicker-type event that occurs on this call + # TODO: Fix a flicker-type event that occurs on this call self.zoom_fill() def on_color_change(self, event): @@ -408,8 +410,8 @@ def on_color_change(self, event): self.canvas.AddObject(self.die_centers) self.draw_wafer_objects() self.canvas.Draw(True) -# self.canvas.Unbind(FloatCanvas.EVT_MOUSEWHEEL) -# self.canvas.Bind(FloatCanvas.EVT_MOUSEWHEEL, self.on_mouse_wheel) + # self.canvas.Unbind(FloatCanvas.EVT_MOUSEWHEEL) + # self.canvas.Bind(FloatCanvas.EVT_MOUSEWHEEL, self.on_mouse_wheel) def on_move_timer(self, event=None): """ @@ -417,7 +419,7 @@ def on_move_timer(self, event=None): This is needed to prevent buffers from being rebuilt too often. """ -# self.canvas.MoveImage(self.diff_loc, 'Pixel', ReDraw=True) + # self.canvas.MoveImage(self.diff_loc, 'Pixel', ReDraw=True) self.canvas.Draw() def on_mouse_wheel(self, event): @@ -434,7 +436,7 @@ def on_mouse_wheel(self, event): # Allows for zoom acceleration: fast wheel move = large zoom. # factor < 1: zoom out. factor > 1: zoom in sign = abs(speed) / speed - factor = (abs(speed) * wm_const.wm_ZOOM_FACTOR)**sign + factor = (abs(speed) * wm_const.wm_ZOOM_FACTOR) ** sign # Changes to FloatCanvas.Zoom mean we need to do the following # rather than calling the zoom() function. @@ -442,10 +444,10 @@ def on_mouse_wheel(self, event): # we can call PixelToWorld(pos) again and get a different value! oldpoint = self.canvas.PixelToWorld(pos) self.canvas.Scale = self.canvas.Scale * factor - self.canvas.SetToNewScale(False) # sets new scale but no redraw + self.canvas.SetToNewScale(False) # sets new scale but no redraw newpoint = self.canvas.PixelToWorld(pos) delta = newpoint - oldpoint - self.canvas.MoveImage(-delta, 'World') # performs the redraw + self.canvas.MoveImage(-delta, "World") # performs the redraw def on_mouse_move(self, event): """Update the status bar with the world coordinates.""" @@ -454,10 +456,11 @@ def on_mouse_move(self, event): ds_x, ds_y = self.die_size gc_x, gc_y = self.grid_center - dc_x, dc_y = wm_utils.coord_to_grid(event.Coords, - self.die_size, - self.grid_center, - ) + dc_x, dc_y = wm_utils.coord_to_grid( + event.Coords, + self.die_size, + self.grid_center, + ) # lookup the die value grid = "x{}y{}" @@ -469,32 +472,39 @@ def on_mouse_move(self, event): # create the status bar string coord_str = "{x:0.3f}, {y:0.3f}" - mouse_coord = "(" + coord_str.format(x=event.Coords[0], - y=event.Coords[1], - ) + ")" - - die_radius = math.sqrt((ds_x * (gc_x - dc_x))**2 - + (ds_y * (gc_y - dc_y))**2) - mouse_radius = math.sqrt(event.Coords[0]**2 + event.Coords[1]**2) + mouse_coord = ( + "(" + + coord_str.format( + x=event.Coords[0], + y=event.Coords[1], + ) + + ")" + ) + + die_radius = math.sqrt( + (ds_x * (gc_x - dc_x)) ** 2 + (ds_y * (gc_y - dc_y)) ** 2 + ) + mouse_radius = math.sqrt(event.Coords[0] ** 2 + event.Coords[1] ** 2) status_str = "Die {d_grid} :: Radius = {d_rad:0.3f} :: Value = {d_val} " status_str += "Mouse {m_coord} :: Radius = {m_rad:0.3f}" - status_str = status_str.format(d_grid=die_grid, # grid - d_val=die_val, # value - d_rad=die_radius, # radius - m_coord=mouse_coord, # coord - m_rad=mouse_radius, # radius - ) + status_str = status_str.format( + d_grid=die_grid, # grid + d_val=die_val, # value + d_rad=die_radius, # radius + m_coord=mouse_coord, # coord + m_rad=mouse_radius, # radius + ) try: parent.SetStatusText(status_str) - except: # TODO: put in exception types. + except: # TODO: put in exception types. pass # If we're dragging, actually move the image. if self.drag: self.end_move_loc = np.array(event.GetPosition()) self.diff_loc = self.mid_move_loc - self.end_move_loc - self.canvas.MoveImage(self.diff_loc, 'Pixel', ReDraw=True) + self.canvas.MoveImage(self.diff_loc, "Pixel", ReDraw=True) self.mid_move_loc = self.end_move_loc # doesn't appear to do anything... @@ -521,18 +531,18 @@ def on_mouse_middle_up(self, event): if self.start_move_loc is not None: self.end_move_loc = np.array(event.GetPosition()) self.diff_loc = self.mid_move_loc - self.end_move_loc - self.canvas.MoveImage(self.diff_loc, 'Pixel', ReDraw=True) + self.canvas.MoveImage(self.diff_loc, "Pixel", ReDraw=True) # change the cursor back to normal self.SetCursor(wx.Cursor(wx.CURSOR_ARROW)) def on_mouse_left_down(self, event): """Start making the zoom-to-box box.""" -# print("Left mouse down!") -# pcoord = event.GetPosition() -# wcoord = self.canvas.PixelToWorld(pcoord) -# string = "Pixel Coord = {} \tWorld Coord = {}" -# print(string.format(pcoord, wcoord)) + # print("Left mouse down!") + # pcoord = event.GetPosition() + # wcoord = self.canvas.PixelToWorld(pcoord) + # string = "Pixel Coord = {} \tWorld Coord = {}" + # print(string.format(pcoord, wcoord)) # TODO: Look into what I was doing here. Why no 'self' on parent? parent = wx.GetTopLevelParent(self) wx.PostEvent(self.parent, event) @@ -550,10 +560,6 @@ def on_mouse_right_up(self, event): print("Right mouse up!") -# --------------------------------------------------------------------------- -### Module Functions -# --------------------------------------------------------------------------- - def xyd_to_dict(xyd_list): """Convert the xyd list to a dict of xNNyNN key-value pairs.""" return {"x{}y{}".format(_x, _y): _d for _x, _y, _d in xyd_list} @@ -579,16 +585,17 @@ def draw_wafer_outline(dia=150, excl=5, flat=None): :class:`wx.lib.floatcanvas.FloatCanvas.Group` A ``Group`` that can be added to any floatcanvas.FloatCanvas instance. """ - rad = float(dia)/2.0 + rad = float(dia) / 2.0 if flat is None: flat = excl # Full wafer outline circle - circ = FloatCanvas.Circle((0, 0), - dia, - LineColor=wm_const.wm_OUTLINE_COLOR, - LineWidth=1, - ) + circ = FloatCanvas.Circle( + (0, 0), + dia, + LineColor=wm_const.wm_OUTLINE_COLOR, + LineWidth=1, + ) # Calculate the exclusion Radius exclRad = 0.5 * (dia - 2.0 * excl) @@ -596,15 +603,16 @@ def draw_wafer_outline(dia=150, excl=5, flat=None): if dia in wm_const.wm_FLAT_LENGTHS: # A flat is defined, so we draw it. flat_size = wm_const.wm_FLAT_LENGTHS[dia] - x = flat_size/2 - y = -math.sqrt(rad**2 - x**2) # Wfr Flat's Y Location + x = flat_size / 2 + y = -math.sqrt(rad**2 - x**2) # Wfr Flat's Y Location - arc = FloatCanvas.Arc((x, y), - (-x, y), - (0, 0), - LineColor=wm_const.wm_WAFER_EDGE_COLOR, - LineWidth=3, - ) + arc = FloatCanvas.Arc( + (x, y), + (-x, y), + (0, 0), + LineColor=wm_const.wm_WAFER_EDGE_COLOR, + LineWidth=3, + ) # actually a wafer flat, but called notch notch = draw_wafer_flat(rad, wm_const.wm_FLAT_LENGTHS[dia]) @@ -613,22 +621,24 @@ def draw_wafer_outline(dia=150, excl=5, flat=None): FSSflatY = y + flat if exclRad < abs(FSSflatY): # Then draw a circle with no flat - excl_arc = FloatCanvas.Circle((0, 0), - exclRad * 2, - LineColor=wm_const.wm_WAFER_EDGE_COLOR, - LineWidth=3, - ) + excl_arc = FloatCanvas.Circle( + (0, 0), + exclRad * 2, + LineColor=wm_const.wm_WAFER_EDGE_COLOR, + LineWidth=3, + ) excl_group = FloatCanvas.Group([excl_arc]) else: FSSflatX = math.sqrt(exclRad**2 - FSSflatY**2) # Define the wafer arc - excl_arc = FloatCanvas.Arc((FSSflatX, FSSflatY), - (-FSSflatX, FSSflatY), - (0, 0), - LineColor=wm_const.wm_WAFER_EDGE_COLOR, - LineWidth=3, - ) + excl_arc = FloatCanvas.Arc( + (FSSflatX, FSSflatY), + (-FSSflatX, FSSflatY), + (0, 0), + LineColor=wm_const.wm_WAFER_EDGE_COLOR, + LineWidth=3, + ) excl_notch = draw_wafer_flat(exclRad, FSSflatX * 2) excl_group = FloatCanvas.Group([excl_arc, excl_notch]) @@ -637,23 +647,25 @@ def draw_wafer_outline(dia=150, excl=5, flat=None): ang = 2.5 start_xy, end_xy = calc_flat_coords(rad, ang) - arc = FloatCanvas.Arc(start_xy, - end_xy, - (0, 0), - LineColor=wm_const.wm_WAFER_EDGE_COLOR, - LineWidth=3, - ) + arc = FloatCanvas.Arc( + start_xy, + end_xy, + (0, 0), + LineColor=wm_const.wm_WAFER_EDGE_COLOR, + LineWidth=3, + ) notch = draw_wafer_notch(rad) # Flat not defined, so use a notch to denote wafer orientation. start_xy, end_xy = calc_flat_coords(exclRad, ang) - excl_arc = FloatCanvas.Arc(start_xy, - end_xy, - (0, 0), - LineColor=wm_const.wm_WAFER_EDGE_COLOR, - LineWidth=3, - ) + excl_arc = FloatCanvas.Arc( + start_xy, + end_xy, + (0, 0), + LineColor=wm_const.wm_WAFER_EDGE_COLOR, + LineWidth=3, + ) excl_notch = draw_wafer_notch(exclRad) excl_group = FloatCanvas.Group([excl_arc, excl_notch]) @@ -729,21 +741,24 @@ def calc_flat_coords(radius, angle): def draw_crosshairs(dia=150, dot=False): """Draw the crosshairs or wafer center dot.""" if dot: - circ = FloatCanvas.Circle((0, 0), - 2.5, - FillColor=wm_const.wm_WAFER_CENTER_DOT_COLOR, - ) + circ = FloatCanvas.Circle( + (0, 0), + 2.5, + FillColor=wm_const.wm_WAFER_CENTER_DOT_COLOR, + ) return FloatCanvas.Group([circ]) else: # Default: use crosshairs rad = dia / 2 - xline = FloatCanvas.Line([(rad * 1.05, 0), (-rad * 1.05, 0)], - LineColor=wx.CYAN, - ) - yline = FloatCanvas.Line([(0, rad * 1.05), (0, -rad * 1.05)], - LineColor=wx.CYAN, - ) + xline = FloatCanvas.Line( + [(rad * 1.05, 0), (-rad * 1.05, 0)], + LineColor=wx.CYAN, + ) + yline = FloatCanvas.Line( + [(0, rad * 1.05), (0, -rad * 1.05)], + LineColor=wx.CYAN, + ) return FloatCanvas.Group([xline, yline]) @@ -772,7 +787,7 @@ def draw_die_gridlines(wf): pos_vert = np.arange(x_ref, edge, x_size) neg_vert = np.arange(x_ref, -edge, -x_size) - y_ref = math.modf(wf.center_xy[1])[0] * y_size + (y_size/2) + y_ref = math.modf(wf.center_xy[1])[0] * y_size + (y_size / 2) pos_horiz = np.arange(y_ref, edge, y_size) neg_horiz = np.arange(y_ref, -edge, -y_size) @@ -790,24 +805,26 @@ def draw_die_gridlines(wf): def draw_wafer_flat(rad, flat_length): """Draw a wafer flat for a given radius and flat length.""" - x = flat_length/2 + x = flat_length / 2 y = -math.sqrt(rad**2 - x**2) - flat = FloatCanvas.Line([(-x, y), (x, y)], - LineColor=wm_const.wm_WAFER_EDGE_COLOR, - LineWidth=3, - ) + flat = FloatCanvas.Line( + [(-x, y), (x, y)], + LineColor=wm_const.wm_WAFER_EDGE_COLOR, + LineWidth=3, + ) return flat -def draw_excl_flat(rad, flat_y, line_width=1, line_color='black'): +def draw_excl_flat(rad, flat_y, line_width=1, line_color="black"): """Draw a wafer flat for a given radius and flat length.""" flat_x = math.sqrt(rad**2 - flat_y**2) - flat = FloatCanvas.Line([(-flat_x, flat_y), (flat_x, flat_y)], - LineColor=wm_const.wm_WAFER_EDGE_COLOR, - LineWidth=3, - ) + flat = FloatCanvas.Line( + [(-flat_x, flat_y), (flat_x, flat_y)], + LineColor=wm_const.wm_WAFER_EDGE_COLOR, + LineWidth=3, + ) return flat @@ -817,14 +834,17 @@ def draw_wafer_notch(rad): ang_rad = ang * math.pi / 180 # Define the Notch as a series of 3 (x, y) points - xy_points = [(-rad * math.sin(ang_rad), -rad * math.cos(ang_rad)), - (0, -rad*0.95), - (rad * math.sin(ang_rad), -rad * math.cos(ang_rad))] - - notch = FloatCanvas.Line(xy_points, - LineColor=wm_const.wm_WAFER_EDGE_COLOR, - LineWidth=2, - ) + xy_points = [ + (-rad * math.sin(ang_rad), -rad * math.cos(ang_rad)), + (0, -rad * 0.95), + (rad * math.sin(ang_rad), -rad * math.cos(ang_rad)), + ] + + notch = FloatCanvas.Line( + xy_points, + LineColor=wm_const.wm_WAFER_EDGE_COLOR, + LineWidth=2, + ) return notch diff --git a/src/wafer_map/wm_frame.py b/src/wafer_map/wm_frame.py index 96f794b..529cbc1 100644 --- a/src/wafer_map/wm_frame.py +++ b/src/wafer_map/wm_frame.py @@ -1,19 +1,11 @@ # -*- coding: utf-8 -*- -# pylint: disable=E1101 -# E1101 = Module X has no Y member """ This is the main window of the Wafer Map application. """ -# --------------------------------------------------------------------------- -### Imports -# --------------------------------------------------------------------------- -# Standard Library from __future__ import absolute_import, division, print_function, unicode_literals -# Third-Party import wx -# Package / Application from . import wm_core from . import wm_constants as wm_const @@ -58,24 +50,26 @@ class WaferMapWindow(wx.Frame): ``True``. """ - def __init__(self, - title, - xyd, - wafer_info, - size=(800, 600), - data_type=wm_const.DataType.CONTINUOUS, - high_color=wm_const.wm_HIGH_COLOR, - low_color=wm_const.wm_LOW_COLOR, - plot_range=None, - plot_die_centers=False, - show_die_gridlines=True, - ): - wx.Frame.__init__(self, - None, - wx.ID_ANY, - title=title, - size=size, - ) + def __init__( + self, + title, + xyd, + wafer_info, + size=(800, 600), + data_type=wm_const.DataType.CONTINUOUS, + high_color=wm_const.wm_HIGH_COLOR, + low_color=wm_const.wm_LOW_COLOR, + plot_range=None, + plot_die_centers=False, + show_die_gridlines=True, + ): + wx.Frame.__init__( + self, + None, + wx.ID_ANY, + title=title, + size=size, + ) self.xyd = xyd self.wafer_info = wafer_info # backwards compatability @@ -114,16 +108,17 @@ def _init_ui(self): if __name__ == "__main__": self.panel = None else: - self.panel = wm_core.WaferMapPanel(self, - self.xyd, - self.wafer_info, - data_type=self.data_type, - high_color=self.high_color, - low_color=self.low_color, - plot_range=self.plot_range, - plot_die_centers=self.plot_die_centers, - show_die_gridlines=self.show_die_gridlines, - ) + self.panel = wm_core.WaferMapPanel( + self, + self.xyd, + self.wafer_info, + data_type=self.data_type, + high_color=self.high_color, + low_color=self.low_color, + plot_range=self.plot_range, + plot_die_centers=self.plot_die_centers, + show_die_gridlines=self.show_die_gridlines, + ) # TODO: There's gotta be a more scalable way to make menu items # and bind events... I'll run out of names if I have too many items. @@ -139,84 +134,94 @@ def _create_menus(self): def _create_menu_items(self): """Create each item for each menu.""" ### Menu: File (mf_) ### -# self.mf_new = wx.MenuItem(self.mfile, -# wx.ID_ANY, -# "&New\tCtrl+N", -# "TestItem") -# self.mf_open = wx.MenuItem(self.mfile, -# wx.ID_ANY, -# "&Open\tCtrl+O", -# "TestItem") - self.mf_close = wx.MenuItem(self.mfile, - wx.ID_ANY, - "&Close\tCtrl+Q", - "TestItem", - ) + # self.mf_new = wx.MenuItem(self.mfile, + # wx.ID_ANY, + # "&New\tCtrl+N", + # "TestItem") + # self.mf_open = wx.MenuItem(self.mfile, + # wx.ID_ANY, + # "&Open\tCtrl+O", + # "TestItem") + self.mf_close = wx.MenuItem( + self.mfile, + wx.ID_ANY, + "&Close\tCtrl+Q", + "TestItem", + ) ### Menu: Edit (me_) ### - self.me_redraw = wx.MenuItem(self.medit, - wx.ID_ANY, - "&Redraw", - "Force Redraw", - ) + self.me_redraw = wx.MenuItem( + self.medit, + wx.ID_ANY, + "&Redraw", + "Force Redraw", + ) ### Menu: View (mv_) ### - self.mv_zoomfit = wx.MenuItem(self.mview, - wx.ID_ANY, - "Zoom &Fit\tHome", - "Zoom to fit", - ) - self.mv_crosshairs = wx.MenuItem(self.mview, - wx.ID_ANY, - "Crosshairs\tC", - "Show or hide the crosshairs", - wx.ITEM_CHECK, - ) - self.mv_outline = wx.MenuItem(self.mview, - wx.ID_ANY, - "Wafer Outline\tO", - "Show or hide the wafer outline", - wx.ITEM_CHECK, - ) - self.mv_diecenters = wx.MenuItem(self.mview, - wx.ID_ANY, - "Die Centers\tD", - "Show or hide the die centers", - wx.ITEM_CHECK, - ) - self.mv_legend = wx.MenuItem(self.mview, - wx.ID_ANY, - "Legend\tL", - "Show or hide the legend", - wx.ITEM_CHECK, - ) + self.mv_zoomfit = wx.MenuItem( + self.mview, + wx.ID_ANY, + "Zoom &Fit\tHome", + "Zoom to fit", + ) + self.mv_crosshairs = wx.MenuItem( + self.mview, + wx.ID_ANY, + "Crosshairs\tC", + "Show or hide the crosshairs", + wx.ITEM_CHECK, + ) + self.mv_outline = wx.MenuItem( + self.mview, + wx.ID_ANY, + "Wafer Outline\tO", + "Show or hide the wafer outline", + wx.ITEM_CHECK, + ) + self.mv_diecenters = wx.MenuItem( + self.mview, + wx.ID_ANY, + "Die Centers\tD", + "Show or hide the die centers", + wx.ITEM_CHECK, + ) + self.mv_legend = wx.MenuItem( + self.mview, + wx.ID_ANY, + "Legend\tL", + "Show or hide the legend", + wx.ITEM_CHECK, + ) # Menu: Options (mo_) ### - self.mo_test = wx.MenuItem(self.mopts, - wx.ID_ANY, - "&Test", - "Nothing", - ) - self.mo_high_color = wx.MenuItem(self.mopts, - wx.ID_ANY, - "Set &High Color", - "Choose the color for high values", - ) - self.mo_low_color = wx.MenuItem(self.mopts, - wx.ID_ANY, - "Set &Low Color", - "Choose the color for low values", - ) + self.mo_test = wx.MenuItem( + self.mopts, + wx.ID_ANY, + "&Test", + "Nothing", + ) + self.mo_high_color = wx.MenuItem( + self.mopts, + wx.ID_ANY, + "Set &High Color", + "Choose the color for high values", + ) + self.mo_low_color = wx.MenuItem( + self.mopts, + wx.ID_ANY, + "Set &Low Color", + "Choose the color for low values", + ) def _add_menu_items(self): """Append MenuItems to each menu.""" -# self.mfile.Append(self.mf_new) -# self.mfile.Append(self.mf_open) + # self.mfile.Append(self.mf_new) + # self.mfile.Append(self.mf_open) self.mfile.Append(self.mf_close) self.medit.Append(self.me_redraw) -# self.medit.Append(self.me_test1) -# self.medit.Append(self.me_test2) + # self.medit.Append(self.me_test1) + # self.medit.Append(self.me_test2) self.mview.Append(self.mv_zoomfit) self.mview.AppendSeparator() @@ -249,8 +254,8 @@ def _bind_events(self): # If I define an ID to the menu item, then I can use that instead of # and event source: - #self.mo_test = wx.MenuItem(self.mopts, 402, "&Test", "Nothing") - #self.Bind(wx.EVT_MENU, self.on_zoom_fit, id=402) + # self.mo_test = wx.MenuItem(self.mopts, 402, "&Test", "Nothing") + # self.Bind(wx.EVT_MENU, self.on_zoom_fit, id=402) def on_quit(self, event): """Action for the quit event.""" @@ -295,7 +300,7 @@ def on_change_high_color(self, event): if cd.ShowModal() == wx.ID_OK: new_color = cd.GetColourData().Colour print("The color {} was chosen!".format(new_color)) - self.panel.on_color_change({'high': new_color, 'low': None}) + self.panel.on_color_change({"high": new_color, "low": None}) self.panel.Refresh() else: print("no color chosen :-(") @@ -311,7 +316,7 @@ def on_change_low_color(self, event): if cd.ShowModal() == wx.ID_OK: new_color = cd.GetColourData().Colour print("The color {} was chosen!".format(new_color)) - self.panel.on_color_change({'high': None, 'low': new_color}) + self.panel.on_color_change({"high": None, "low": new_color}) self.panel.Refresh() else: print("no color chosen :-(") @@ -325,5 +330,6 @@ def main(): frame.Show() app.MainLoop() + if __name__ == "__main__": main() diff --git a/src/wafer_map/wm_info.py b/src/wafer_map/wm_info.py index d199a2b..6b5602b 100644 --- a/src/wafer_map/wm_info.py +++ b/src/wafer_map/wm_info.py @@ -3,6 +3,7 @@ The :class:`wafer_map.wm_info.WaferInfo` class. """ + class WaferInfo(object): """ Contains the wafer information. @@ -38,12 +39,13 @@ def __str__(self): Edge Excl: {} Flat Excl: {} """ - return string.format(self.dia, - self.die_size, - self.center_xy, - self.edge_excl, - self.flat_excl, - ) + return string.format( + self.dia, + self.die_size, + self.center_xy, + self.edge_excl, + self.flat_excl, + ) def main(): diff --git a/src/wafer_map/wm_legend.py b/src/wafer_map/wm_legend.py index 7ce51ad..ae4f205 100644 --- a/src/wafer_map/wm_legend.py +++ b/src/wafer_map/wm_legend.py @@ -2,23 +2,17 @@ """ Draws the wafer map legend. """ -# --------------------------------------------------------------------------- -### Imports -# --------------------------------------------------------------------------- -# Standard Library from __future__ import absolute_import, division, print_function, unicode_literals import colorsys from collections import OrderedDict -# Third-Party import wx from wx.lib.floatcanvas import FloatCanvas import wx.lib.colourselect as csel -# Package / Application from wafer_map import PY2 -from . import wm_utils -from . import wm_constants as wm_const +from wafer_map import wm_utils +from wafer_map import wm_constants as wm_const # TODO: Update to Bezier Curves for colors. See http://bsou.io/p/3 @@ -98,15 +92,16 @@ class calculates the color directly. 6. Profit. """ - def __init__(self, - parent, - plot_range, - high_color=wm_const.wm_HIGH_COLOR, - low_color=wm_const.wm_LOW_COLOR, - num_ticks=wm_const.wm_TICK_COUNT, - oor_high_color=wm_const.wm_OOR_HIGH_COLOR, - oor_low_color=wm_const.wm_OOR_LOW_COLOR, - ): + def __init__( + self, + parent, + plot_range, + high_color=wm_const.wm_HIGH_COLOR, + low_color=wm_const.wm_LOW_COLOR, + num_ticks=wm_const.wm_TICK_COUNT, + oor_high_color=wm_const.wm_OOR_HIGH_COLOR, + oor_low_color=wm_const.wm_OOR_LOW_COLOR, + ): wx.Panel.__init__(self, parent) ### Inputs ########################################################## @@ -123,6 +118,7 @@ def __init__(self, # These get set in set_sizes(), but are here to remind me that # the instance attribute exists and what they are. # Values are in px. + # fmt: off self.text_h = None # text height self.text_w = None # Length of longest tick label self.grad_w = None # gradient width @@ -136,25 +132,28 @@ def __init__(self, self.tick_start_x = None # tick label left pixel coord self.dc_w = None # total bitmap width self.dc_h = None # total bitmap height + # fmt: on ### Other Instance Attributes ####################################### self.ticks = None - self.gradient = wm_utils.LinearGradient(self.low_color, - self.high_color) + self.gradient = wm_utils.LinearGradient(self.low_color, self.high_color) ### Remainder of __init__ ########################################### # Create the MemoryDC now - we'll add the bitmap later. self.mdc = wx.MemoryDC() - self.mdc.SetFont(wx.Font(9, - wx.FONTFAMILY_SWISS, - wx.FONTSTYLE_NORMAL, - wx.FONTWEIGHT_NORMAL, - )) + self.mdc.SetFont( + wx.Font( + 9, + wx.FONTFAMILY_SWISS, + wx.FONTSTYLE_NORMAL, + wx.FONTWEIGHT_NORMAL, + ) + ) self.set_sizes() # Create EmptyBitmap in our MemoryDC where we'll do all our drawing. -# self.mdc.SelectObject(wx.EmptyBitmap(self.dc_w, self.dc_h)) + # self.mdc.SelectObject(wx.EmptyBitmap(self.dc_w, self.dc_h)) self.mdc.SelectObject(wx.Bitmap(self.dc_w, self.dc_h)) # Draw the entire thing @@ -165,10 +164,6 @@ def __init__(self, self._init_ui() - ### #-------------------------------------------------------------------- - ### Methods - ### #-------------------------------------------------------------------- - def _init_ui(self): """Add a Sizer that is the same size as the MemoryDC.""" self.hbox = wx.BoxSizer(wx.HORIZONTAL) @@ -179,9 +174,9 @@ def _bind_events(self): """Bind events to various event handlers.""" self.Bind(wx.EVT_PAINT, self._on_paint) self.Bind(wx.EVT_SIZE, self._on_size) -# self.Bind(wx.EVT_MOTION, self.on_mouse_move) -# self.Bind(wx.EVT_LEFT_DOWN, self.on_mouse_left_down) -# self.Bind(wx.EVT_RIGHT_DOWN, self.on_mouse_right_down) + # self.Bind(wx.EVT_MOTION, self.on_mouse_move) + # self.Bind(wx.EVT_LEFT_DOWN, self.on_mouse_left_down) + # self.Bind(wx.EVT_RIGHT_DOWN, self.on_mouse_right_down) def get_color(self, value): """Get a color from the gradient.""" @@ -193,19 +188,17 @@ def get_color(self, value): color = self.oor_low_color else: try: -# pxl = int(wm_utils.rescale(value, -# self.plot_range, -# (self.grad_end_y - 1, -# self.grad_start_y))) -# -# x_pt = self.grad_w // 2 + self.grad_start_x -# point = (x_pt, pxl) -# color = self.mdc.GetPixelPoint(point) + # pxl = int(wm_utils.rescale(value, + # self.plot_range, + # (self.grad_end_y - 1, + # self.grad_start_y))) + # + # x_pt = self.grad_w // 2 + self.grad_start_x + # point = (x_pt, pxl) + # color = self.mdc.GetPixelPoint(point) # New Method - pxl = wm_utils.rescale(value, - self.plot_range, - (0, 1)) + pxl = wm_utils.rescale(value, self.plot_range, (0, 1)) color = self.gradient.get_color(pxl) color = wx.Colour(*color) except ValueError: @@ -222,18 +215,18 @@ def calc_ticks(self): pr = self.plot_range[1] - self.plot_range[0] spacing = pr / (self.num_ticks - 1) - tick_values = wm_utils.frange(self.plot_range[0], - self.plot_range[1] + 1, - spacing) + tick_values = wm_utils.frange( + self.plot_range[0], self.plot_range[1] + 1, spacing + ) ticks = [] for tick in tick_values: string = "{:.3f}".format(tick) value = tick # `grad_end_y - 1` so that the bottom tick is aligned correctly. - pixel = wm_utils.rescale(tick, - self.plot_range, - (self.grad_end_y - 1, self.grad_start_y)) + pixel = wm_utils.rescale( + tick, self.plot_range, (self.grad_end_y - 1, self.grad_start_y) + ) # Putting gradient_end_y as the "low" for rescale makes the # high value be at the north end and the low value at the south. ticks.append((string, value, pixel)) @@ -255,8 +248,7 @@ def draw_ticks(self, ticks): # Sorry, everything is measured from right to left... tick_end = self.grad_start_x - self.spacer tick_start = tick_end - self.tick_w - self.mdc.DrawLine(tick_start, tick[2], - tick_end, tick[2]) + self.mdc.DrawLine(tick_start, tick[2], tick_end, tick[2]) # Text origin is top left of bounding box. # Text is currently left-aligned. Maybe Change? @@ -290,8 +282,8 @@ def set_sizes(self): self.tick_start_x = self.spacer + self.text_w + self.spacer self.grad_start_x = self.tick_start_x + self.tick_w + self.spacer self.grad_end_x = self.grad_start_x + self.grad_w - self.dc_w = self.grad_end_x + self.spacer # total bitmap width - self.dc_h = self.grad_h + self.text_h # total bitmap height + self.dc_w = self.grad_end_x + self.spacer # total bitmap width + self.dc_h = self.grad_h + self.text_h # total bitmap height def draw_background(self): """ @@ -329,32 +321,31 @@ def draw_scale(self): brush = wx.Brush(c) self.mdc.SetPen(pen) self.mdc.SetBrush(brush) - self.mdc.DrawRectangle(self.grad_start_x, - 2, - self.grad_w, - self.grad_start_y - 2) + self.mdc.DrawRectangle(self.grad_start_x, 2, self.grad_w, self.grad_start_y - 2) c = self.oor_low_color pen = wx.Pen(c) brush = wx.Brush(c) self.mdc.SetPen(pen) self.mdc.SetBrush(brush) - self.mdc.DrawRectangle(self.grad_start_x, - self.grad_end_y, - self.grad_w, - self.dc_h - self.grad_end_y - 2) + self.mdc.DrawRectangle( + self.grad_start_x, + self.grad_end_y, + self.grad_w, + self.dc_h - self.grad_end_y - 2, + ) # Calculate and draw the tickmarks. self.draw_ticks(self.ticks) def draw_gradient(self): """Draw the Gradient, painted from North (high) to South (low).""" -# self.mdc.GradientFillLinear((self.grad_start_x, self.grad_start_y, -# self.grad_w, self.grad_h), -# self.high_color, -# self.low_color, -# wx.SOUTH, -# ) + # self.mdc.GradientFillLinear((self.grad_start_x, self.grad_start_y, + # self.grad_w, self.grad_h), + # self.high_color, + # self.low_color, + # wx.SOUTH, + # ) # Remake of the wx.DC.GradientFillLinear wx core function, but uses # my own algorithm for determining the colors. @@ -363,21 +354,17 @@ def draw_gradient(self): # Save the old pen colors old_pen = self.mdc.GetPen() old_brush = self.mdc.GetBrush() - delta = self.grad_h / 255 # height of one shade box + delta = self.grad_h / 255 # height of one shade box if delta < 1: - delta = 1 # max of 255 pts - fractional colors not defined. + delta = 1 # max of 255 pts - fractional colors not defined. y = self.grad_start_y while y <= self.grad_end_y: - val = wm_utils.rescale(y, - (self.grad_start_y, self.grad_end_y), - (1, 0)) + val = wm_utils.rescale(y, (self.grad_start_y, self.grad_end_y), (1, 0)) color = self.gradient.get_color(val) self.mdc.SetPen(wx.Pen(color)) self.mdc.SetBrush(wx.Brush(color)) - self.mdc.DrawRectangle(self.grad_start_x, - y, - self.grad_w, delta + 1) + self.mdc.DrawRectangle(self.grad_start_x, y, self.grad_w, delta + 1) y += delta # Set the pen and brush back to what they were @@ -392,7 +379,6 @@ def get_max_text_w(self, ticks): """ return max([self.mdc.GetTextExtent(i[0])[0] for i in ticks]) - ### #-------------------------------------------------------------------- ### Events ### #-------------------------------------------------------------------- @@ -404,7 +390,7 @@ def _on_size(self, event): self.set_sizes() self.hbox.Remove(0) self.hbox.Add((self.dc_w, self.dc_h)) -# self.mdc.SelectObject(wx.EmptyBitmap(self.dc_w, self.dc_h)) + # self.mdc.SelectObject(wx.EmptyBitmap(self.dc_w, self.dc_h)) self.mdc.SelectObject(wx.Bitmap(self.dc_w, self.dc_h)) self.draw_scale() self.Refresh() @@ -420,29 +406,27 @@ def on_color_change(self, event): This is done by updating self.gradient and calling self.draw_scale() """ - if event['low'] is not None: - self.low_color = event['low'] - if event['high'] is not None: - self.high_color = event['high'] - self.gradient = wm_utils.LinearGradient(self.low_color, - self.high_color) - -# self._clear_scale() + if event["low"] is not None: + self.low_color = event["low"] + if event["high"] is not None: + self.high_color = event["high"] + self.gradient = wm_utils.LinearGradient(self.low_color, self.high_color) + + # self._clear_scale() self.hbox.Remove(0) self.hbox.Add((self.dc_w, self.dc_h)) -# self.mdc.SelectObject(wx.EmptyBitmap(self.dc_w, self.dc_h)) + # self.mdc.SelectObject(wx.EmptyBitmap(self.dc_w, self.dc_h)) self.mdc.SelectObject(wx.Bitmap(self.dc_w, self.dc_h)) self.draw_scale() def on_scale_change(self, event): """Redraw things on scale change.""" - self.gradient = wm_utils.LinearGradient(self.low_color, - self.high_color) + self.gradient = wm_utils.LinearGradient(self.low_color, self.high_color) self.hbox.Remove(0) self.hbox.Add((self.dc_w, self.dc_h)) -# self.mdc.SelectObject(wx.EmptyBitmap(self.dc_w, self.dc_h)) + # self.mdc.SelectObject(wx.EmptyBitmap(self.dc_w, self.dc_h)) self.mdc.SelectObject(wx.Bitmap(self.dc_w, self.dc_h)) self.draw_scale() @@ -456,11 +440,13 @@ def on_mouse_left_down(self, event): """Used for debugging.""" print("Left-click - color from self.mdc.GetPixelPoint.") pos = event.GetPosition() - w, h = self.mdc.GetSize() # change to gradient area + w, h = self.mdc.GetSize() # change to gradient area if pos[0] < w and pos[1] < h: - val = wm_utils.rescale(pos[1], - (self.grad_start_y, self.grad_end_y - 1), - reversed(self.plot_range)) + val = wm_utils.rescale( + pos[1], + (self.grad_start_y, self.grad_end_y - 1), + reversed(self.plot_range), + ) a = self.mdc.GetPixelPoint(event.GetPosition()) print("{}\t{}\t{}".format(pos, a, val)) @@ -468,18 +454,19 @@ def on_mouse_right_down(self, event): """Used for debugging.""" print("Right-click - color from get_color()") pos = event.GetPosition() - w, h = self.mdc.GetSize() # change to gradient area + w, h = self.mdc.GetSize() # change to gradient area if pos[0] < w and pos[1] < h: - val = wm_utils.rescale(pos[1], - (self.grad_start_y, self.grad_end_y - 1), - reversed(self.plot_range)) + val = wm_utils.rescale( + pos[1], + (self.grad_start_y, self.grad_end_y - 1), + reversed(self.plot_range), + ) a = self.get_color(val) print("{}\t{}\t{}".format(pos, a, val)) def on_mouse_wheel(self, event): print("mouse wheel!") -# self.on_mouse_left_down(event) - + # self.on_mouse_left_down(event) class DiscreteLegend(wx.Panel): @@ -497,11 +484,12 @@ class DiscreteLegend(wx.Panel): colors : list, optional """ - def __init__(self, - parent, - labels, - colors=None, - ): + def __init__( + self, + parent, + labels, + colors=None, + ): wx.Panel.__init__(self, parent) self.parent = parent self.labels = labels @@ -522,17 +510,15 @@ def _init_ui(self): # Create items to add for _i, (key, value) in enumerate(zip(self.labels, self.colors)): - self.label = wx.StaticText(self, - label=str(key), - style=wx.ALIGN_LEFT, - ) - - self.colorbox = csel.ColourSelect(self, - _i, - "", - tuple(value), - style=wx.NO_BORDER, - size=(20, 20)) + self.label = wx.StaticText( + self, + label=str(key), + style=wx.ALIGN_LEFT, + ) + + self.colorbox = csel.ColourSelect( + self, _i, "", tuple(value), style=wx.NO_BORDER, size=(20, 20) + ) self.Bind(csel.EVT_COLOURSELECT, self.on_color_pick, id=_i) @@ -566,11 +552,11 @@ def create_colors(n): spacing = 360 / n colors = [] for val in wm_utils.frange(0, 360, spacing): - hsl = (val/360, 1, 0.75) + hsl = (val / 360, 1, 0.75) colors.append(colorsys.hsv_to_rgb(*hsl)) # convert from 0-1 to 0-255 and return - colors = [tuple(int(i*255) for i in color) for color in colors] + colors = [tuple(int(i * 255) for i in color) for color in colors] # Alternate colors across the circle colors = colors[::2] + colors[1::2] @@ -589,7 +575,7 @@ def create_color_dict(self): def on_color_pick(self, event): """Recreate the {label: color} dict and send to the parent.""" -# print(event.GetId()) + # print(event.GetId()) self.colors[event.GetId()] = event.GetValue().Get() self.create_color_dict() # Send the event to the parent: @@ -599,28 +585,32 @@ def on_color_pick(self, event): class LegendOverlay(FloatCanvas.Text): """Demo of drawing overlay - to be used for legend.""" - def __init__(self, - String, - xy, - Size=24, - Color="Black", - BackgroundColor=None, - Family=wx.MODERN, - Style=wx.NORMAL, - Weight=wx.NORMAL, - Underlined=False, - Font=None): - FloatCanvas.Text.__init__(self, - String, - xy, - Size=Size, - Color=Color, - BackgroundColor=BackgroundColor, - Family=Family, - Style=Style, - Weight=Weight, - Underlined=Underlined, - Font=Font) + def __init__( + self, + String, + xy, + Size=24, + Color="Black", + BackgroundColor=None, + Family=wx.MODERN, + Style=wx.NORMAL, + Weight=wx.NORMAL, + Underlined=False, + Font=None, + ): + FloatCanvas.Text.__init__( + self, + String, + xy, + Size=Size, + Color=Color, + BackgroundColor=BackgroundColor, + Family=Family, + Style=Style, + Weight=Weight, + Underlined=Underlined, + Font=Font, + ) def _Draw(self, dc, Canvas): """ @@ -642,7 +632,7 @@ def _Draw(self, dc, Canvas): def main(): """Display the Legend when module is run directly.""" legend_labels = ["A", "Banana!", "C", "Donut", "E"] -# legend_labels = [str(_i) for _i in range(10)] + # legend_labels = [str(_i) for _i in range(10)] legend_colors = None @@ -652,12 +642,13 @@ class ExampleFrame(wx.Frame): """Base Frame.""" def __init__(self, title): - wx.Frame.__init__(self, - None, # Window Parent - wx.ID_ANY, # id - title=title, # Window Title - size=(300 + 16, 550 + 38), # Size in px - ) + wx.Frame.__init__( + self, + None, # Window Parent + wx.ID_ANY, # id + title=title, # Window Title + size=(300 + 16, 550 + 38), # Size in px + ) self.Bind(wx.EVT_CLOSE, self.OnQuit) diff --git a/src/wafer_map/wm_utils.py b/src/wafer_map/wm_utils.py index 786a6f9..e9f0a1a 100644 --- a/src/wafer_map/wm_utils.py +++ b/src/wafer_map/wm_utils.py @@ -1,20 +1,12 @@ # -*- coding: utf-8 -*- -# pylint: disable=C0103 -# C0103 = invalid variable name """ Holds various utilities used by ``wafer_map``. """ -# --------------------------------------------------------------------------- -### Imports -# --------------------------------------------------------------------------- -# Standard Library from __future__ import absolute_import, division, print_function, unicode_literals -# Third-Party import numpy as np from colour import Color -# Package/Application from wafer_map import PY2 @@ -191,11 +183,11 @@ def linear_gradient(initial_color, dest_color, value): return dest_color # Old way, linear averaging. -# r1, g1, b1 = (_c for _c in initial_color) -# r2, g2, b2 = (_c for _c in dest_color) -# r = int(rescale(value, (0, 1), (r1, r2))) -# g = int(rescale(value, (0, 1), (g1, g2))) -# b = int(rescale(value, (0, 1), (b1, b2))) + # r1, g1, b1 = (_c for _c in initial_color) + # r2, g2, b2 = (_c for _c in dest_color) + # r = int(rescale(value, (0, 1), (r1, r2))) + # g = int(rescale(value, (0, 1), (g1, g2))) + # b = int(rescale(value, (0, 1), (b1, b2))) # Using the ``colour`` package # Convert 0-255 to 0-1, drop the 4th term, and instance the Color class @@ -236,10 +228,10 @@ def polylinear_gradient(colors, value): return colors[0] # divide up our range into n - 1 segments, where n is the number of colors - l = 1 / (n - 1) # float division + l = 1 / (n - 1) # float division # figure out which segment we're in - determines start and end colors - m = int(value // l) # Note floor division + m = int(value // l) # Note floor division low = m * l high = (m + 1) * l @@ -260,11 +252,12 @@ def beizer_gradient(initial_color, arc_color, dest_color, value): pass -def _GradientFillLinear(rect, - intial_color, - dest_color, - direction, - ): +def _GradientFillLinear( + rect, + intial_color, + dest_color, + direction, +): """ Reimplement the ``wxDCImpl::DoGradientFillLinear`` algorithm. @@ -289,6 +282,8 @@ def _GradientFillLinear(rect, I'm an idiot! This is just linear algebra, I can solve this! """ pass + + r""" void wxDCImpl::DoGradientFillLinear(const wxRect& rect, const wxColour& initialColour, @@ -416,8 +411,8 @@ def coord_to_grid(coord, die_size, grid_center): (grid_x, grid_y) : tuple The grid coordinates. Also known as (column, row). """ -# grid_x = int(grid_center[0] + 0.5 + (coord[0] / die_size[0])) -# grid_y = int(grid_center[1] + 0.5 - (coord[1] / die_size[1])) + # grid_x = int(grid_center[0] + 0.5 + (coord[0] / die_size[0])) + # grid_y = int(grid_center[1] + 0.5 - (coord[1] / die_size[1])) # Fixes #30, courtesy of GitHub user sinkra grid_x = int(round(grid_center[0] + (coord[0] / die_size[0]))) grid_y = int(round(grid_center[1] - (coord[1] / die_size[1]))) @@ -488,13 +483,13 @@ def max_dist_sqrd(center, size): Calculates the distance from the orgin (0, 0) to the farthest corner of a rectangle. """ - half_x = size[0]/2. - half_y = size[1]/2. + half_x = size[0] / 2.0 + half_y = size[1] / 2.0 if center[0] < 0: half_x = -half_x if center[1] < 0: half_y = -half_y - dist = (center[0] + half_x)**2 + (center[1] + half_y)**2 + dist = (center[0] + half_x) ** 2 + (center[1] + half_y) ** 2 return dist @@ -541,7 +536,7 @@ def rescale(x, orig_scale, new_scale=(0, 1)): part_b = original_min * new_max - original_max * new_min denominator = original_max - original_min try: - result = (part_a - part_b)/denominator + result = (part_a - part_b) / denominator except ZeroDivisionError: result = 0 return float(result) @@ -575,6 +570,7 @@ def rescale_clip(x, orig_scale, new_scale=(0, 1)): else: return result + if __name__ == "__main__": print("0 and 1") print(polylinear_gradient([(0, 0, 0), (255, 0, 0), (0, 255, 0)], 0)) @@ -589,12 +585,17 @@ def rescale_clip(x, orig_scale, new_scale=(0, 1)): print(polylinear_gradient([(0, 0, 0), (255, 0, 0), (0, 255, 0)], 0.75)) print("\n4 colors") - print(polylinear_gradient([(0, 0, 0), - (255, 0, 0), - (0, 255, 0), - (0, 0, 255), - ], - 0.5)) + print( + polylinear_gradient( + [ + (0, 0, 0), + (255, 0, 0), + (0, 255, 0), + (0, 0, 255), + ], + 0.5, + ) + ) print("\nLinear Gradient, 0.5") - print(linear_gradient((0, 0, 0), (255, 255, 255), .5)) + print(linear_gradient((0, 0, 0), (255, 255, 255), 0.5)) diff --git a/tests/test_utils.py b/tests/test_utils.py index 32f4db5..5e28644 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -2,26 +2,22 @@ """ Unittests for the :module:`wafer_map.utils` module. """ -# --------------------------------------------------------------------------- -### Imports -# --------------------------------------------------------------------------- -# Standard Library from __future__ import absolute_import, division, print_function, unicode_literals import unittest -# Package/Application from wafer_map import wm_utils as utils class LinearGradient(unittest.TestCase): - known_values = (((0, 0, 0), (255, 255, 255), 0.5, (127, 127, 127)), - ((0, 0, 0), (255, 0, 0), 0.5, (95, 31, 31)), - ((0, 0, 0), (0, 255, 0), 0.5, (95, 95, 31)), - ((0, 0, 0), (0, 0, 255), 0.5, (31, 95, 31)), - ) + known_values = ( + ((0, 0, 0), (255, 255, 255), 0.5, (127, 127, 127)), + ((0, 0, 0), (255, 0, 0), 0.5, (95, 31, 31)), + ((0, 0, 0), (0, 255, 0), 0.5, (95, 95, 31)), + ((0, 0, 0), (0, 0, 255), 0.5, (31, 95, 31)), + ) def test_known_values(self): - for _start, _end, _value, _expected in self.known_values: + for _start, _end, _value, _expected in self.known_values: result = utils.linear_gradient(_start, _end, _value) self.assertEqual(result, _expected) diff --git a/tests/test_wx_edits.py b/tests/test_wx_edits.py index 67eea89..a5a349b 100644 --- a/tests/test_wx_edits.py +++ b/tests/test_wx_edits.py @@ -6,19 +6,13 @@ @author: dthor """ -# --------------------------------------------------------------------------- -### Imports -# --------------------------------------------------------------------------- -# Standard Library import unittest -# Third-Party import wx from wx.lib.floatcanvas import FloatCanvas class TestWxPython(unittest.TestCase): - def setUp(cls): cls.app = wx.App()