Пример #1
0
def main():
	''' Main GUI window '''

	# Create user configuration files
	if not settings.create_user_config_files():
		cprint('\n[ERROR]\tSome Ki-nTree configuration files seem to be missing')
		return

	CREATE_CUSTOM = False

	# Select PySimpleGUI theme
	# sg.theme_previewer() # Show all
	sg.theme('DarkTeal10')

	# Main Menu
	menu_def = [
		['Settings', 
			[	
				'Digi-Key',
				'KiCad',
				'InvenTree',
			],
		],
		[ 'More', 
			[
				# 'Synchronize',
				'Custom Part',
			],
		],
	]
	# Main Window
	layout = [
		[sg.Menu(menu_def,)],
		[
			sg.Text('Enter Part Number:'),
			sg.InputText(key='part_number'),
		],
		[
			sg.Checkbox('Add to KiCad', enable_events=True, default=settings.ENABLE_KICAD, key='enable_kicad'),
			sg.Checkbox('Add to InvenTree', enable_events=True, default=settings.ENABLE_INVENTREE, key='enable_inventree'),
		],
		[
			sg.Button('CREATE', size=(59,1)),
		],
	]

	# Create the Window
	window = sg.Window(f'Ki-nTree [{settings.version}]', 
						layout, 
						location=(500, 500) )

	# Event Loop to process 'events' and get the 'values' of the inputs
	while True:
		if CREATE_CUSTOM:
			event = 'CREATE_CUSTOM'
		else:
			event, values = window.read()

		if event == sg.WIN_CLOSED:  # if user closes window or clicks cancel
			break

		if event == 'Digi-Key':
			search_api_settings_window()
		elif event == 'InvenTree':
			inventree_settings_window()
		elif event == 'KiCad':
			kicad_settings_window()
		elif 'enable' in event:
			settings.set_inventree_enable_flag(values['enable_inventree'], save=True)
			settings.set_kicad_enable_flag(values['enable_kicad'], save=True)
		elif event == 'Custom Part':
			custom_part_info = add_custom_part(part_data={})
			if custom_part_info:
				CREATE_CUSTOM = True
		else:
			# Adding part information to InvenTree
			categories = [None, None]
			symbol = None
			template = None
			footprint = None
			new_part = False
			part_pk = 0
			part_info = {}
			part_data = {}
			progressbar = False

			if CREATE_CUSTOM:
				if custom_part_info['name'] and custom_part_info['description']:
					part_info = custom_part_info
					cprint('\n[MAIN]\tCustom Part', silent=settings.SILENT)
			else:
				if values['part_number']:
					# New part separation
					new_search = '-' * 20
					cprint(f'\n{new_search}', silent=settings.SILENT)

					# Load KiCad settings
					settings.load_kicad_settings()

					# Load InvenTree settings
					settings.load_inventree_settings()

					# SnapEDA test
					# snapeda_window(values['part_number'])

					# Digi-Key Search
					part_info = inventree_interface.digikey_search(values['part_number'])

			if not part_info:
				# Missing Part Information
				if CREATE_CUSTOM:
					sg.popup_ok(f'Missing "Name" and "Description"',
								title='Incomplete Custom Part Data',
								location=(500, 500))
				else:
					sg.popup_ok(f'Failed to fetch part information\n'
								'Make sure:\n- Digi-Key API settings are correct ("Settings > Digi-Key")'
								'\n- Part number is valid',
								title='Digi-Key API Search',
								location=(500, 500))
			else:
				if settings.ENABLE_INVENTREE:
					cprint('\n[MAIN]\tConnecting to Inventree server', silent=settings.SILENT)
					inventree_connect = inventree_interface.connect_to_server()
					if part_info and not inventree_connect:
						sg.popup_ok(f'Failed to access InvenTree server\nMake sure your username and password are correct',
									title='InvenTree Server Error',
									location=(500, 500))
						# Reset part info
						part_info = {}

			# User Categories
			if part_info and (settings.ENABLE_INVENTREE or settings.ENABLE_KICAD):
				if settings.ENABLE_INVENTREE:
					cprint('\n[MAIN]\tCreating part in Inventree', silent=settings.SILENT)

				categories = inventree_interface.get_categories(part_info=part_info,
																supplier_only=False)
			
				# If categories do not exist: request user to fill in categories
				if not categories[0]:
					categories = user_defined_categories(extend=settings.ENABLE_INVENTREE)
					if categories[0]:
						cprint(f'[INFO]\tCategory: "{categories[0]}"', silent=settings.SILENT)
					if categories[1]:
						cprint(f'[INFO]\tSubcategory: "{categories[1]}"', silent=settings.SILENT)
				elif categories[0] and not categories[1]:
					categories = user_defined_categories(category=categories[0],
														 extend=settings.ENABLE_INVENTREE)
					if categories[1]:
						cprint(f'[INFO]\tUpdated Category: "{categories[0]}"', silent=settings.SILENT)
						cprint(f'[INFO]\tSubcategory: "{categories[1]}"', silent=settings.SILENT)
				else:
					# Ask user to re-confirm categories (pre-filled)
					categories = user_defined_categories(category=categories[0], 
														 subcategory=categories[1],
														 extend=settings.ENABLE_INVENTREE)
					cprint(f'[INFO]\tUser Category: "{categories[0]}"', silent=settings.SILENT)
					cprint(f'[INFO]\tUser Subcategory: "{categories[1]}"', silent=settings.SILENT)

			# User Part Info
			if not (categories[0] and categories[1]):
				part_info = {}
			else:
				if CREATE_CUSTOM:
					# Translate custom part data
					part_info = inventree_interface.translate_form_to_digikey(part_info=part_info,
																			  categories=categories,
																			  custom=True)
				else:
					# Add to supplier categories configuration file
					category_dict = {
						categories[0]:
							{ categories[1]: part_info['subcategory'] }
					}
					if not config_interface.add_supplier_category(category_dict, settings.CONFIG_DIGIKEY_CATEGORIES):
						config_file = settings.CONFIG_DIGIKEY_CATEGORIES.split(os.sep)[-1]
						cprint(f'[INFO]\tWarning: Failed to add new supplier category to {config_file} file', silent=settings.SILENT)
						cprint(f'[DBUG]\tcategory_dict = {category_dict}', silent=settings.SILENT)

					# Confirm part data with user
					form_data = add_custom_part(inventree_interface.translate_digikey_to_inventree(part_info=part_info,
																								   categories=categories,
																								   skip_params=True))
					if form_data:
						# Translate to part info format
						user_part_info = inventree_interface.translate_form_to_digikey(part_info=form_data,
																				  	   categories=categories,
																				  	   custom=False)

						# Merge original part_info with user_part_info
						part_info = {**part_info, **user_part_info}
					else:
						# User did not proceed
						part_info = {}

			# Set KiCad user libraries and symbol/footprint
			if part_info and settings.ENABLE_KICAD:
				# Request user to select symbol and footprint libraries
				symbol, template, footprint = user_defined_symbol_template_footprint(categories, values['part_number'])
				# cprint(f'{symbol=}\t{template=}\t{footprint=}', silent=settings.HIDE_DEBUG)
				if not symbol and not footprint:
					part_info = {}
			
			if part_info:
				# Create progress bar window
				progressbar = progress.create_progress_bar_window()

				# InvenTree
				if (symbol and footprint) or settings.ENABLE_INVENTREE:
					
					# Create part in InvenTree
					if settings.ENABLE_INVENTREE:
						new_part, part_pk, part_data = inventree_interface.inventree_create(part_info=part_info,
																							categories=categories,
																							kicad=settings.ENABLE_KICAD,
																							symbol=symbol,
																							footprint=footprint,
																							show_progress=progressbar)
						if not part_data:
							cprint(f'[INFO]\tError: Could not add part to InvenTree', silent=settings.SILENT)

					else:
						if not categories[0]:
							pseudo_categories = [symbol, None]
							part_data = inventree_interface.translate_digikey_to_inventree(part_info=part_info,
																						   categories=pseudo_categories)
						else:
							part_data = inventree_interface.translate_digikey_to_inventree(part_info=part_info,
																						   categories=categories)
							part_data['parameters']['Symbol'] = symbol
							part_data['parameters']['Footprint'] = footprint
						if not part_data:
							cprint(f'[INFO]\tError: Could not format part data', silent=settings.SILENT)

			if part_data:
				if not settings.ENABLE_INVENTREE:
					# Replace IPN with part name if InvenTree is not used (no part number)
					if CREATE_CUSTOM:
						try:
							manufacturer = part_data['manufacturer'].keys()[0]
							part_data['IPN'] = part_data['manufacturer'][manufacturer][0]
						except:
							part_data['IPN'] = part_data['name']
					else:
						part_data['IPN'] = values['part_number']
					if part_data['datasheet']:
						part_data['inventree_url'] = part_data['datasheet']

				kicad_success = False

				# KiCad
				if settings.ENABLE_KICAD:
					# Reload paths
					settings.load_kicad_settings()
					symbol_libraries_paths = config_interface.load_libraries_paths(settings.CONFIG_KICAD_CATEGORY_MAP, settings.KICAD_SYMBOLS_PATH)
					symbol_templates_paths = config_interface.load_templates_paths(settings.CONFIG_KICAD_CATEGORY_MAP, settings.KICAD_TEMPLATES_PATH)

					# Adding part symbol to KiCAD
					cprint(f'\n[MAIN]\tAdding part to KiCad', silent=settings.SILENT)

					if not symbol:
						kicad_error = 'Incorrect symbol choice'
						cprint(f'[INFO]\tError: {kicad_error}', silent=settings.SILENT)
					elif not template:
						kicad_error = 'Incorrect template choice'
						cprint(f'[INFO]\tError: {kicad_error}', silent=settings.SILENT)
					elif not footprint:
						kicad_error = 'Incorrect footprint choice'
						cprint(f'[INFO]\tError: {kicad_error}', silent=settings.SILENT)
					else:
						try:
							library_path = symbol_libraries_paths[categories[0]][symbol]
						except:
							library_path = symbol_libraries_paths[symbol][symbol]
							
						if template == 'None':
							cprint(f'[INFO]\tWarning: Missing template, using default', silent=settings.SILENT)
							template_path = settings.KICAD_TEMPLATES_PATH + 'default.lib'
						else:
							try:
								template_path = symbol_templates_paths[categories[0]][template]
							except:
								template_path = symbol_templates_paths[symbol][template]

						try:
							library_directory = os.path.dirname(library_path)
						except:
							library_directory = None
							cprint(f'[INFO]\tError: Failed to map library file', silent=settings.SILENT)
						
						if library_directory:
							if settings.AUTO_GENERATE_LIB:
								create_library(library_directory, symbol, settings.symbol_template_lib)

							try:
								kicad_success, kicad_new_part = kicad_interface.inventree_to_kicad(part_data=part_data,
																								   library_path=library_path,
																								   template_path=template_path,
																								   show_progress=progressbar)
							except:
								cprint(f'[INFO]\tError: Failed to add part to KiCad (incomplete InvenTree data)', silent=settings.SILENT)

				# Final result message
				result_message = ''

				# Result pop-up window
				if settings.ENABLE_INVENTREE:
					if not new_part:
						if part_pk:
							result_message = 'Part already in InvenTree database'
						else:
							result_message = 'Error while adding part to InvenTree (check output)'
					else:
						result_message = 'Part added to InvenTree database'
				if settings.ENABLE_KICAD and settings.ENABLE_INVENTREE:
					result_message += '\n'
				if settings.ENABLE_KICAD:
					if not kicad_success:
						result_message += 'Error while adding part in KiCad (check output)'
						try:
							result_message += f'\nINFO: {kicad_error}'
						except:
							pass
					else:
						if kicad_new_part:
							result_message += 'Part added to KiCad library'
						else:
							result_message += 'Part already in KiCad library'

			else:
				if settings.ENABLE_INVENTREE:
					if not categories[0] or categories[1]:
						result_message = 'Part categories were not set properly'
				if settings.ENABLE_INVENTREE or settings.ENABLE_KICAD:
					if not part_data:
						result_message = 'Part data not found - Check part number'
					if not part_pk:
						result_message = 'Unexpected error - Contact developper'

			# Update progress bar to complete and close window
			if progressbar:
				progress.update_progress_bar_window(progress.MAX_PROGRESS)
				progress.close_progress_bar_window()

			if symbol and result_message:
				sg.popup_ok(result_message, title='Results', location=(500, 500))

			if part_data.get('inventree_url', None):
				# Auto-Open Browser Window
				cprint(f'\n[MAIN]\tOpening URL {part_data["inventree_url"]} in browser',
					   silent=settings.SILENT)
				try:
					webbrowser.open(part_data['inventree_url'], new=2)
				except TypeError:
					cprint(f'[INFO]\tError: Failed to open URL', silent=settings.SILENT)

			# Reset create custom flag
			CREATE_CUSTOM = False

	window.close()
