NovaBryan/EVE_Dump
Log | Files | 📦 下载最新源码 (.zip) | Refs | README

tutorial.py (20571B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\packages\projectdiscovery\client\projects\subcellular\tutorial.py
      2 from __future__ import absolute_import
      3 import copy
      4 import logging
      5 import math
      6 import random
      7 import carbonui.const as uiconst
      8 import localization
      9 import uthread
     10 from carbonui.primitives.container import Container
     11 from carbonui.primitives.sprite import Sprite
     12 from carbonui.primitives.transform import Transform
     13 from carbonui.uianimations import animations
     14 from .categoryselector import CategorySelector
     15 from eve.client.script.ui.control import eveLabel
     16 from carbonui.control.button import Button
     17 from eve.client.script.ui.control.eveLabel import EveCaptionLarge
     18 from eve.client.script.ui.tooltips.tooltipUtil import SetTooltipHeaderAndDescription
     19 from .info import INFO as PROJECT_INFO
     20 from .processingview import ProcessingView
     21 from projectdiscovery.client import const
     22 from projectdiscovery.client.projects.subcellular import nested_categories_from_json
     23 from projectdiscovery.client.util.dialogue import Dialogue
     24 from projectdiscovery.client.util.eventlistener import eventlistener, on_event
     25 from projectdiscovery.common.exceptions import MissingKeyError, NoConnectionToAPIError
     26 from .resultwindow import ResultWindow
     27 from .rewardsview import RewardsView
     28 from .taskimages import TaskImage
     29 logger = logging.getLogger(__name__)
     30 
     31 @eventlistener()
     32 
     33 class Tutorial(Container):
     34     MAX_CATEGORIES = PROJECT_INFO['resultSchema']['maxItems'] - 1
     35 
     36     def ApplyAttributes(self, attributes):
     37         super(Tutorial, self).ApplyAttributes(attributes)
     38         self.service = sm.RemoteSvc('ProjectDiscovery')
     39         self.selection = []
     40         self.isGreeting = False
     41         self.isSubmitting = False
     42         self.project_id = self.service.get_project_id()
     43         self.playerState = attributes.get('playerState')
     44         self.starting_scale = attributes.get('starting_scale')
     45         self.bottom_container = self.parent.parent.FindChild('bottom_container')
     46         self.loading = False
     47         self.task_number = None
     48         self.finishedTaskCount = 0
     49         self.setup_layout()
     50 
     51     def setup_layout(self):
     52         self.dialogue_container = Container(name='dialogue_container', parent=self.parent, idx=0)
     53         self.left_main_container = Container(name='left_main_container', parent=self, align=uiconst.TOLEFT_PROP, width=0.5, clipChildren=True, top=20)
     54         self.category_container = Container(name='category_container', parent=self, align=uiconst.TOLEFT_PROP, width=0.5, padTop=45, clipChildren=True)
     55         self.task_label = EveCaptionLarge(parent=self.left_main_container, align=uiconst.CENTERTOP, text=localization.GetByLabel('UI/ProjectDiscovery/Subcellular/Tutorial/TaskLabel'), color=(0.48, 0.48, 0.48, 1), top=35)
     56         self.explanation_icon = Sprite(parent=self.left_main_container, align=uiconst.TOPRIGHT, width=54, height=54, top=35, left=15, texturePath='res:/UI/Texture/WindowIcons/attention.png', state=uiconst.UI_HIDDEN)
     57         self.training_task_image = TaskImage(label=const.Texts.TaskImageLabel, parent=self.left_main_container, align=uiconst.TOPLEFT_PROP, pos=(25, 80, 420, 445), starting_scale=self.starting_scale)
     58         self.result_window = ResultWindow(name='ResultWindow', parent=self.parent, align=uiconst.TOALL, opacity=0, isTrainingPhase=True, taskImage=self.training_task_image, starting_scale=self.starting_scale, bottom_container=self.bottom_container)
     59         self.rewards_view = RewardsView(parent=self.parent, opacity=0, playerState=self.playerState, bottom_container=self.bottom_container, idx=0, state=uiconst.UI_DISABLED)
     60         self.rewards_view.isTraining = True
     61         self.rewards_view.tutorial_completed = False
     62         self.processing_view = ProcessingView(parent=self.parent, opacity=0, state=uiconst.UI_DISABLED)
     63         self.training_category_selector = CategorySelector(categories=nested_categories_from_json(PROJECT_INFO['info']['classes']), parent=self.category_container, state=uiconst.UI_HIDDEN, starting_scale=self.starting_scale)
     64         self.main_button_container = Container(name='main_button_container', parent=self.bottom_container, align=uiconst.CENTERBOTTOM, width=355, height=53, bgTexturePath='res:/UI/Texture/classes/ProjectDiscovery/footerBG.png')
     65         self.submit_button_container = Container(name='submitButtonContainer', parent=self.main_button_container, width=250, align=uiconst.CENTER, height=40, top=5)
     66         self.submit_button = Button(name='submitButton', parent=self.submit_button_container, align=uiconst.CENTER, label=localization.GetByLabel('UI/ProjectDiscovery/SubmitButtonLabel'), fontsize=18, fixedwidth=170, fixedheight=30, func=lambda x: self.submit_training_solution())
     67         Sprite(parent=self.submit_button_container, align=uiconst.CENTERLEFT, width=34, height=20, texturePath='res:/UI/Texture/classes/ProjectDiscovery/submitArrow.png', opacity=0.7)
     68         Sprite(parent=Transform(parent=self.submit_button_container, align=uiconst.CENTERRIGHT, width=34, height=20, rotation=math.pi), align=uiconst.CENTERRIGHT, width=34, height=20, texturePath='res:/UI/Texture/classes/ProjectDiscovery/submitArrow.png', opacity=0.7)
     69         self.task_id = eveLabel.Label(parent=self, align=uiconst.BOTTOMRIGHT, height=20, opacity=0.5, left=10)
     70         self.refresh_task_button = Button(name='refreshTaskButton', parent=self.category_container, align=uiconst.CENTER, func=lambda x: self.reset_task(), idx=0, label='Refresh Task', fixedwidth=120, fixedheight=30, state=uiconst.UI_HIDDEN)
     71 
     72     def get_total_task_count(self):
     73         count = 0
     74         for level in self.levelList:
     75             count += level['tasksToFinishLevel']
     76 
     77         return count
     78 
     79     def get_finished_task_count(self):
     80         count = 1
     81         for x in range(0, self.level):
     82             count += self.levelList[x]['tasksToFinishLevel']
     83 
     84         return count
     85 
     86     @on_event(const.Events.QuitTutorialTooltips)
     87     def enable_everything_after_tutorial(self):
     88         self.enable_ui()
     89 
     90     def get_tutorial_tooltip_steps(self):
     91         return [{'owner': self.training_task_image.images_container,
     92           'header': localization.GetByLabel('UI/ProjectDiscovery/Subcellular/Tutorial/SampleImageHeader'),
     93           'text': localization.GetByLabel('UI/ProjectDiscovery/Subcellular/Tutorial/SampleImageText')},
     94          {'owner': self.training_category_selector,
     95           'header': localization.GetByLabel('UI/ProjectDiscovery/Subcellular/Tutorial/CategorySelectionHeader'),
     96           'text': localization.GetByLabel('UI/ProjectDiscovery/Subcellular/Tutorial/CategorySelectionText')},
     97          {'owner': self.training_category_selector.super_categories[0],
     98           'header': localization.GetByLabel('UI/ProjectDiscovery/Subcellular/Tutorial/NucleusHeader'),
     99           'text': localization.GetByLabel('UI/ProjectDiscovery/Subcellular/Tutorial/NucleusText')},
    100          {'owner': self.training_category_selector.super_categories[1],
    101           'header': localization.GetByLabel('UI/ProjectDiscovery/Subcellular/Tutorial/CytoplasmHeader'),
    102           'text': localization.GetByLabel('UI/ProjectDiscovery/Subcellular/Tutorial/CytoplasmText')},
    103          {'owner': self.training_category_selector.super_categories[2],
    104           'header': localization.GetByLabel('UI/ProjectDiscovery/Subcellular/Tutorial/PeripheryHeader'),
    105           'text': localization.GetByLabel('UI/ProjectDiscovery/Subcellular/Tutorial/PeripheryText')},
    106          {'owner': self.training_category_selector.super_categories[3],
    107           'header': localization.GetByLabel('UI/ProjectDiscovery/Subcellular/Tutorial/MiscellaneousHeader'),
    108           'text': localization.GetByLabel('UI/ProjectDiscovery/Subcellular/Tutorial/MiscellaneousText')},
    109          {'owner': self.training_category_selector.super_categories[4],
    110           'header': localization.GetByLabel('UI/ProjectDiscovery/Subcellular/Tutorial/NotIdentifiableHeader'),
    111           'text': localization.GetByLabel('UI/ProjectDiscovery/Subcellular/Tutorial/NotIdentifiableText')},
    112          {'owner': self.training_task_image.colorFilterContainer,
    113           'header': localization.GetByLabel('UI/ProjectDiscovery/Subcellular/Tutorial/ChannelFilterHeader'),
    114           'text': localization.GetByLabel('UI/ProjectDiscovery/Subcellular/Tutorial/ChannelFilterText')},
    115          {'owner': self.main_button_container,
    116           'header': localization.GetByLabel('UI/ProjectDiscovery/Subcellular/Tutorial/SubmitButtonHeader'),
    117           'text': localization.GetByLabel('UI/ProjectDiscovery/Subcellular/Tutorial/SubmitButtonText')}]
    118 
    119     @on_event(const.Events.ProjectDiscoveryStarted)
    120     def skip_tutorial(self, show_dialogue):
    121         self.service.skip_tutorial()
    122 
    123     def start(self):
    124         self.show_greeting_dialogue()
    125         if self.playerState.finishedTutorial:
    126             self.playerState = self.service.reset_tutorial()
    127         self.level = self.playerState.tutorialLevel
    128         self.levelList = copy.deepcopy(const.get_training_task_list(self.project_id))
    129         self.levelDict = copy.deepcopy(self.levelList[self.level])
    130         self.new_training_task()
    131         self.finishedTaskCount = self.get_finished_task_count()
    132         self.set_task_label()
    133 
    134     def show_greeting_dialogue(self):
    135         self.disable_ui()
    136         Dialogue(name='greetingDialogue', parent=self.dialogue_container, align=uiconst.CENTER, width=450, height=340, messageText=localization.GetByLabel('UI/ProjectDiscovery/Subcellular/Tutorial/GreetingText'), messageHeaderText=localization.GetByLabel('UI/ProjectDiscovery/Subcellular/Tutorial/GreetingHeader'), label=localization.GetByLabel('UI/ProjectDiscovery/Subcellular/Tutorial/GreetingLabel'), buttonLabel=localization.GetByLabel('UI/ProjectDiscovery/Subcellular/Tutorial/GreetingButton'), toHide=self, isTutorial=True)
    137 
    138     def close(self):
    139         self.dialogue_container.Close()
    140         self.main_button_container.Close()
    141         self.result_window.Close()
    142         self.processing_view.Close()
    143         self.training_task_image.Flush()
    144         self.training_task_image.Close()
    145         self.training_category_selector.Close()
    146         self.rewards_view.Close()
    147         self.Close()
    148 
    149     def reset_task(self):
    150         if not self.loading:
    151             self.loading = True
    152             self.refresh_task_button.SetState(uiconst.UI_HIDDEN)
    153             self.training_task_image.reset_image()
    154             self.training_category_selector.cascade_categories_out()
    155             self.training_category_selector.reset_categories()
    156             self.new_training_task()
    157             self.loading = False
    158 
    159     def new_training_task(self):
    160         self.training_task_image.reset_image()
    161         self.selection = []
    162         self.hide_explanation_icon()
    163         if not self.levelDict:
    164             logger.error('tutorial.py::new_training_task:: no training tasks found')
    165             return
    166         self.choose_random_task()
    167         self.explanation = self.levelDict[self.task_number]
    168         try:
    169             self.task = self.service.new_training_task(self.task_number)
    170         except (NoConnectionToAPIError, MissingKeyError):
    171             self.disable_ui()
    172             self.training_category_selector.cascade_categories_out()
    173             self.category_container.SetState(uiconst.UI_NORMAL)
    174             self.training_category_selector.SetState(uiconst.UI_DISABLED)
    175             self.show_error_message_and_button()
    176             return
    177 
    178         self.task_id.SetText(self.task['taskId'])
    179         self.delete_task_number()
    180         self.set_categories_to_solution()
    181         self.set_task_label()
    182         sm.ScatterEvent(const.Events.NewTask, self.task)
    183         self.training_category_selector.cascade_categories_in()
    184         self.training_category_selector.SetState(uiconst.UI_NORMAL)
    185         self.enable_ui()
    186 
    187     def show_error_message_and_button(self):
    188         self.training_task_image.hide_loading_wheel()
    189         self.training_task_image.show_error_message()
    190         self.refresh_task_button.SetState(uiconst.UI_NORMAL)
    191 
    192     def set_task_label(self):
    193         self.task_label.SetText(localization.GetByLabel('UI/ProjectDiscovery/Subcellular/Tutorial/TaskLabel') + ' ' + str(self.finishedTaskCount) + '/' + str(self.get_total_task_count()))
    194 
    195     def set_categories_to_solution(self):
    196         relevant_categories = list(self.task['solution'])
    197         for super_cat in self.training_category_selector.super_categories:
    198             for sub in super_cat.sub_categories:
    199                 if sub.id in self.task['solution']:
    200                     relevant_categories.extend(sub.excludes)
    201 
    202         for super_cat in self.training_category_selector.super_categories:
    203             for sub in super_cat.sub_categories:
    204                 if sub.id in self.task['solution']:
    205                     temp_list = list(super_cat.sub_categories)
    206                     if sub in temp_list:
    207                         temp_list.remove(sub)
    208                     max_cats = 2
    209                     if len(temp_list) < max_cats:
    210                         max_cats = len(temp_list)
    211                     for n in range(0, max_cats):
    212                         item = random.choice(temp_list)
    213                         relevant_categories.append(item.id)
    214                         if item in temp_list:
    215                             temp_list.remove(item)
    216 
    217                     break
    218 
    219         for super_cat in self.training_category_selector.super_categories:
    220             for sub in super_cat.sub_categories:
    221                 if sub.id not in relevant_categories:
    222                     sub.set_unavailable()
    223                     sub.set_unclickable()
    224                 else:
    225                     sub.set_clickable()
    226                     sub.set_available()
    227 
    228     def delete_task_number(self):
    229         if self.task_number:
    230             del self.levelDict[self.task_number]
    231             self.task_number = None
    232 
    233     def choose_random_task(self):
    234         if not self.task_number:
    235             self.task_number = random.choice(self.levelDict.keys()[1:])
    236 
    237     def hide_explanation_icon(self):
    238         self.explanation_icon.SetAlpha(0)
    239         self.explanation_icon.SetState(uiconst.UI_HIDDEN)
    240 
    241     def open_error_dialogue(self):
    242         self.disable_ui()
    243         self.dialogue = Dialogue(name='ErrorDialogue', parent=self.dialogue_container, align=uiconst.CENTER, width=450, height=215, messageText=localization.GetByLabel('UI/ProjectDiscovery/NoCategorySelectedErrorMessage'), messageHeaderText=localization.GetByLabel('UI/ProjectDiscovery/NoCategorySelectedHeader'), label=localization.GetByLabel('UI/ProjectDiscovery/NotificationHeader'), buttonLabel=localization.GetByLabel('UI/ProjectDiscovery/ErrorButton'), toHide=self)
    244 
    245     def open_too_many_categories_selected_dialogue(self):
    246         self.disable_ui()
    247         self.dialogue = Dialogue(name='ErrorDialogue', parent=self.dialogue_container, align=uiconst.CENTER, width=450, height=215, messageText=localization.GetByLabel('UI/ProjectDiscovery/TooManyCategoriesMessage'), messageHeaderText=localization.GetByLabel('UI/ProjectDiscovery/TooManyCategoriesHeader'), label=localization.GetByLabel('UI/ProjectDiscovery/NotificationHeader'), buttonLabel=localization.GetByLabel('UI/ProjectDiscovery/ErrorButton'), toHide=self)
    248 
    249     def disable_ui(self):
    250         self.left_main_container.state = uiconst.UI_DISABLED
    251         self.category_container.state = uiconst.UI_DISABLED
    252         self.main_button_container.state = uiconst.UI_DISABLED
    253         self.training_category_selector.state = uiconst.UI_DISABLED
    254 
    255     @on_event(const.Events.EnableUI)
    256     def enable_ui(self):
    257         self.training_task_image.state = uiconst.UI_NORMAL
    258         self.left_main_container.state = uiconst.UI_NORMAL
    259         self.category_container.state = uiconst.UI_NORMAL
    260         self.main_button_container.state = uiconst.UI_NORMAL
    261         self.training_category_selector.state = uiconst.UI_NORMAL
    262 
    263     def submit_training_solution(self):
    264         if self.isSubmitting:
    265             return
    266         if not self.selection:
    267             self.open_error_dialogue()
    268             return
    269         self.isSubmitting = True
    270         self.finishedTaskCount += 1
    271         sm.GetService('audio').SendUIEvent(const.Sounds.MainImageLoopStop)
    272         classification = list(set([ cat['id'] for cat in self.selection if not cat['excluded'] ]))
    273         self.result = {'playerSelection': classification,
    274          'task': {'solution': self.task['solution']}}
    275         SetTooltipHeaderAndDescription(targetObject=self.explanation_icon, headerText='', descriptionText=self.explanation)
    276         self.result_window.assign_result(self.result)
    277         self.training_category_selector.cascade_categories_out()
    278         self.training_category_selector.state = uiconst.UI_DISABLED
    279         animations.FadeOut(self.main_button_container)
    280         animations.FadeOut(self.training_task_image.colorFilterContainer)
    281         animations.FadeOut(self.task_label)
    282         animations.FadeOut(self.refresh_task_button)
    283         self.training_task_image.start_transmission_animation()
    284         self.left_main_container.state = uiconst.UI_DISABLED
    285         self.category_container.state = uiconst.UI_DISABLED
    286         self.main_button_container.state = uiconst.UI_DISABLED
    287 
    288     @on_event(const.Events.TransmissionFinished)
    289     def on_transmission_finished(self):
    290         self.left_main_container.state = uiconst.UI_NORMAL
    291         self.result_window.open()
    292         self.result_window.show_result()
    293         animations.FadeIn(self.training_task_image.colorFilterContainer)
    294         self.show_and_blink_explanation_icon()
    295 
    296     def show_and_blink_explanation_icon(self):
    297         self.explanation_icon.SetAlpha(0)
    298         self.explanation_icon.SetState(uiconst.UI_NORMAL)
    299         animations.FadeIn(self.explanation_icon, duration=1, loops=5, curveType=uiconst.ANIM_OVERSHOT5)
    300 
    301     def is_tutorial_finished(self):
    302         self.playerState = self.service.get_player_state()
    303         return self.playerState.tutorialLevel >= self.get_number_of_levels()
    304 
    305     def get_number_of_levels(self):
    306         return len(self.levelList)
    307 
    308     @on_event(const.Events.ContinueFromTrainingResult)
    309     def on_training_result_closed(self):
    310         self.isSubmitting = False
    311         self.increment_level_if_needed()
    312         uthread.new(self.new_training_task)
    313         animations.FadeIn(self, duration=1)
    314         animations.FadeIn(self.main_button_container)
    315         animations.FadeIn(self.training_task_image.colorFilterContainer)
    316         animations.FadeIn(self.task_label, timeOffset=1)
    317         animations.FadeIn(self.refresh_task_button)
    318         self.training_task_image.state = uiconst.UI_NORMAL
    319         self.left_main_container.state = uiconst.UI_NORMAL
    320         self.category_container.state = uiconst.UI_NORMAL
    321         self.main_button_container.state = uiconst.UI_NORMAL
    322 
    323     def end_tutorial(self):
    324         if self.service.give_tutorial_rewards():
    325             result = {'XP_Reward': self.service.get_tutorial_xp_reward(),
    326              'LP_Reward': 0,
    327              'ISK_Reward': 0,
    328              'AK_Reward': 0,
    329              'playerState': self.service.get_player_state(),
    330              'player': {'score': 0.5},
    331              'isTraining': True}
    332             self.rewards_view.tutorial_completed = True
    333             sm.ScatterEvent(const.Events.CloseResult, result)
    334             self.processing_view.start()
    335             settings.char.ui.Set('loadStatisticsAfterSubmission', True)
    336         else:
    337             sm.ScatterEvent(const.Events.ProjectDiscoveryStarted, True)
    338         self.Close()
    339 
    340     def increment_level_if_needed(self):
    341         self.levelDict['tasksToFinishLevel'] -= 1
    342         if self.levelDict['tasksToFinishLevel'] <= 0:
    343             self.playerState = self.service.increase_tutorial_level()
    344             if self.is_tutorial_finished():
    345                 self.end_tutorial()
    346             else:
    347                 self.level = self.playerState.tutorialLevel
    348                 self.levelDict = copy.deepcopy(self.levelList[self.level])
    349 
    350     def _update_excluded(self, excluder):
    351         excluded = set()
    352         for cat in self.selection:
    353             excluded.update(cat.get('excludes', []))
    354 
    355         sm.ScatterEvent(const.Events.ExcludeCategories, excluder, excluded)
    356 
    357     @on_event(const.Events.CategoryChanged)
    358     def on_category_changed(self, hexagon):
    359         if hexagon.category['selected']:
    360             if len(self.selection) > self.MAX_CATEGORIES:
    361                 hexagon.set_unselected()
    362                 self.open_too_many_categories_selected_dialogue()
    363                 return
    364             self.selection.append(hexagon.category)
    365         elif hexagon.category in self.selection:
    366             self.selection.remove(hexagon.category)
    367         self._update_excluded(hexagon.category['id'])