Kuinka luoda mukautettu käyttöliittymä?

Katsoin tätä videota tehdessäni mukautettua käyttöliittymävälilehteä Pythonilla ja rivi from bpy.types import Menu, Panel, UIList, näen esineiden nimet, jotka näyttävät siltä, että niitä kaikkia voidaan käyttää erilaisten käyttöliittymäelementtien tekemiseen. Sisältääkö bpy.types kaikki objektit, joita ihmiset käyttävät, kun he haluavat tehdä mukautettuja käyttöliittymäelementtejä?

Kommentit

  • Tyyppi on luokka, jota voidaan käyttää mallina muille luokille perintönä, esim class MyPanel(bpy.types.Panel): blender.org/api/blender_python_api_current/bpy.types.html . Ehdotukseni on katsoa läpi ” tekstieditorin > mallit > python > Ui * ” esimerkit, jotka tulevat tehosekoittimen mukana.

Vastaa

Käyttöliittymän tai lisäosan suunnittelu on pohjimmiltaan yhdistelmä Ominaisuudet -toiminnon ja sisäänrakennettujen Tyyppiluokkien perimistä (paneeli, operaattori , Valikko jne.).


Ominaisuudet

Aloita määrittämällä ensin Ominaisuudet . Ominaisuudet ovat pohjimmiltaan ”tietotyyppejä”, ja ne voidaan näyttää käyttöliittymässä käyttäjän peruskäyttäytymistä varten. Huomaa, että pääset kunkin ominaisuuden arvoon melkein mistä tahansa. Täytä täydellinen luettelo konsolissa käyttämällä python ”s dir() -menetelmää kohdassa bpy.props :

kirjoita kuvan kuvaus tähän Klikkaa suuremmaksi

Ominaisuuden ulkonäkö

Ominaisuuden määrittely

from bpy.props import (StringProperty, BoolProperty, IntProperty, FloatProperty, EnumProperty, ) my_bool : BoolProperty( name="Enable or Disable", description="A bool property", default = False ) my_int : IntProperty( name = "Set a value", description="A integer property", default = 23, min = 10, max = 100 ) ... 

Huomaa, että Blender 2.8x -tilasta alkaen ominaisuudet tulisi määrittää muuttujille u laulaa yksi kaksoispiste : tavallisen määritysoperaattorin = sijaan, kuten Blender 2.7x : ssä tai vanhemmissa Blender.


Tyypit

bpy.types ovat luokkamalleja, jotka on rakennettu perintöä varten.

Paneeli

Paneelit ovat kaikkialla Blenderissä, joten se on käyttöliittymän perustekijä. käytettävä paneeli on määritelty bl_space_type . Tehosekoittimen käyttöliittymä on ”kontekstiherkkä”, joten voit määrittää bl_context saadaksesi paneelin yhteen vastaavaan tilaan (Object Mode, Edit Mode jne.) .

kirjoita kuvan kuvaus tähän

class HelloWorldPanel(bpy.types.Panel): bl_idname = "OBJECT_PT_hello_world" bl_label = "Hello World" bl_space_type = "PROPERTIES" bl_region_type = "WINDOW" bl_context = "object" def draw(self, context): self.layout.label(text="Hello World") bpy.utils.register_class(HelloWorldPanel) 

Alipaneelit

kirjoita kuvan kuvaus tähän

Blender 2.8x -versiosta alkaen meillä voi olla alipaneelit määrittämällä tietty paneeli (ylätaso) bl_parent_id :

import bpy class HelloWorldPanel: bl_space_type = "VIEW_3D" bl_region_type = "UI" bl_category = "Tools" bl_options = {"DEFAULT_CLOSED"} class HELLO_PT_World1(HelloWorldPanel, bpy.types.Panel): bl_idname = "HELLO_PT_World1" bl_label = "Panel 1" def draw(self, context): layout = self.layout layout.label(text="This is the main panel.") class HELLO_PT_World2(HelloWorldPanel, bpy.types.Panel): bl_parent_id = "HELLO_PT_World1" bl_label = "Panel 2" def draw(self, context): layout = self.layout layout.label(text="First Sub Panel of Panel 1.") class HELLO_PT_World3(HelloWorldPanel, bpy.types.Panel): bl_parent_id = "HELLO_PT_World1" bl_label = "Panel 3" def draw(self, context): layout = self.layout layout.label(text="Second Sub Panel of Panel 1.") classes = ( HELLO_PT_World1, HELLO_PT_World2, HELLO_PT_World3 ) def register(): for cls in classes: bpy.utils.register_class(cls) def unregister(): for cls in classes: bpy.utils.unregister_class(cls) if __name__ == "__main__": register() 