Пример #2
0
def inventree_create(part_info: dict,
                     categories: list,
                     kicad=False,
                     symbol=None,
                     footprint=None,
                     show_progress=True,
                     is_custom=False):
    ''' Create InvenTree part from supplier part data and categories '''
    # TODO: Make 'supplier' a variable for use with other APIs (eg. Octopart)
    supplier = 'Digi-Key'
    part_pk = 0
    new_part = False

    # Translate to InvenTree part format
    if supplier == 'Digi-Key':
        inventree_part = translate_digikey_to_inventree(part_info=part_info,
                                                        categories=categories,
                                                        skip_params=is_custom)

    if not inventree_part:
        cprint(f'\n[MAIN]\tError: Failed to process {supplier} data',
               silent=settings.SILENT)

    # Fetch category info from InvenTree part
    category_name = inventree_part['category'][0]
    subcategory_name = inventree_part['category'][1]
    category_pk = inventree_api.get_inventree_category_id(category_name)
    category_select = category_pk

    # Check if subcategory exists
    if subcategory_name:
        # Fetch subcategory id
        subcategory_pk = inventree_api.get_inventree_category_id(
            category_name=subcategory_name, parent_category_id=category_pk)
        if subcategory_pk > 0:
            category_select = subcategory_pk
        else:
            cprint(
                f'\n[TREE]\tWarning: Subcategory "{subcategory_name}" does not exist',
                silent=settings.SILENT)

    # Progress Update
    if show_progress and not progress.update_progress_bar_window():
        return new_part, part_pk, inventree_part

    if category_select <= 0:
        cprint(
            f'[ERROR]\tCategories ({category_name} - {subcategory_name}) does not exist in InvenTree',
            silent=settings.SILENT)
    else:
        # Check if part already exists
        part_pk = inventree_api.is_new_part(category_pk, inventree_part)
        ### Part exists
        if part_pk > 0:
            cprint(f'[INFO]\tPart already exists, skipping.',
                   silent=settings.SILENT)
            ipn = inventree_api.get_part_number(part_pk)
            # Update InvenTree part number
            inventree_part['IPN'] = ipn
            # Update InvenTree URL
            inventree_part[
                'inventree_url'] = f'{settings.PART_URL_ROOT}{inventree_part["IPN"]}/'
        ### Part is new
        else:
            new_part = True
            # Create a new Part
            # Use the pk (primary-key) of the category
            part_pk = inventree_api.create_part(
                category_id=category_select,
                name=inventree_part['name'],
                description=inventree_part['description'],
                revision=inventree_part['revision'],
                image=inventree_part['image'],
                keywords=inventree_part['keywords'])

            # Check part primary key
            if not part_pk: return new_part, part_pk, inventree_part
            # Progress Update
            if show_progress and not progress.update_progress_bar_window():
                return new_part, part_pk, inventree_part

            # Generate Internal Part Number
            cprint(f'\n[MAIN]\tGenerating Internal Part Number',
                   silent=settings.SILENT)
            ipn = part_tools.generate_part_number(category_name, part_pk)
            cprint(f'[INFO]\tInternal Part Number = {ipn}',
                   silent=settings.SILENT)
            # Update InvenTree part number
            ipn_update = inventree_api.set_part_number(part_pk, ipn)
            if not ipn_update:
                cprint(f'\n[INFO]\tError updating IPN', silent=settings.SILENT)
            inventree_part['IPN'] = ipn
            # Update InvenTree URL
            inventree_part[
                'inventree_url'] = f'{settings.PART_URL_ROOT}{inventree_part["IPN"]}/'

    # Progress Update
    if show_progress and not progress.update_progress_bar_window():
        return new_part, part_pk, inventree_part

    if part_pk > 0:
        if new_part:
            cprint('[INFO]\tSuccess: Added new part to InvenTree',
                   silent=settings.SILENT)
            if inventree_part['image']:
                # Add image
                image_result = inventree_api.upload_part_image(
                    inventree_part['image'], part_pk)
                if not image_result:
                    cprint(f'[TREE]\tWarning: Failed to upload part image',
                           silent=settings.SILENT)

        if kicad:
            # Create mandatory parameters (symbol & footprint)
            if symbol and ipn:
                kicad_symbol = symbol + ':' + ipn
            else:
                try:
                    kicad_symbol = settings.symbol_libraries_paths[
                        category_name].split(
                            os.sep)[-1].split('.')[0] + ':' + ipn
                except:
                    kicad_symbol = category_name.replace(' ', '_') + ':TBD'
            if footprint:
                kicad_footprint = footprint
            else:
                try:
                    supplier_package = inventree_part['parameters'][
                        'Package Type']
                    kicad_footprint = settings.footprint_lookup_table[
                        category_name][supplier_package]
                except:
                    kicad_footprint = category_name.replace(' ', '_') + ':TBD'

            # Add symbol & footprint to InvenTree part
            inventree_part['parameters']['Symbol'] = kicad_symbol
            inventree_part['parameters']['Footprint'] = kicad_footprint

        if not inventree_part['parameters']:
            category_parameters = inventree_api.get_category_parameters(
                category_select)

            # Add category-defined parameters
            for parameter in category_parameters:
                inventree_part['parameters'][parameter[0]] = parameter[1]

        # Create parameters
        if len(inventree_part['parameters']) > 0:
            cprint('\n[MAIN]\tCreating parameters', silent=settings.SILENT)
            parameters_lists = [
                [],  # Store new parameters
                [],  # Store existings parameters
            ]
            for name, value in inventree_part['parameters'].items():
                parameter, is_new_parameter = inventree_api.create_parameter(
                    part_id=part_pk, template_name=name, value=value)
                # Progress Update
                if show_progress and not progress.update_progress_bar_window(
                        3):
                    return new_part, part_pk, inventree_part

                if is_new_parameter:
                    parameters_lists[0].append(name)
                else:
                    parameters_lists[1].append(name)

            if parameters_lists[0]:
                cprint(
                    f'[INFO]\tSuccess: The following parameters were created:',
                    silent=settings.SILENT)
                for item in parameters_lists[0]:
                    cprint(f'--->\t{item}', silent=settings.SILENT)
            if parameters_lists[1]:
                cprint(
                    f'[TREE]\tWarning: The following parameters were skipped:',
                    silent=settings.SILENT)
                for item in parameters_lists[1]:
                    cprint(f'--->\t{item}', silent=settings.SILENT)

        # Create manufacturer part
        manufacturer_name = None
        manufacturer_mpn = None
        # Extract manufacturer name and number from part data
        for key, values in inventree_part['manufacturer'].items():
            manufacturer_name = key
            manufacturer_mpn = values[0]
            break

        if manufacturer_mpn:
            cprint('\n[MAIN]\tCreating manufacturer part',
                   silent=settings.SILENT)
            is_new_manufacturer_part = inventree_api.is_new_manufacturer_part(
                manufacturer_name=manufacturer_name,
                manufacturer_mpn=manufacturer_mpn)

            if not is_new_manufacturer_part:
                cprint(f'[INFO]\tManufacturer part already exists, skipping.',
                       silent=settings.SILENT)
            else:
                # Create a new manufacturer part
                is_manufacturer_part_created = inventree_api.create_manufacturer_part(
                    part_id=part_pk,
                    manufacturer_name=manufacturer_name,
                    manufacturer_mpn=manufacturer_mpn,
                    datasheet=inventree_part['datasheet'],
                    description=inventree_part['description'])

                if is_manufacturer_part_created:
                    cprint('[INFO]\tSuccess: Added new manufacturer part',
                           silent=settings.SILENT)

        # Create supplier part
        try:
            supplier_sku = inventree_part['supplier'][supplier][0]
        except KeyError:
            supplier_sku = None

        if supplier_sku:
            cprint('\n[MAIN]\tCreating supplier part', silent=settings.SILENT)
            is_new_supplier_part = inventree_api.is_new_supplier_part(
                supplier_name=supplier, supplier_sku=supplier_sku)

            if not is_new_supplier_part:
                cprint(f'[INFO]\tSupplier part already exists, skipping.',
                       silent=settings.SILENT)
            else:
                # Create a new supplier part
                is_supplier_part_created = inventree_api.create_supplier_part(
                    part_id=part_pk,
                    manufacturer_name=manufacturer_name,
                    manufacturer_mpn=manufacturer_mpn,
                    supplier_name=supplier,
                    supplier_sku=inventree_part['supplier'][supplier],
                    description=inventree_part['description'],
                    link=inventree_part['supplier_link'])

                if is_supplier_part_created:
                    cprint('[INFO]\tSuccess: Added new supplier part',
                           silent=settings.SILENT)

    # Progress Update
    if show_progress and not progress.update_progress_bar_window(3): pass

    return new_part, part_pk, inventree_part
