import bge # Bibliothèque Blender Game Engine (UPBGE) import importlib import webbrowser ############################################################################### # twin_doc.py # @title: Documentation de l'environnement 3D pour jumeau numérique # @project: Blender-EduTech # @lang: fr # @authors: Philippe Roy # @copyright: Copyright (C) 2020-2022 Philippe Roy # @license: GNU GPL ############################################################################### # UPBGE scene scene = bge.logic.getCurrentScene() # Colors # color_link = (0.198, 0.109, 0.8, 1) # Violet # color_link_hl = (0.8, 0.005, 0.315, 1) # Magenta # Colors color_doc_chap = (0.198, 0.109, 0.8, 1) # Violet color_doc_fct = (0.198, 0.109, 0.8, 1) # Violet color_doc_hl = (0.8, 0.005, 0.315, 1) # Magenta # color_doc_activate = (0.936, 0.033, 1, 1) # Activé : Rose color_doc_activate = (0.051, 0.270, 0.279,1) # Turquoise # color_doc_activate = (0.936, 0.033, 1, 1) # Activé : Rose # UPBGE constants JUST_ACTIVATED = bge.logic.KX_INPUT_JUST_ACTIVATED JUST_RELEASED = bge.logic.KX_INPUT_JUST_RELEASED ACTIVATE = bge.logic.KX_INPUT_ACTIVE # JUST_DEACTIVATED = bge.logic.KX_SENSOR_JUST_DEACTIVATED # Cards description card_description ={} # Documentation du sytème system=importlib.import_module(scene.objects['System']['script'][:-4]+'_doc') # Système system_card = system.get_system_card() card_description.update(system.get_card_description()) ############################################################################### # Documentation Python ############################################################################### # "oop-card" python_card=["function-card", "alternative-card", "loop-card", "flow-card", "text-card", "list-card", "dict-card", "console-card", "sleep-card", "python-card"] # Fonction card_function_title="Fonction" card_function_text=" La définition d\"une fonction se fait avec \n \'def\'. La fonction peut renvoyer une \n valeur avec \'return\'. \n\n" card_function_text=card_function_text + " def fonction_1 (arguments) : \n instruction_1 \n instruction_2 \n ....\n return valeurs_renvoyées \n\n" card_function_text=card_function_text + " Les arguments sont des données \n transmises à la fonction." card_function_url=[["w3schools.com : functions","https://www.w3schools.com/python/python_functions.asp"]] card_description.update({"function-card" : [card_function_title, card_function_text, card_function_url]}) # Alternative card_alternative_title="Alternative" card_alternative_text=" L\"alternative permet d\"éxécuter des \n instructions en fonction d\"un test. \n" card_alternative_text=card_alternative_text + " Elle se programme en suivant la suite : \n \'if \' (si) ... \'else\' (sinon) ... \n\n" card_alternative_text=card_alternative_text + " if condition :\n" card_alternative_text=card_alternative_text + " instruction_1\n" card_alternative_text=card_alternative_text + " else : \n" card_alternative_text=card_alternative_text + " instruction_2\n\n" card_alternative_text=card_alternative_text + "Le \'else\' (sinon) est facultatif." card_alternative_url=[["w3schools.com : if ... else","https://www.w3schools.com/python/python_conditions.asp"]] card_description.update({"alternative-card" : [card_alternative_title, card_alternative_text, card_alternative_url]}) # Boucles card_loop_title="Boucles" card_loop_text=" Il y a deux types de boucle : \n - avec \'for\' pour définir un nombre de \n répétition (ici n), \n - avec \'while\' (tant que) pour prendre \n en compte une condition. \n \n" card_loop_text=card_loop_text + " for i in range (n) : \n instruction \n \n" card_loop_text=card_loop_text + " while condition : \n instruction" card_loop_url=[["w3schools.com : for","https://www.w3schools.com/python/python_for_loops.asp"], ["w3schools.com : while","https://www.w3schools.com/python/python_while_loops.asp"]] card_description.update({"loop-card" : [card_loop_title, card_loop_text, card_loop_url]}) # Flux card_flow_title="Contrôle du flux" card_flow_text=""" Les structures (if, while, for) peuvent \n être gérées plus finement par les \n fonctions 'break', 'continue' et 'pass'. \n - 'break' : termine l"itération en cours et \n arrête la boucle. - 'continue' : termine l"itération en cours et \n reprend la boucle. - 'pass' : instruction vide.""" card_flow_url=[["w3schools.com : break","https://www.w3schools.com/python/ref_keyword_break.asp"], ["w3schools.com : continue","https://www.w3schools.com/python/ref_keyword_break.asp"], ["w3schools.com : pass","https://www.w3schools.com/python/ref_keyword_pass.asp"]] card_description.update({"flow-card" : [card_flow_title, card_flow_text, card_flow_url]}) # Chaîne de caractères card_text_title="Chaîne de caractères" card_text_text=""" Une chaîne de caractères correspond à un \n texte (mot, phrase). Elle est délimitée \n par " ou ' : texte = 'Bonjour ! ' \n - texte1 + texte2 : concaténe plusieurs \n chaînes. - len(texte) : renvoie la longueur de texte. - "\\n" : caractère aller à la ligne. - texte[i] : renvoie le caractère du rang i de \n texte (commence à 0). - texte[2:5] : renvoie les caractères du rang \n 2 au rang 5. - texte.replace("a","b") : remplace les "a" en "b". """ card_text_url=[ ["Doc. Python v3 Français : string","https://docs.python.org/fr/3/library/string.html"], ["w3schools.com : strings","https://www.w3schools.com/python/python_strings.asp"]] card_description.update({"text-card" : [card_text_title, card_text_text, card_text_url]}) # Liste card_list_title="Liste" card_list_text=""" Une liste est une séquence d"éléments. \n Elle est délimitée par des crochets : liste = ['pomme', 7, 'rouge', True] \n - liste[i] : renvoie l"élément du rang i de la \n liste (commence à 0). - liste.append('neige') : ajoute 'neige' à la \n fin de la liste. - len(liste) : renvoie le nombre d"éléments. - liste.sort() : classe par ordre croissant la \n liste (alphabétiquement / numériquement). - liste.remove('rouge') : enlève la première \n occurence de la valeur.""" card_list_url=[ ["Doc. Python v3 Français : list","https://docs.python.org/fr/3/library/stdtypes.html#sequence-types-list-tuple-range"], ["w3schools.com : lists","https://www.w3schools.com/python/python_lists.asp"]] card_description.update({"list-card" : [card_list_title, card_list_text, card_list_url]}) # Dictionnaire card_dict_title="Dictionnaire" card_dict_text=""" Une liste est une collection d"éléments. \n Elle est délimitée par des accolades, \n chaque valeur comporte une clé unique : dico = {'nom' : 'Haddock', 'prénom' : \n 'Archibald', 'année' : 1940} \n - dico[clé] : renvoie la valeur liée à la clé. - dico.update('domicile' : 'Moulinsart') : met \n à jour les paires de clé/valeur. - len(dico) : renvoie le nombre d"éléments. - list(dico) : renvoie la liste des clés. - dico.pop("année") : enlève la valeur associée \n à la clé.""" card_dict_url=[ ["Doc. Python v3 Français : dict","https://docs.python.org/fr/3/library/stdtypes.html#mapping-types-dict"], ["w3schools.com : dictionaries","https://www.w3schools.com/python/python_dictionaries.asp"]] card_description.update({"dict-card" : [card_dict_title, card_dict_text, card_dict_url]}) # Objet (POO) card_oop_title="Programmation\norientée objet (POO)" card_oop_text="\nFIXME" card_oop_url=[["w3schools.com : classes & objects","https://www.w3schools.com/python/python_classes.asp"]] card_description.update({"oop-card" : [card_oop_title, card_oop_text, card_oop_url]}) # Console card_console_title="Console" card_console_text=" Si vous avez executé Ropy dans un \n terminal (avec l\"option \'-con\'), vous \n pouvez utiliser le terminal comme \n console de debuggage.\n\n" card_console_text= card_console_text + " print(\'Bonjour !\') \n -> Affiche le texte dans la console.\n\n" card_console_text= card_console_text + " variable = input () \n -> Permet la saisie, par exemple : \n" card_console_text= card_console_text + " entree = \'\'\n while entree == \'\' :\n entree = input ()" card_console_url=[["w3schools.com : print","https://www.w3schools.com/python/ref_func_print.asp"], ["w3schools.com : input","https://www.w3schools.com/python/ref_func_input.asp"]] card_description.update({"console-card" : [card_console_title, card_console_text, card_console_url]}) # Temps card_sleep_title="Gestion du temps" card_sleep_text=" Vous pouvez créer des temporisations \n dans le déroulement du script.\n\n" card_sleep_text= card_sleep_text + " time.sleep(x) \n -> Marque d\"un temps d\"arrêt de \n x secondes.\n\n" card_sleep_text= card_sleep_text + " Il faudra préalablement importer la \n bibliothèque \'time\' avec \'import time\'." card_sleep_url=[["Doc. Python v3 Fr : sleep","https://docs.python.org/fr/3/library/time.html#time.sleep"]] card_description.update({"sleep-card" : [card_sleep_title, card_sleep_text, card_sleep_url]}) # Python card_python_title="Langage Python" card_python_text=""" Le Python est un langage de programmation \n interprété open source. Python vise à être \n visuellement épuré avec une syntaxe \n clairement séparée des mécanismes de \n bas niveau.\n Python possède beaucoup de bibliothèques \n spécialisées. Multiplateformes et \n multiparadigme, il est utilisé dans de \n nombreux contextes : scriptage, calcul \n numérique, prototypage, enseignement, \n ou encore comme langage de commande \n pour de nombreux logiciels.""" card_python_url=[["python.org","https://python.org"], ["AFPy","https://www.afpy.org"]] card_description.update({"python-card" : [card_python_title, card_python_text, card_python_url]}) ############################################################################### # Interface ############################################################################### ## # Initialisation de la documentation ## def init(): # Mettre les couleurs sur les icones (chapitres et cartes) chap=("general", "system", "python") for page in chap: scene.objects["Doc-"+page].color = color_doc_chap scene.objects["Doc-"+page+"-text"].color = color_doc_chap for i in range(len(system_card)): scene.objects[system_card[i]].color = color_doc_fct scene.objects[system_card[i]+"-icon"].color = color_doc_fct scene.objects[system_card[i]+"-text"].color = color_doc_fct for i in range(len(python_card)): scene.objects[python_card[i]].color = color_doc_fct scene.objects[python_card[i]+"-icon"].color = color_doc_fct scene.objects[python_card[i]+"-text"].color = color_doc_fct # Cacher et mettre les couleurs sur les liens (URL) scene.objects['Doc_chap-system-url_title'].setVisible(False,True) scene.objects['Doc_chap-system-url_title'].color = color_doc_activate scene.objects['Doc_chap-python-url_title'].setVisible(False,True) scene.objects['Doc_chap-python-url_title'].color = color_doc_activate for i in range(3): scene.objects['Doc_chap-python-url'+str(i)].setVisible(False,True) scene.objects['Doc_chap-python-url'+str(i)+'-colbox'].suspendPhysics() scene.objects['Doc_chap-python-url'+str(i)].color = color_doc_chap scene.objects['Doc_chap-system-url'+str(i)].setVisible(False,True) scene.objects['Doc_chap-system-url'+str(i)+'-colbox'].suspendPhysics() scene.objects['Doc_chap-system-url'+str(i)].color = color_doc_chap # Chargement du texte text_load() # Mémorisation de la position des pages for page in chap: scene.objects["Doc_chap-"+page]['init_lx']=scene.objects["Doc_chap-"+page].worldPosition.x scene.objects["Doc_chap-"+page]['init_ly']=scene.objects["Doc_chap-"+page].worldPosition.y scene.objects["Doc_chap-"+page]['init_lz']=scene.objects["Doc_chap-"+page].worldPosition.z # Page par défaut scene.objects['Doc']['page_chap'] = "general" ## # Ouvrir la documentation ## def open(): # Placer la tablette scene.active_camera = scene.objects["Camera-Doc"] scene.objects['Doc_close'].color= color_doc_fct scene.objects['Doc'].setVisible(True,True) text_hide() # Placer le nouveau chapitre name_chap = scene.objects['Doc']['page_chap'] scene.objects['Doc-'+name_chap].color = color_doc_activate scene.objects['Doc-'+name_chap+'-text'].color = color_doc_activate scene.objects['Doc_chap-'+name_chap].worldPosition = scene.objects['Doc'].worldPosition scene.objects['Doc_chap-'+name_chap].setVisible(True,True) # URL if name_chap == "system" or name_chap == "python": name_fct = scene.objects['Doc_chap-'+name_chap]['page_fct'] scene.objects['Doc_chap-'+name_chap+'-url_title'].setVisible(False,True) for i in range(3): scene.objects['Doc_chap-'+name_chap+'-url'+str(i)].setVisible(False,True) scene.objects['Doc_chap-'+name_chap+'-url'+str(i)+'-colbox'].suspendPhysics() if name_fct !="": if len(card_description[name_fct][2])>0: scene.objects['Doc_chap-'+name_chap+'-url_title'].setVisible(True,True) for i in range(len(card_description[name_fct][2])): scene.objects['Doc_chap-'+name_chap+'-url'+str(i)]['Text'] = card_description[name_fct][2][i][0] scene.objects['Doc_chap-'+name_chap+'-url'+str(i)].setVisible(True,True) scene.objects['Doc_chap-'+name_chap+'-url'+str(i)+'-colbox'].restorePhysics() scene.objects['Doc_chap-'+name_chap+'-url'+str(i)+'-colbox']['Url'] = card_description[name_fct][2][i][1] # Afficher le texte de la carte active if name_chap != "general" and scene.objects['Doc_chap-'+name_chap]['page_fct'] !="": name_fct = scene.objects['Doc_chap-'+name_chap]['page_fct'] scene.objects['Doc_title']['Text'] = card_description[name_fct][0] scene.objects['Doc_title'].setVisible(True, False) text_show(name_fct) else: scene.objects['Doc_title']['Text'] = " " scene.objects['Doc_title'].setVisible(False,True) text_hide() ## # Fermer la documentation ## def close(cont): if cont.sensors['Click'].status == JUST_ACTIVATED and cont.sensors['MO'].positive : scene.active_camera = scene.objects["Camera"] chap=("general", "system", "python") for page in chap: scene.objects["Doc_chap-"+page].setVisible(False,True) scene.objects["Doc_chap-"+page].worldPosition.x = scene.objects["Doc_chap-"+page]['init_lx'] scene.objects["Doc_chap-"+page].worldPosition.y = scene.objects["Doc_chap-"+page]['init_ly'] scene.objects["Doc_chap-"+page].worldPosition.z = scene.objects["Doc_chap-"+page]['init_lz'] scene.objects['Doc'].setVisible(False,True) ## # Highlight ## def hl (cont): # Activation if cont.sensors['MO'].status == JUST_ACTIVATED : obj = cont.owner name=obj.name[:-7] name_text=name+"-text" name_icon=name+"-icon" # Close ou lien if name == "Doc_close" : scene.objects[name].color = color_doc_hl elif name == "Doc_chap-python-url0" or name == "Doc_chap-python-url1" or name == "Doc_chap-python-url2": scene.objects[name].color = color_doc_hl elif name == "Doc_chap-system-url0" or name == "Doc_chap-system-url1" or name == "Doc_chap-system-url2": scene.objects[name].color = color_doc_hl else: if "Doc-" in name : # Chapitre if name[4:] == scene.objects['Doc']['page_chap']: scene.objects[name].color = color_doc_activate scene.objects[name_text].color = color_doc_activate else: scene.objects[name].color = color_doc_hl scene.objects[name_text].color = color_doc_hl else: # Carte name_chap = scene.objects['Doc']['page_chap'] if name == scene.objects['Doc_chap-'+name_chap]['page_fct'] : scene.objects[name].color = color_doc_activate scene.objects[name_text].color = color_doc_activate scene.objects[name_icon].color = color_doc_activate else: scene.objects[name].color = color_doc_hl scene.objects[name_text].color = color_doc_hl scene.objects[name_icon].color = color_doc_hl # Désactivation if cont.sensors['MO'].status == JUST_RELEASED : obj = cont.owner name=obj.name[:-7] name_text=obj.name[:-7]+"-text" name_icon=obj.name[:-7]+"-icon" # Close ou lien if name == "Doc_close" : scene.objects[name].color = color_doc_fct elif name == "Doc_chap-python-url0" or name == "Doc_chap-python-url1" or name == "Doc_chap-python-url2": scene.objects[name].color = color_doc_fct elif name == "Doc_chap-system-url0" or name == "Doc_chap-system-url1" or name == "Doc_chap-system-url2": scene.objects[name].color = color_doc_fct else: if "Doc-" in name : # Chapitre if name[4:] == scene.objects['Doc']['page_chap']: scene.objects[name].color = color_doc_activate scene.objects[name_text].color = color_doc_activate else: scene.objects[name].color = color_doc_fct scene.objects[name_text].color = color_doc_fct else: # Carte name_chap = scene.objects['Doc']['page_chap'] if name == scene.objects['Doc_chap-'+name_chap]['page_fct'] : scene.objects[name].color = color_doc_activate scene.objects[name_text].color = color_doc_activate scene.objects[name_icon].color = color_doc_activate else: scene.objects[name].color = color_doc_fct scene.objects[name_text].color = color_doc_fct scene.objects[name_icon].color = color_doc_fct ## # Afficher le chapitre ## def chapter(cont): if cont.sensors['Click'].status == JUST_ACTIVATED and cont.sensors['MO'].positive and cont.sensors['Click'].positive: obj = cont.owner scene.objects['Doc_title']['Text'] = " " # Enlever l'ancien chapitre scene.objects['Doc-'+scene.objects['Doc']['page_chap']].color = color_doc_chap scene.objects['Doc-'+scene.objects['Doc']['page_chap']+'-text'].color = color_doc_chap scene.objects["Doc_chap-"+scene.objects['Doc']['page_chap']].worldPosition.x = scene.objects["Doc_chap-"+scene.objects['Doc']['page_chap']]['init_lx'] scene.objects["Doc_chap-"+scene.objects['Doc']['page_chap']].worldPosition.y = scene.objects["Doc_chap-"+scene.objects['Doc']['page_chap']]['init_ly'] scene.objects["Doc_chap-"+scene.objects['Doc']['page_chap']].worldPosition.z = scene.objects["Doc_chap-"+scene.objects['Doc']['page_chap']]['init_lz'] if scene.objects['Doc']['page_chap'] != "general" and scene.objects["Doc_chap-"+scene.objects['Doc']['page_chap']]['page_fct'] !="": text_hide(scene.objects["Doc_chap-"+scene.objects['Doc']['page_chap']]['page_fct']) # Placer le nouveau chapitre name_chap= obj.name[4:-7] scene.objects['Doc-'+name_chap].color = color_doc_activate scene.objects['Doc-'+name_chap+'-text'].color = color_doc_activate scene.objects['Doc']['page_chap'] = name_chap scene.objects['Doc_chap-'+name_chap].worldPosition = scene.objects['Doc'].worldPosition scene.objects['Doc_chap-'+name_chap].setVisible(True,True) # URL if name_chap == "system" or name_chap == "python": name_fct = scene.objects['Doc_chap-'+name_chap]['page_fct'] scene.objects['Doc_chap-'+name_chap+'-url_title'].setVisible(False,True) for i in range(3): scene.objects['Doc_chap-'+name_chap+'-url'+str(i)].setVisible(False,True) scene.objects['Doc_chap-'+name_chap+'-url'+str(i)+'-colbox'].suspendPhysics() if name_fct !="": if len(card_description[name_fct][2])>0: scene.objects['Doc_chap-'+name_chap+'-url_title'].setVisible(True,True) for i in range(len(card_description[name_fct][2])): scene.objects['Doc_chap-'+name_chap+'-url'+str(i)]['Text'] = card_description[name_fct][2][i][0] scene.objects['Doc_chap-'+name_chap+'-url'+str(i)].setVisible(True,True) scene.objects['Doc_chap-'+name_chap+'-url'+str(i)+'-colbox'].restorePhysics() scene.objects['Doc_chap-'+name_chap+'-url'+str(i)+'-colbox']['Url'] = card_description[name_fct][2][i][1] # Afficher le texte de la carte active if name_chap != "general" and scene.objects['Doc_chap-'+name_chap]['page_fct'] !="": name_fct = scene.objects['Doc_chap-'+name_chap]['page_fct'] scene.objects['Doc_title']['Text'] = card_description[name_fct][0] scene.objects['Doc_title'].setVisible(True, False) text_show(name_fct) else: scene.objects['Doc_title']['Text'] = " " scene.objects['Doc_title'].setVisible(False,True) text_hide() ## # Cacher le texte ## def text_hide(card=None): if card is None: for card in card_description: for i in range (13): scene.objects['Doc_text-l'+str(i+1)+'-'+str(card)].setVisible(False, False) else: for i in range (13): scene.objects['Doc_text-l'+str(i+1)+'-'+card].setVisible(False, False) ## # Afficher le texte ## def text_show(card=None): if card is None: for card in card_description: for i in range (13): scene.objects['Doc_text-l'+str(i+1)+'-'+str(card)].setVisible(True, False) else: for i in range (13): scene.objects['Doc_text-l'+str(i+1)+'-'+card].setVisible(True, False) ## # Préchargement des textes ## def text_load(): for i in range (13): scene.objects['Doc_text-l'+str(i+1)+'-ref']['Text'] = "" # Création des objets 3D for card in card_description: lines = card_description[card][1].split("\n") for i in range (13): doc_text= scene.addObject('Doc_text-l'+str(i+1), None, 0.00, True) doc_text.setParent(scene.objects['Doc']) doc_text.name = 'Doc_text-l'+str(i+1)+'-'+str(card) doc_text.worldPosition.x = scene.objects['Doc_text-l'+str(i+1)+'-ref'].worldPosition.x doc_text.worldPosition.y = scene.objects['Doc_text-l'+str(i+1)+'-ref'].worldPosition.y doc_text.worldPosition.z = scene.objects['Doc_text-l'+str(i+1)+'-ref'].worldPosition.z doc_text.setVisible(False, False) if i >= len(lines): scene.objects['Doc_text-l'+str(i+1)+'-'+str(card)]['Text']="" else: if len(lines[i]) ==0: scene.objects['Doc_text-l'+str(i+1)+'-'+str(card)]['Text']="" else: scene.objects['Doc_text-l'+str(i+1)+'-'+str(card)]['Text']=lines[i] ## # Afficher les details de la fonction à partir d'une carte ## def card (cont): if cont.sensors['Click'].status == JUST_ACTIVATED and cont.sensors['MO'].positive : obj = cont.owner name_chap = scene.objects['Doc']['page_chap'] name_fct= obj.name[:-7] scene.objects['Doc_title']['Text'] = " " # Enlever l'ancienne carte if scene.objects['Doc_chap-'+name_chap]['page_fct'] !="": text_hide(scene.objects['Doc_chap-'+name_chap]['page_fct']) scene.objects[scene.objects['Doc_chap-'+name_chap]['page_fct']].color = color_doc_fct scene.objects[scene.objects['Doc_chap-'+name_chap]['page_fct']+'-text'].color = color_doc_fct scene.objects[scene.objects['Doc_chap-'+name_chap]['page_fct']+'-icon'].color = color_doc_fct # Afficher le texte de la carte scene.objects['Doc_chap-'+name_chap]['page_fct'] = name_fct scene.objects[name_fct].color = color_doc_activate scene.objects[name_fct+'-icon'].color = color_doc_activate scene.objects[name_fct+'-text'].color = color_doc_activate scene.objects['Doc_title']['Text'] = card_description[name_fct][0] scene.objects['Doc_title'].setVisible(True, False) text_show(name_fct) # URL if name_chap == "system" or name_chap == "python": name_fct = scene.objects['Doc_chap-'+name_chap]['page_fct'] scene.objects['Doc_chap-'+name_chap+'-url_title'].setVisible(False,True) for i in range(3): scene.objects['Doc_chap-'+name_chap+'-url'+str(i)].setVisible(False,True) scene.objects['Doc_chap-'+name_chap+'-url'+str(i)+'-colbox'].suspendPhysics() if name_fct !="": if len(card_description[name_fct][2])>0: scene.objects['Doc_chap-'+name_chap+'-url_title'].setVisible(True,True) for i in range(len(card_description[name_fct][2])): scene.objects['Doc_chap-'+name_chap+'-url'+str(i)]['Text'] = card_description[name_fct][2][i][0] scene.objects['Doc_chap-'+name_chap+'-url'+str(i)].setVisible(True,True) scene.objects['Doc_chap-'+name_chap+'-url'+str(i)+'-colbox'].restorePhysics() scene.objects['Doc_chap-'+name_chap+'-url'+str(i)+'-colbox']['Url'] = card_description[name_fct][2][i][1] ## # Aller à la page internet ## def link (cont): if cont.sensors['Click'].status == JUST_ACTIVATED and cont.sensors['MO'].positive : webbrowser.open(cont.owner['Url'])