Katso myös: Tekstieditori> Mallit> Python > Käyttöliittymäpaneeli .

Operaattori

Operaattori on tärkein ymmärrettävä bitti. Voit näyttää ”operaattorit” painikkeina ja rekisteröityneinä, ja voit soittaa sille kaikkialta bpy.ops.IDNAME() -palvelun kautta. Se on myös sekoittimen muotoilu, kaikki oikeat painikkeet ovat ”operaattoreita” konepellin alla, enimmäkseen kirjoitettuina C: llä, mutta altistuvat sitten pythonille. Katso myös: Tekstieditori> Mallit> Python > Operaattori … .

kirjoita kuvan kuvaus tähän

class HelloWorldMinimal(bpy.types.Operator): bl_idname = "wm.hello_world" bl_label = "Minimal Operator" def execute(self, context): # Report "Hello World" to the Info Area self.report({"INFO"}, "Hello World") return {"FINISHED"} bpy.utils.register_class(HelloWorldMinimal) # test call the operator bpy.ops.wm.hello_world() 

Operaattoriluokan malli sisältää ennalta määritetyt menetelmät , itse asiassa poll, invoke, execute, draw, modal ja cancel, joita voidaan käyttää mukautettujen ominaisuuksien mukana kaikentyyppisissä toiminnoissa ja myös tarjota käyttäjille vuorovaikutusta. Täydellisempi esimerkki operaattorista :

class HelloWorld(bpy.types.Operator): bl_idname = "wm.hello_world" bl_label = "Minimal Operator" bl_options = {"REGISTER"} # Operator user properties, should be assigned using a single colon : # instead of using an equal sign = in Blender 2.8 report_flag: bpy.props.BoolProperty( name = "Report", default = True) @classmethod # Will never run when poll returns false def poll(cls, context): return context.object def invoke(self, context, event): # Used for user interaction wm = context.window_manager return wm.invoke_props_dialog(self) def draw(self, context): # Draw options (typically displayed in the tool-bar) row = self.layout row.prop(self, "report_flag", text="Report Hello World") def execute(self, context): # Runs by default if self.report_flag: self.report({"INFO"}, "Hello World") else: print ("Hello World") return {"FINISHED"} bpy.utils.register_class(HelloWorld) # For interaction, pass "INVOKE_DEFAULT" when calling # the operator, this way invoke runs before execute method bpy.ops.wm.hello_world("INVOKE_DEFAULT") 

Lisätietoja: Kuinka soitetaanko vahvistusvalintaikkunaan? ( erittäin vaarallisille käyttäjille ).

Valikko

Määritä / peri mukautetun valikon kohdalla valikkoluokka . Lisää operaattorit ja ominaisuudet draw() -toimintoon oikein (katso myös: Tekstieditori> Mallit> Python> UI-valikkomallit) .

kirjoita kuvan kuvaus tähän

class SimpleCustomMenu(bpy.types.Menu): bl_label = "Simple Custom Menu" bl_idname = "OBJECT_MT_simple_custom_menu" def draw(self, context): layout = self.layout layout.operator("wm.open_mainfile") layout.operator("wm.save_as_mainfile") # The menu can also be called from scripts bpy.ops.wm.call_menu(name=SimpleCustomMenu.bl_idname) 

Voit myös piirtää painikkeen, jolla voit soittaa valikkoon ilmoittamatta ylimääräistä operaattoria layout.operator("wm.call_menu").name="bl_idname"

Alivalikko

Saadaksesi alivalikon, soita toiselle päävalikossa layout.menu(bl_idname) .

kirjoita kuvan kuvaus tähän