Пример #3
0
    def add_component_to_library_from_inventree(self,
                                                component_data,
                                                library_path,
                                                template_path=None,
                                                show_progress=True):
        ''' Create component (symbol) in KiCad library '''
        part_in_lib = False
        new_part = False
        category = component_data['category'][0]
        subcategory = component_data['category'][1]

        # Load library and template paths
        cprint(f'[KCAD]\tlibrary_path: {library_path}', silent=settings.SILENT)

        if not template_path:
            # Fetch template path
            try:
                template_path = settings.symbol_templates_paths[category][
                    subcategory]
            except:
                template_path = settings.symbol_templates_paths[category][
                    'Default']

        # Check files exist
        if not os.path.isfile(library_path):
            cprint(f'[KCAD]\tError loading library file ({library_path})',
                   silent=settings.SILENT)
            return part_in_lib, new_part
        if not os.path.isfile(template_path):
            cprint(f'[KCAD]\tError loading template file ({template_path})',
                   silent=settings.SILENT)
            return part_in_lib, new_part

        # Load library
        schlib = SchLib(library_path)
        library_name = library_path.split(os.sep)[-1]
        cprint('[KCAD]\tNumber of parts in library ' + library_name + ': ' +
               str(schlib.getComponentCount()),
               silent=settings.SILENT)

        # Check if part already in library
        try:
            is_component_in_library = self.is_component_in_library(
                schlib, component_data['IPN'])
            part_in_lib = True
        except:
            is_component_in_library = False
        if is_component_in_library:
            return part_in_lib, new_part

        # Progress Update
        if show_progress and not progress.update_progress_bar_window():
            return part_in_lib, new_part

        # Load template
        templatelib = SchLib(template_path)
        # Load new component
        if templatelib.getComponentCount() == 1:
            for component in templatelib.components:
                new_component = component
        else:
            cprint(
                '[KCAD]\tError: Found more than 1 component template in template file, aborting',
                silent=settings.SILENT)
            return part_in_lib, new_part

        # Update comment, name and definition
        new_component.comments[1] = '# ' + component_data['IPN'] + '\n'
        new_component.name = component_data['IPN']
        new_component.definition['name'] = component_data['IPN']

        # Update documentation
        new_component.documentation['description'] = component_data[
            'description']
        new_component.documentation['datasheet'] = component_data[
            'inventree_url']
        new_component.documentation['keywords'] = component_data['keywords']
        #cprint(new_component.documentation, silent=SILENT)

        # Update fields
        manufacturer_name = ''
        for field_idx in range(len(new_component.fields)):
            if 'name' in new_component.fields[field_idx]:
                component_field = str(
                    new_component.fields[field_idx]['name']).replace('"', '')
                if component_field in component_data['parameters'].keys():
                    new_component.fields[field_idx]['name'] = component_data[
                        'parameters'][component_field]
                elif component_field == 'IPN':
                    new_component.fields[field_idx]['name'] = component_data[
                        'IPN']
                elif component_field == 'Manufacturer':
                    for manufacturer in component_data['manufacturer'].keys():
                        manufacturer_name = manufacturer
                        new_component.fields[field_idx][
                            'name'] = manufacturer_name
                        break
                elif component_field == 'MPN':
                    new_component.fields[field_idx]['name'] = component_data[
                        'manufacturer'][manufacturer_name][0]

        schlib.addComponent(new_component)
        schlib.save()
        cprint(f'[KCAD]\tSuccess: Component added to library {library_name}',
               silent=settings.SILENT)
        part_in_lib = True
        new_part = True

        # Progress Update
        if show_progress and not progress.update_progress_bar_window(): pass

        return part_in_lib, new_part