# -*- coding: utf-8 -*- """ @author: Edward Middleton-Smith """ # import argument_validation as av from colour_theme_braille_character import Colour_Theme_Character_Braille, BaseStyle from openscad_objects import ModelOpenSCAD # get_openscad_colours # from translate_msg_2_braille import Character_Braille from character_braille import Character_Braille from translation_braille import Translation_Braille, Enum_Braille_Proficiency_Level from utils_system import render_openscad, make_openscad, exec_oscmd import openpyscad as ops # Union, Difference import numpy as np from typing import Optional, List from prettytable import PrettyTable from enum import Enum from pydantic import BaseModel, Field, ValidationError, validate_arguments, validator # from color import Color from builtins import staticmethod import os import string from abc import ABC, abstractmethod import inspect """ class EnumBaseStyleMeta(type(Enum), type(BaseStyle)): pass """ class Enum_Visibility_Character_Braille(Enum): # BaseStyle, metaclass=EnumBaseStyleMeta): EMBOSSED = -1 HIDDEN = 0 VISIBLE = 1 def minimum(): return min([e.value for e in Enum_Visibility_Character_Braille]) def maximum(): return max([e.value for e in Enum_Visibility_Character_Braille]) def get_text_filename(self): return '_0' if (self == Enum_Visibility_Character_Braille.HIDDEN) else '__' if (self == Enum_Visibility_Character_Braille.EMBOSSED) else '_1' class Style_Character_Braille(BaseModel, BaseStyle): show_braille: Enum_Visibility_Character_Braille # = Enum_Visibility_Character_Braille.VISIBLE show_letter: Enum_Visibility_Character_Braille # = Enum_Visibility_Character_Braille.VISIBLE show_number: Enum_Visibility_Character_Braille # = Enum_Visibility_Character_Braille.VISIBLE """ def __new__(cls, braille, letter, number): _m = 'style.__new__' av.val_type(braille, "", 'braille', _m) av.val_type(braille, "", 'braille', _m) av.val_type(braille, "", 'braille', _m) return super(style, cls).__new__(cls) def __init__(self, braille, letter, number): self.braille = braille self.letter = letter self.number = number """ def get_text_filename(self): return f"{self.show_braille.get_text_filename()}{self.show_letter.get_text_filename()}{self.show_number.get_text_filename()}" @staticmethod def get_default(): return Style_Character_Braille(show_braille=Enum_Visibility_Character_Braille.VISIBLE, show_letter=Enum_Visibility_Character_Braille.VISIBLE, show_number=Enum_Visibility_Character_Braille.VISIBLE) @staticmethod def get_defaults(): return [Style_Character_Braille.get_default()] def as_row(self): return [self.show_braille.name, self.show_letter.name, self.show_number.name] """ def input_from_console(): sizes = Size_Character_Braille.get_defaults() count_sizes = len(sizes) table_output = PrettyTable() table_output.field_names = ['index', 'name', 'dot spacing', 'block height', 'character spacing', 'base height', 'dot height', 'dot bottom radius', 'dot top radius'] for index_size in range(count_sizes): size_index = sizes[index_size] table_output.add_row([index_size + 1] + size_index.as_row()) print() print("Please select product dimensions configuration from below:") print(table_output) while True: size_input = str(input("Product dimensions configuration (name or index): ")) print(size_input + " selected") if size_input == "#!ERRORCODE!#": exit for index_size in range(count_sizes): if size_input == sizes[index_size].name or size_input == str(index_size + 1): return sizes[index_size] def input_many_from_console(): sizes_selected = [] print('Inputting many Size_Character_Braille objects') print() while True: try: count_sizes = int(input('Quantity of size objects to enter:')) except: continue for index_size in range(count_sizes): print(f'Inputting size object {index_size + 1}') size_new = Size_Character_Braille.input_from_console() sizes_selected.append(size_new) break return sizes_selected """ def get_list_headings(): return ['show braille', 'show letter', 'show number'] class Size_Character_Braille(BaseModel, BaseStyle): # ATTRIBUTE DECLARATION name: str spacing_dot: float = Field(ge = 0) # dp distance between dots height_block: float = Field(ge = 0) # hblock height of spacing_character: float = Field(ge = 0) # spacing between keys on keyboard height_base: float = Field(ge = 0) # height of height_dot: float = Field(ge = 0) # height of Braille dots radius_bottom_dot: float = Field(ge = 0) # base radius of Braille dots radius_top_dot : float = Field(ge = 0) # top radius of Braille dots width_character_braille: float = 0 R_block_letter: List[float] = [0, 0, 0] R_block_braille: List[float] = [0, 0, 0] R_block_number: List[float] = [0, 0, 0] size_font_alphabetic: float = 0 size_font_numeric: float = 0 sf_size_font_word_length: float = 0 sf_size_font_number_length: float = 0 def __init__(self, name, spacing_dot, height_block, spacing_character, height_base, height_dot, radius_bottom_dot, radius_top_dot): BaseModel.__init__(self, name=name, spacing_dot=spacing_dot, height_block=height_block, spacing_character=spacing_character, height_base=height_base, height_dot=height_dot, radius_bottom_dot=radius_bottom_dot, radius_top_dot=radius_top_dot) self.width_character_braille = self.spacing_dot + 2 * self.spacing_character + self.radius_bottom_dot * 2 self.R_block_letter = [self.width_character_braille, self.width_character_braille, self.height_block] # alphabet block dimensions [x, y] self.R_block_braille = [self.width_character_braille, self.width_character_braille + self.spacing_dot, self.height_block] # braille block dimensions [x, y] self.R_block_number = [self.width_character_braille, self.width_character_braille - 2 * self.spacing_character, self.height_block] #np.array(* i_s_num # number block dimensions [x, y] ' adjust to scalar coefficient rather than linear translation self.size_font_alphabetic = self.R_block_letter[0] - 4 * self.spacing_character self.size_font_numeric = self.size_font_alphabetic * 0.75 self.sf_size_font_word_length = 0.65 # scale factor for shrinking number text to fit more letters in single char space self.sf_size_font_number_length = 0.75 # scale factor for shrinking message text to fit more letters in single char space """ # METHODS def __new__(cls, name, dp, hblock, s, base_height, hcyl, rcyl, rsphere): # FUNCTION # Initialise class object # ARGUMENTS # str name - name size scheme # float dp - distance between braille pins # float hblock - height of base of each key # float s - spacing # float base_height - height of base block - board # float hcyl - height of braille pins # float rcyl - base radius of braille pins # float rsphere - tip radius of braille pins # ARGUMENT VALIDATION _m = 'scrabble_dimensions.__new__' v_arg_type = 'class attribute' av.val_str(name, 'name', _m, 1, v_arg_type = v_arg_type) av.full_val_float(dp, 'dp', _m, 0, v_arg_type = v_arg_type) av.full_val_float(hblock, 'hblock', _m, v_arg_type = v_arg_type) av.full_val_float(s, 's', _m, 0, v_arg_type = v_arg_type) av.full_val_float(base_height, 'base_height', _m, 0, v_arg_type = v_arg_type) av.full_val_float(hcyl, 'hcyl', _m, 0, v_arg_type = v_arg_type) av.full_val_float(rcyl, 'rcyl', _m, 0, v_arg_type = v_arg_type) av.full_val_float(rsphere, 'rsphere', _m, 0, v_arg_type = v_arg_type) # RETURNS return super(scrabble_dimensions, cls).__new__(cls) def __init__(self, name, dp, hblock, s, base_height, hcyl, rcyl, rsphere): # FUNCTION # Construct class object # ARGUMENTS # str name # float dp # float hblock # float s # float base_height # float hcyl # float rcyl # float rsphere # ARGUMENT VALIDATION # see __new__() # ATTRIBUTE INSTANTIATION self.name = name self.dp = dp self.hblock = hblock self.s = s self.base_height = base_height self.hcyl = hcyl self.rcyl = rcyl self.rsphere = rsphere """ def __repr__(self): # FUNCTION # Convert object to str representation # RETURNS return f"name = {self.name}, spacing_dot = {self.spacing_dot}, height_block = {self.height_block}, spacing_character = {self.spacing_character}, height_base = {self.height_base}, height_dot = {self.height_dot}, radius_bottom_dot = {self.radius_bottom_dot}, radius_top_dot = {self.radius_top_dot}" def as_dict(self): # FUNCTION # Convert object attribute, value pairs to dictionary representation # RETURNS return {'name': self.name, 'spacing_dot': self.spacing_dot, 'height_block': self.height_block, 'spacing_character': self.spacing_character, 'height_base': self.height_base, 'height_dot': self.height_dot, 'radius_bottom_dot': self.radius_bottom_dot, 'radius_top_dot': self.radius_top_dot} def as_row(self): return [self.name, self.spacing_dot, self.height_block, self.spacing_character, self.height_base, self.height_dot, self.radius_bottom_dot, self.radius_top_dot] def get_defaults(): k = 2.3622 s = 1 sizes = [] sizes.append(Size_Character_Braille('sz_1', 2.5, 2*s, s, 4*s, 1.2, 2/3, 1/2)) sizes.append(Size_Character_Braille('sz_2', 4, 1.28, s, 4*s, 1.2, 3/2, 1)) sizes.append(Size_Character_Braille('sz_3', 8, 2.5, s, 6*s, 1.2, 3/2, 1)) sizes.append(Size_Character_Braille('sz_4', 10, 3, 1, 6*s, 1.2, 3/2, 1)) sizes.append(Size_Character_Braille('sz_5', 10, 1, 1, 6*s, 1.2, 3/2, 1)) sizes.append(Size_Character_Braille('keyboard', 2.5, 0.8, 0.5, 4, 1.2, 2/3, 0.5)) sizes.append(Size_Character_Braille('poster', 2.5, 0.8, 0.5, 2, 1.2, 2/3, 0.5)) sizes.append(Size_Character_Braille('poster_big', 2.5*k, 0.8, 0.5*k, 2, 1.2, 2/3*k, 0.5*k)) # print('szs_scrabble') return sizes def get_default(): sizes_default = Size_Character_Braille.get_defaults() return sizes_default['Character_Braille'].apply(lambda x: x if x.name == 'Keyboard' else None).dropna()[0] """ def input_from_console(): sizes = Size_Character_Braille.get_defaults() count_sizes = len(sizes) table_output = PrettyTable() table_output.field_names = ['index', 'name', 'dot spacing', 'block height', 'character spacing', 'base height', 'dot height', 'dot bottom radius', 'dot top radius'] for index_size in range(count_sizes): size_index = sizes[index_size] table_output.add_row([index_size + 1] + size_index.as_row()) print() print("Please select product dimensions configuration from below:") print(table_output) while True: size_input = str(input("Product dimensions configuration (name or index): ")) print(size_input + " selected") if size_input == "#!ERRORCODE!#": exit for index_size in range(count_sizes): if size_input == sizes[index_size].name or size_input == str(index_size + 1): return sizes[index_size] def input_many_from_console(): sizes_selected = [] print('Inputting many Size_Character_Braille objects') print() while True: try: count_sizes = int(input('Quantity of size objects to enter:')) except: continue for index_size in range(count_sizes): print(f'Inputting size object {index_size + 1}') size_new = Size_Character_Braille.input_from_console() sizes_selected.append(size_new) break return sizes_selected """ def get_list_headings(): return ['name', 'dot spacing', 'block height', 'character spacing', 'base height', 'dot height', 'dot bottom radius', 'dot top radius'] """ class Utils_Size_Character_Braille: @staticmethod @validate_arguments def is_valid_size_input(size_input: str, sizes_valid: List[Size_Character_Braille]): count_sizes_valid = len(sizes_valid) for i_size_valid in range(count_sizes_valid): if size_input == sizes_valid[i_size_valid].name: return True return False """ class Enum_Justification_Text(Enum): LEFT = 0 CENTRE = 1 RIGHT = 2 def minimum(): return min([e.value for e in Enum_Visibility_Character_Braille]) def maximum(): return max([e.value for e in Enum_Visibility_Character_Braille]) def input_from_console(cls): print("Please select justification for text:") print("0. Left") print("1. Centre") print("2. Right") while True: justification_input = input("Justification (0, 1, 2): ") print(str(justification_input) + " selected") if justification_input == "#!ERRORCODE!#": exit try: justification_input = int(justification_input) except: continue # if justification_input is not int: continue if justification_input < Enum_Justification_Text.minimum() or justification_input > Enum_Justification_Text.maximum(): continue return Enum_Justification_Text(justification_input) def get_default(): return Enum_Justification_Text(0) def input_many_from_console(): return BaseStyle.input_many_from_console(Enum_Justification_Text) class Keyboard_3D(ModelOpenSCAD): #, BaseModel): size_characters: Size_Character_Braille style_characters: Style_Character_Braille colour_theme: Colour_Theme_Character_Braille justification_text: Enum_Justification_Text max_characters_per_row: int translation: Translation_Braille # List[List[Character_Braille]] # fn: int = Field(ge=0, default=25) # filename: str = '' def __init__(self, **kwargs): # , size_characters, style_characters, colour_theme, justification_text, path_dir, max_characters_per_row, translation, fn=25, filename=''): # print(f'size_characters, style_characters, colour_theme, justification_text, path_dir, max_characters_per_row, translation, fn, filename: {size_characters, style_characters, colour_theme, justification_text, path_dir, max_characters_per_row, translation, fn, filename}') # BaseModel.__init__(self, size_characters=size_characters, style_characters=style_characters, colour_theme=colour_theme, justification_text=justification_text, path_dir=path_dir, max_characters_per_row=max_characters_per_row, translation=translation, fn=fn, filename=filename) # BaseModel.__init__(self, size_characters=size_characters, style_characters=style_characters, justification_text=justification_text, max_characters_per_row=max_characters_per_row, translation=translation, filename='') # self.filename = self.get_filename() ModelOpenSCAD.__init__(self, **{**kwargs, 'filename': '', 'model': None}) # , path_dir=path_dir, filename=filename, colour_theme=colour_theme, fn=fn) # BaseModel.__init__(self, size_characters=size_characters, style_characters=style_characters, justification_text=justification_text, max_characters_per_row=max_characters_per_row, translation=translation) # BaseModel.__init__(self, **kwargs) # , size_characters=size_characters, style_characters=style_characters, colour_theme=colour_theme, justification_text=justification_text, path_dir=path_dir, max_characters_per_row=max_characters_per_row, translation=translation, fn=fn, filename=filename) self.filename = self.get_filename() def get_filename(self): #, sz_scheme, col_theme, my_style, path_file = "C:\\Users\\edwar\\OneDrive\\Documents\\Programming\\Python Scripts\\Braille_Scrabble", suffix = "stl", presuffix=""): """ av.val_str(path_file, 'path_file', _m) av.val_str(suffix, 'suffix', _m) av.val_str(presuffix, 'presuffix', _m) """ return f"{self.size_characters.name}_{self.colour_theme.name}{self.style_characters.get_text_filename()}" def get_path_file(self, suffix): return f"{self.path_dir}/{self.filename}.{suffix}" @staticmethod @validate_arguments def make_from_styles_and_plaintext_and_proficiency_level(size_characters: Size_Character_Braille, style_characters: Style_Character_Braille, colour_theme: Colour_Theme_Character_Braille, justification_text: Enum_Justification_Text, path_dir: str, max_characters_per_row: int, plaintext: str, proficiency_level: Enum_Braille_Proficiency_Level): braille_translation = Translation_Braille(plaintext, proficiency_level) return Keyboard_3D(size_characters = size_characters, style_characters = style_characters, colour_theme = colour_theme, justification_text = justification_text, path_dir = path_dir, max_characters_per_row = max_characters_per_row, translation = braille_translation) def make_model_openpyscad(self): # integer boolean conversions for position show_braille = (self.style_characters.show_braille == Enum_Visibility_Character_Braille.VISIBLE) # or self.style_characters.show_braille == Enum_Visibility_Character_Braille.EMBOSSED) show_letter = (self.style_characters.show_letter == Enum_Visibility_Character_Braille.VISIBLE) # or self.style_characters.show_letter == Enum_Visibility_Character_Braille.EMBOSSED) show_number = (self.style_characters.show_number == Enum_Visibility_Character_Braille.VISIBLE) # or self.style_characters.show_number == Enum_Visibility_Character_Braille.EMBOSSED) # dimensions """ width_character_braille = self.size_characters.spacing_dot + 2 * self.size_characters.spacing_character + self.size_characters.radius_bottom_dot * 2 R_base_letter = [width_character_braille, width_character_braille, self.size_characters.height_block] # alphabet block dimensions [x, y] R_base_braille = [width_character_braille, width_character_braille + self.size_characters.spacing_dot, self.size_characters.height_block] # braille block dimensions [x, y] R_base_number = [width_character_braille, width_character_braille - 2 * self.size_characters.spacing_character, self.size_characters.height_block] #np.array(* i_s_num # number block dimensions [x, y] ' adjust to scalar coefficient rather than linear translation """ # _fn = self.fn model_positive = ops.Union() model_negative = ops.Union() count_rows = 0 count_characters_in_row = 0 size_character = np.array([ self.size_characters.spacing_character + self.size_characters.width_character_braille, self.size_characters.R_base_braille[1] * show_braille + self.size_characters.R_base_letter[1] * show_letter + self.size_characters.R_base_number[1] * show_number + self.size_characters.spacing_character * (show_braille + show_letter + show_number), 0 ]) origin_character = np.array([-self.size_characters.spacing_character, -self.size_characters.spacing_character, 0]) for braille_translation in self.translation: length_translation = len(braille_translation.braille_text) if count_characters_in_row + length_translation > self.max_characters_per_row or (braille_translation.plaintext == 'NEWLINE' and braille_translation.list_dots_braille == [0, 0, 0, 0, 0, 0] and count_characters_in_row > 0): count_rows += 1 count_characters_in_row = 0 for character in braille_translation.braille_text: model_character_positive, model_character_negative = self.make_model_openscad_from_character(character) # character.make_model_openpyscad() position_character = origin_character + np.array([ (self.size_characters.spacing_character + size_character[0]) * count_characters_in_row, (self.size_characters.spacing_character + size_character[1]) * count_rows, 0 ]) model_character_positive.translate(position_character) model_positive.append(model_character_positive) model_character_negative.translate(position_character) model_negative.append(model_character_negative) count_characters_in_row += 1 self.model = ops.Difference() self.model.append(model_character_positive) self.model.append(model_character_negative) return self.model @validate_arguments def make_model_openscad_from_character(self, character: Character_Braille): # origin_character: List[float] , show_braille: bool, show_letter: bool, show_number: bool show_braille = (self.style_characters.show_braille == Enum_Visibility_Character_Braille.VISIBLE) # or self.style_characters.show_braille == Enum_Visibility_Character_Braille.EMBOSSED) show_letter = (self.style_characters.show_letter == Enum_Visibility_Character_Braille.VISIBLE or self.style_characters.show_letter == Enum_Visibility_Character_Braille.EMBOSSED) show_number = (self.style_characters.show_number == Enum_Visibility_Character_Braille.VISIBLE) # or self.style_characters.show_number == Enum_Visibility_Character_Braille.EMBOSSED) model_positive = ops.Union() model_negative = ops.Union() origin_block = np.array([0, 0, 0]) if show_braille: block = self.make_model_openscad_block_braille_from_character(character) block.translate(origin_block) model_positive.append(block) origin_block += np.array([ 0, -self.size_characters.spacing_character - self.size_characters.R_block_braille[1], 0 ]) if show_letter: block = self.make_model_openscad_block_letter_from_character(character) block.translate(origin_block) if self.style_characters.show_letter == Enum_Visibility_Character_Braille.VISIBLE: model_positive.append(block) else: model_negative.append(block) origin_block += np.array([ 0, -self.size_characters.spacing_character - self.size_characters.R_block_letter[1], 0 ]) if show_number: block = self.make_model_openscad_block_number_from_character(character) block.translate(origin_block) model_positive.append(block) return model_positive, model_negative """ @validator('origin_character') def validate_origin_character(cls, value): if len(value) != 3: raise ValueError("origin_character must have 3 elements") for element in value: if not isinstance(element, (int, float)): raise ValueError("origin_character must be a list of integers or floats") return value @validator('R_block_braille') def validate_R_block_braille(cls, value): if len(value) != 3: raise ValueError("R_block_braille must have 3 elements") return value @validator('R_block_letter') def validate_R_block_letter(cls, value): if len(value) != 3: raise ValueError("R_block_letter must have 3 elements") return value @validator('R_block_number') def validate_R_block_number(cls, value): if len(value) != 3: raise ValueError("R_block_number must have 3 elements") return value """ @validate_arguments def make_model_openscad_block_braille_from_character(self, character: Character_Braille): model_block_braille = ops.Union() if (self.style_characters.show_letter == Enum_Visibility_Character_Braille.HIDDEN): return model_block_braille if (self.style_characters.show_letter == Enum_Visibility_Character_Braille.VISIBLE): block = ops.Cube(self.size_characters.R_block_braille, center = False) block.translate([0, -self.size_characters.R_block_braille[1], -self.size_characters.R_block_braille[2]]) block.color(self.colour_theme.colour_base) model_block_braille.append(block) if (self.style_characters.show_braille == Enum_Visibility_Character_Braille.VISIBLE): for index_dot_braille_y in range(3): for index_dot_braille_x in range(2): if (character.matrix_braille_dots[index_dot_braille_y][index_dot_braille_x]): dot_braille = ops.Cylinder( h = self.size_characters.height_cylinder, r1 = self.size_characters.radius_bottom_cylinder, r2 = self.size_characters.radius_top_cylinder, center = True, _fn = self.fn ) dot_braille.translate([ index_dot_braille_x * self.size_characters.spacing_dot, -index_dot_braille_y * self.size_characters.spacing_dot, 0 ]) dot_braille.color(self.colour_theme.colour_top) model_block_braille.append(dot_braille) return model_block_braille @validate_arguments def make_model_openscad_block_letter_from_character(self, character: Character_Braille): font = '"Arial:style=Bold"' model_block_letter = ops.Union() if (self.style_characters.show_letter == Enum_Visibility_Character_Braille.HIDDEN): return model_block_letter if (self.style_characters.show_letter == Enum_Visibility_Character_Braille.VISIBLE): block = ops.Cube(self.size_characters.R_block_letter, center = False) block.translate([0, -self.size_characters.R_block_letter[1], -self.size_characters.R_block_letter[2]]) block.color(self.colour_theme.colour_base) model_block_letter.append(block) size_text = self.size_characters.size_font_alphabetic * self.size_characters.scale_text ** (len(character.plaintext) - 1) model_letter = ops.Text(f'"{character.plaintext}"', size_text, font, halign = '"center"', valign = '"center"', _fn = self.fn) model_letter.linear_extrude(self.size_characters.height_dot, center = False) model_letter.color(self.colour_theme.colour_top) model_letter.translate([ self.size_characters.R_block_letter[0] / 2, self.size_characters.R_block_letter[1] / 2 if (self.style_characters.show_letter == Enum_Visibility_Character_Braille.VISIBLE) else self.size_characters.R_block_braille[1] / 2, 0 if (self.style_characters.show_letter == Enum_Visibility_Character_Braille.VISIBLE) else self.size_characters.height_dot - self.size_characters.height_block - self.size_characters.height_base ]) model_block_letter.append(model_letter) return model_block_letter @validate_arguments def make_model_openscad_block_number_from_character(self, character: Character_Braille): model_block_number = ops.Union() if (self.style_characters.show_number == Enum_Visibility_Character_Braille.HIDDEN or self.style_characters.show_number == Enum_Visibility_Character_Braille.EMBOSSED or len(character.plaintext) != 1): return model_block_number font = '"Arial:style=Bold"' number = string.ascii_lowercase.index(character.plaintext.lower()) + 1 length_number = len(str(number)) if (number < 1 or number > 26): return model_block_number block = ops.Cube(self.size_characters.R_block_number, center = True) block.translate([]) block.color(self.colour_theme.colour_base) model_block_number.append(block) size_text = (self.size_characters.R_block_number[1] - self.size_characters.spacing_character * (len(number) + 1)) / (len(number) ** self.size_characters.sf_size_font_number_length) model_number = ops.Text(f'"{number}"', size_text, font, halign = '"center"', valign = '"center"', _fn = self.fn) model_number.linear_extrude(self.size_characters.height_dot, center = True) model_number.color(self.colour_theme.colour_top) model_number.translate([ self.size_characters.R_block_number[0] / 2 if (length_number == 1) else self.size_characters.R_block_number[0] / 2 + 1.6 * size_text * (number - (length_number - 1) / 2), self.size_characters.R_block_number[1] * 0.25, self.size_characters.height_dot / 2 ]) model_block_number.append(model_number) return model_block_number def make_file_openscad(self): if self.model is None: self.make_model_openpyscad() self.model.write(self.get_path_file('scad')) def make_file_stl(self): if not os.path.exists(self.get_path_file('scad')): self.make_file_openscad() render_openscad(self.get_path_file('scad'), self.get_path_file('stl')) def make_file_png(self): if not os.path.exists(self.get_path_file('scad')): self.make_file_openscad() render_openscad(self.get_path_file('scad'), self.get_path_file('png'))