class MyCustomMenu(bpy.types.Menu): bl_label = "First Menu" bl_idname = "OBJECT_MT_custom_menu" def draw(self, context): layout = self.layout layout.label(text="Hello First Menu!", icon="WORLD_DATA") # call the second custom menu layout.menu("OBJECT_MT_sub_menu", icon="COLLAPSEMENU") class MyCustomSubMenu(bpy.types.Menu): bl_label = "Sub Menu" bl_idname = "OBJECT_MT_sub_menu" def draw(self, context): layout = self.layout layout.label(text="Hello Second Menu!", icon="WORLD_DATA") # call another predefined menu layout.operator("wm.call_menu", text="Unwrap").name = "VIEW3D_MT_uv_map" # draw a button within the panel to call the first menu class OBJECT_PT_my_panel(bpy.types.Panel): ... def draw(self, context): layout.operator("wm.call_menu", text="Call My Menu").name = "OBJECT_MT_custom_menu" ... 

Kaikki yhdessä

Lisäosaa luodessasi vaaditaan yleensä paljon ominaisuuksia. Parempaa organisaatiota varten voit luoda ”asetusluokan” käyttämällä PropertyGroup -palvelua. Huomaa, että Blender 2.8x , ominaisuudet tulisi määrittää yhdellä kaksoispisteellä : tavallisen määritysoperaattorin = sijaan.

2.7x

class MySettings(PropertyGroup): my_bool = BoolProperty() my_int = IntProperty() my_float = FloatProperty() ... 

2.8x

class MySettings(PropertyGroup): my_bool: BoolProperty() my_int: IntProperty() my_float: FloatProperty() ... 

Samoin kuin Blender 2.8x -moduulista / luokan rekisteröinti on muuttunut estämään nimiristiriidat. bpy.utils.register_module(__name__) ei ole enää käytettävissä, joten sinun on periaatteessa rekisteröitävä / poistettava jokainen luokka erikseen tai silmukan sisällä (paras käytäntö):

classes = ( WM_OT_HelloWorld, OBJECT_PT_CustomPanel, ) def register(): from bpy.utils import register_class for cls in classes: register_class(cls) def unregister(): from bpy.utils import unregister_class for cls in reversed(classes): unregister_class(cls) 

Huomaa , että vanhan nimeämisen lisäksi käytäntöjä , sinun on myös lisättävä erotin , kuten _OT_, _MT_ tai _PT_ luokkasi nimelle perityn luokkatyypin perusteella (Operator, Menu, Panel).


Laajennusmalli

Seuraava lisäosa lisää mukautetun paneelin 3D-näkymän työkaluhyllyyn ja tulostaa kaikkien mukautettujen ominaisuuksien nykyiset ”käyttäjäarvot” konsolille:

kirjoita kuvan kuvaus tähän

Blender 2.7x

 bl_info = { "name": "Add-on Template", "description": "", "author": "", "version": (0, 0, 2), "blender": (2, 70, 0), "location": "3D View > Tools", "warning": "", # used for warning icon and text in addons panel "wiki_url": "", "tracker_url": "", "category": "Development" } import bpy from bpy.props import (StringProperty, BoolProperty, IntProperty, FloatProperty, EnumProperty, PointerProperty, ) from bpy.types import (Panel, Operator, PropertyGroup, ) # ------------------------------------------------------------------------ # Scene Properties # ------------------------------------------------------------------------ class MySettings(PropertyGroup): my_bool = BoolProperty( name="Enable or Disable", description="A bool property", default = False ) my_int = IntProperty( name = "Int Value", description="A integer property", default = 23, min = 10, max = 100 ) my_float = FloatProperty( name = "Float Value", description = "A float property", default = 23.7, min = 0.01, max = 30.0 ) my_string = StringProperty( name="User Input", description=":", default="", maxlen=1024, ) my_enum = EnumProperty( name="Dropdown:", description="Apply Data to attribute.", items=[ ("OP1", "Option 1", ""), ("OP2", "Option 2", ""), ("OP3", "Option 3", ""), ] ) # ------------------------------------------------------------------------ # Operators # ------------------------------------------------------------------------ class WM_OT_HelloWorld(bpy.types.Operator): bl_idname = "wm.hello_world" bl_label = "Print Values Operator" def execute(self, context): scene = context.scene mytool = scene.my_tool # print the values to the console print("Hello World") print("bool state:", mytool.my_bool) print("int value:", mytool.my_int) print("float value:", mytool.my_float) print("string value:", mytool.my_string) print("enum state:", mytool.my_enum) return {"FINISHED"} # ------------------------------------------------------------------------ # Menus # ------------------------------------------------------------------------ class OBJECT_MT_CustomMenu(bpy.types.Menu): bl_idname = "object.custom_menu" bl_label = "Select" def draw(self, context): layout = self.layout # Built-in example operators layout.operator("object.select_all", text="Select/Deselect All").action = "TOGGLE" layout.operator("object.select_all", text="Inverse").action = "INVERT" layout.operator("object.select_random", text="Random") # ------------------------------------------------------------------------ # Panel in Object Mode # ------------------------------------------------------------------------ class OBJECT_PT_CustomPanel(Panel): bl_idname = "object.custom_panel" bl_label = "My Panel" bl_space_type = "VIEW_3D" bl_region_type = "TOOLS" bl_category = "Tools" bl_context = "objectmode" @classmethod def poll(self,context): return context.object is not None def draw(self, context): layout = self.layout scene = context.scene mytool = scene.my_tool layout.prop(mytool, "my_bool") layout.prop(mytool, "my_enum", text="") layout.prop(mytool, "my_int") layout.prop(mytool, "my_float") layout.prop(mytool, "my_string") layout.operator("wm.hello_world") layout.menu(OBJECT_MT_CustomMenu.bl_idname, text="Presets", icon="SCENE") layout.separator() # ------------------------------------------------------------------------ # Registration # ------------------------------------------------------------------------ def register(): bpy.utils.register_module(__name__) bpy.types.Scene.my_tool = PointerProperty(type=MySettings) def unregister(): bpy.utils.unregister_module(__name__) del bpy.types.Scene.my_tool if __name__ == "__main__": register() 

Lista:

https://gist.github.com/p2or/a00bdde9f2751940717a404cf977dd01

Blender 2.8x

bl_info = { "name": "Add-on Template", "description": "", "author": "p2or", "version": (0, 0, 3), "blender": (2, 80, 0), "location": "3D View > Tools", "warning": "", # used for warning icon and text in addons panel "wiki_url": "", "tracker_url": "", "category": "Development" } import bpy from bpy.props import (StringProperty, BoolProperty, IntProperty, FloatProperty, FloatVectorProperty, EnumProperty, PointerProperty, ) from bpy.types import (Panel, Menu, Operator, PropertyGroup, ) # ------------------------------------------------------------------------ # Scene Properties # ------------------------------------------------------------------------ class MyProperties(PropertyGroup): my_bool: BoolProperty( name="Enable or Disable", description="A bool property", default = False ) my_int: IntProperty( name = "Int Value", description="A integer property", default = 23, min = 10, max = 100 ) my_float: FloatProperty( name = "Float Value", description = "A float property", default = 23.7, min = 0.01, max = 30.0 ) my_float_vector: FloatVectorProperty( name = "Float Vector Value", description="Something", default=(0.0, 0.0, 0.0), min= 0.0, # float max = 0.1 ) my_string: StringProperty( name="User Input", description=":", default="", maxlen=1024, ) my_path: StringProperty( name = "Directory", description="Choose a directory:", default="", maxlen=1024, subtype="DIR_PATH" ) my_enum: EnumProperty( name="Dropdown:", description="Apply Data to attribute.", items=[ ("OP1", "Option 1", ""), ("OP2", "Option 2", ""), ("OP3", "Option 3", ""), ] ) # ------------------------------------------------------------------------ # Operators # ------------------------------------------------------------------------ class WM_OT_HelloWorld(Operator): bl_label = "Print Values Operator" bl_idname = "wm.hello_world" def execute(self, context): scene = context.scene mytool = scene.my_tool # print the values to the console print("Hello World") print("bool state:", mytool.my_bool) print("int value:", mytool.my_int) print("float value:", mytool.my_float) print("string value:", mytool.my_string) print("enum state:", mytool.my_enum) return {"FINISHED"} # ------------------------------------------------------------------------ # Menus # ------------------------------------------------------------------------ class OBJECT_MT_CustomMenu(bpy.types.Menu): bl_label = "Select" bl_idname = "OBJECT_MT_custom_menu" def draw(self, context): layout = self.layout # Built-in operators layout.operator("object.select_all", text="Select/Deselect All").action = "TOGGLE" layout.operator("object.select_all", text="Inverse").action = "INVERT" layout.operator("object.select_random", text="Random") # ------------------------------------------------------------------------ # Panel in Object Mode # ------------------------------------------------------------------------ class OBJECT_PT_CustomPanel(Panel): bl_label = "My Panel" bl_idname = "OBJECT_PT_custom_panel" bl_space_type = "VIEW_3D" bl_region_type = "UI" bl_category = "Tools" bl_context = "objectmode" @classmethod def poll(self,context): return context.object is not None def draw(self, context): layout = self.layout scene = context.scene mytool = scene.my_tool layout.prop(mytool, "my_bool") layout.prop(mytool, "my_enum", text="") layout.prop(mytool, "my_int") layout.prop(mytool, "my_float") layout.prop(mytool, "my_float_vector", text="") layout.prop(mytool, "my_string") layout.prop(mytool, "my_path") layout.operator("wm.hello_world") layout.menu(OBJECT_MT_CustomMenu.bl_idname, text="Presets", icon="SCENE") layout.separator() # ------------------------------------------------------------------------ # Registration # ------------------------------------------------------------------------ classes = ( MyProperties, WM_OT_HelloWorld, OBJECT_MT_CustomMenu, OBJECT_PT_CustomPanel ) def register(): from bpy.utils import register_class for cls in classes: register_class(cls) bpy.types.Scene.my_tool = PointerProperty(type=MyProperties) def unregister(): from bpy.utils import unregister_class for cls in reversed(classes): unregister_class(cls) del bpy.types.Scene.my_tool if __name__ == "__main__": register() 

Lista: https://gist.github.com/p2or/2947b1aa89141caae182526a8fc2bc5a


Monimutkaisempia esimerkkejä

Kommentit

  • Hyvää, tämä oli todella perusteellista! ’ on paljon enemmän Python-käyttöliittymään kuin vain bpy.types ja haha. Kiitos!
  • Tämä on hämmästyttävän tiivis ja selkeä yleiskatsaus, joka olisi hieno osana dokumentteja. Kesti viikkoja selvittää tämä monien, monien esimerkkien avulla.
  • Paljon parempi kuin virallinen asiakirja. Tallensit päiväni!

Vastaa

muokattu versio – tehosekoittimelle 2.80 !!!

 # https://blender.stackexchange.com/q/57306/3710 # https://blender.stackexchange.com/q/79779/3710 # # modified for blender 2.80 # last modification: 2019-09-12 -- add custom-preferences panel -- Emanuel Rumpf -- bl_info = { "name": "Add-on Template", "description": "", "author": "", "version": (0, 0, 2), "blender": (2, 80, 0), "location": "3D View > Tools", "warning": "", # used for warning icon and text in addons panel "wiki_url": "", "tracker_url": "", "category": "Development" } """ This is an addon - template for blender 2.80 Use it as base for new addons. -- Some changes made for blender 2.80 version (from 2.79): - Properties are annotations now, assigned with : not = - bl_region_type now is "UI" not "TOOLS" - Registration procedure changed: Use bpy.utils.register_class() not register_module() More information see: python api blender 2.80 """ import bpy #import collections #import importlib #import mathutils #import math from bpy.utils import ( register_class, unregister_class ) from bpy.props import ( StringProperty, BoolProperty, IntProperty, FloatProperty, FloatVectorProperty, EnumProperty, PointerProperty, ) from bpy.types import ( Panel, AddonPreferences, Operator, PropertyGroup, ) # this must match the addon name, use "__package__" # when defining this in a submodule of a python package. addon_name = __name__ # when single file #addon_name = __package__ # when file in package # ------------------------------------------------------------------------ # settings in addon-preferences panel # ------------------------------------------------------------------------ # panel update function for PREFS_PT_MyPrefs panel def _update_panel_fnc (self, context): # # load addon custom-preferences print( addon_name, ": update pref.panel function called" ) # main_panel = OBJECT_PT_my_panel # main_panel .bl_category = context .preferences.addons[addon_name] .preferences.tab_label # re-register for update unregister_class( main_panel ) register_class( main_panel ) class PREFS_PT_MyPrefs( AddonPreferences ): """ Custom Addon Preferences Panel - in addon activation panel - menu / edit / preferences / add-ons """ bl_idname = addon_name tab_label: StringProperty( name="Tab Label", description="Choose a label-name for the panel tab", default="New Addon", update=_update_panel_fnc ) def draw(self, context): layout = self.layout row = layout.row() col = row.column() col.label(text="Tab Label:") col.prop(self, "tab_label", text="") # ------------------------------------------------------------------------ # properties visible in the addon-panel # ------------------------------------------------------------------------ class PG_MyProperties (PropertyGroup): my_bool : BoolProperty( name="Enable or Disable", description="A bool property", default = False ) my_int : IntProperty( name = "Int Value", description="A integer property", default = 23, min = 10, max = 100 ) my_float : FloatProperty( name = "Float Value", description = "A float property", default = 23.7, min = 0.01, max = 30.0 ) my_float_vector : FloatVectorProperty( name = "Float Vector Value", description="Something", default=(0.0, 0.0, 0.0), min= 0.0, # float max = 0.1 ) my_string : StringProperty( name="User Input", description=":", default="", maxlen=1024, ) my_enum : EnumProperty( name="Dropdown:", description="Apply Data to attribute.", items=[ ("OP1", "Option 1", ""), ("OP2", "Option 2", ""), ("OP3", "Option 3", ""), ] ) # ------------------------------------------------------------------------ # operators # ------------------------------------------------------------------------ class OT_HelloWorldOperator (bpy.types.Operator): bl_idname = "wm.hello_world" bl_label = "Print Values Operator" def execute(self, context): scene = context.scene mytool = scene.my_tool # print the values to the console print("Hello World") print("bool state:", mytool.my_bool) print("int value:", mytool.my_int) print("float value:", mytool.my_float) print("string value:", mytool.my_string) print("enum state:", mytool.my_enum) return {"FINISHED"} # ------------------------------------------------------------------------ # menus # ------------------------------------------------------------------------ class MT_BasicMenu (bpy.types.Menu): bl_idname = "OBJECT_MT_select_test" bl_label = "Select" def draw(self, context): layout = self.layout # built-in example operators layout.operator("object.select_all", text="Select/Deselect All").action = "TOGGLE" layout.operator("object.select_all", text="Inverse").action = "INVERT" layout.operator("object.select_random", text="Random") # ------------------------------------------------------------------------ # addon - panel -- visible in objectmode # ------------------------------------------------------------------------ class OBJECT_PT_my_panel (Panel): bl_idname = "OBJECT_PT_my_panel" bl_label = "My Panel" bl_space_type = "VIEW_3D" bl_region_type = "UI" bl_category = "Tool" # note: replaced by preferences-setting in register function bl_context = "objectmode" # def __init(self): # super( self, Panel ).__init__() # bl_category = bpy.context.preferences.addons[__name__].preferences.category @classmethod def poll(self,context): return context.object is not None def draw(self, context): layout = self.layout scene = context.scene mytool = scene.my_tool layout.prop( mytool, "my_bool") layout.prop( mytool, "my_enum", text="") layout.prop( mytool, "my_int") layout.prop( mytool, "my_float") layout.prop( mytool, "my_float_vector", text="") layout.prop( mytool, "my_string") layout.operator( "wm.hello_world") layout.menu( "OBJECT_MT_select_test", text="Presets", icon="SCENE") # ------------------------------------------------------------------------ # register and unregister # ------------------------------------------------------------------------ classes = ( PG_MyProperties, # OT_HelloWorldOperator, MT_BasicMenu, OBJECT_PT_my_panel, # PREFS_PT_MyPrefs, ) def register(): # for cls in classes: register_class(cls) # bpy.types.Scene.my_tool = PointerProperty(type=PG_MyProperties) # def unregister(): # for cls in reversed(classes): unregister_class(cls) # del bpy.types.Scene.my_tool # remove PG_MyProperties if __name__ == "__main__": pass #register() 

Tehosekoittimen 2.80 versiolle tehtyjä muutoksia:

  • Ominaisuudet ovat nyt merkintöjä, jotka on nimetty : ei =
  • bl_region_type on nyt ”käyttöliittymä”, ei ”TYÖKALUT”
  • Rekisteröintimenettelyä muutettu:
    • Käytä bpy.utils.register_class() ei register_module()

Lisätietoja: python api -tehosekoitin 2.80

Kommentit

  • OMG. Kiitos paljon. Oppin pythonia tehosekoittimessa. Koodaan paljon VB.netissä & C #. Taisteli käyttöliittymän kanssa. Tämä selitti sen hyvin ja
  • hei, haluaisin tietää, mihin scene.my_tool viittasi? koska en nähnyt missä se määritettiin ensin?
  • Voi nyt saan sen, joten rekisteröidään osoitinominaisuus sanojen joidenkin muiden ominaisuuksien tallentamiseksi?

Vastaa

Sähköpostiosoitettasi ei julkaista. Pakolliset kentät on merkitty *