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

image_cache_demo_window.py (10753B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\packages\imagecaching\demo\image_cache_demo_window.py
      2 import logging
      3 import blue
      4 import carbonui
      5 import eveui
      6 import uthread2
      7 from carbonui import fontconst, uiconst, Align
      8 from carbonui.control.dragResizeCont import DragResizeCont
      9 from carbonui.control.singlelineedits.singleLineEditText import SingleLineEditText
     10 from carbonui.control.window import Window
     11 from carbonui.primitives.container import Container
     12 from carbonui.primitives.containerAutoSize import ContainerAutoSize
     13 from carbonui.primitives.sprite import Sprite
     14 from carbonui.uiconst import PickState
     15 from eve.client.script.ui.control.eveEditPlainText import EditPlainText
     16 from eve.client.script.ui.control.eveLabel import EveLabelMedium
     17 from imagecaching.client.api.repository import get_repository
     18 from imagecaching.client.const import INDEX_FILENAME, LOGGER_NAME, OUTCOME_SERVED_STALE, OUTCOME_NOT_MODIFIED, image_cache_path
     19 from six.moves.http_client import responses
     20 logger = logging.getLogger(LOGGER_NAME)
     21 MARGIN = 8
     22 PREVIEW_SIZE = 512
     23 INDEX_PANEL_HEIGHT = 140
     24 INDEX_PANEL_MIN_PROPORTION = 0.15
     25 INDEX_PANEL_MAX_PROPORTION = 0.75
     26 INDEX_PANEL_SETTINGS_ID = 'imageCacheDemoIndexPanel'
     27 DEFAULT_URL = u'https://images.evetech.net/types/587/render?size=512'
     28 DEFAULT_FOLDER = u'demo'
     29 
     30 class ImageCacheDemoWindow(Window):
     31     __guid__ = 'ImageCacheDemoTool'
     32     default_caption = 'Image Cache Demo'
     33     default_captionLabelPath = None
     34     default_windowID = 'ImageCacheDemoTool'
     35     default_iconNum = 'res:/Ui/Texture/WindowIcons/info.png'
     36     default_width = PREVIEW_SIZE + 4 * MARGIN
     37     default_height = PREVIEW_SIZE + 220 + INDEX_PANEL_HEIGHT + MARGIN
     38     default_minSize = (440, 420)
     39     panelID = default_windowID
     40 
     41     def ApplyAttributes(self, attributes):
     42         super(ImageCacheDemoWindow, self).ApplyAttributes(attributes)
     43         self._last_cache_path = None
     44         self._last_image_id = None
     45         self._last_folder = None
     46         self._construct_layout()
     47 
     48     def _construct_layout(self):
     49         self.main_container = Container(name='main_container', parent=self.content, padding=MARGIN)
     50         self._construct_inputs()
     51         self._construct_buttons()
     52         self._construct_index_panel()
     53         self._construct_path_label()
     54         self._construct_preview()
     55 
     56     def _construct_inputs(self):
     57         self.input_container = ContainerAutoSize(name='input_container', parent=self.main_container, align=Align.TOTOP, height=70)
     58         self.url_edit = SingleLineEditText(name='url_edit', parent=self.input_container, align=Align.TOTOP, setvalue=DEFAULT_URL, height=18, label='Image URL', padding=(0, 10, 0, 0))
     59         self.folder_edit = SingleLineEditText(name='folder_edit', parent=self.input_container, align=Align.TOTOP, setvalue=DEFAULT_FOLDER, height=18, label='Folder (relative to cache:/Storage/imagecache)', padding=(0, 30, 0, 0))
     60 
     61     def _construct_buttons(self):
     62         self.button_container = Container(name='button_container', parent=self.main_container, align=Align.TOTOP, height=70, top=MARGIN)
     63         left = 0
     64         self.fetch_button = self._construct_button(name='fetch_button', label='FETCH & CACHE', func=self._on_fetch, width=120, left=left, variant=carbonui.ButtonVariant.PRIMARY)
     65         left += 120 + MARGIN
     66         self.open_button = self._construct_button(name='open_button', label='OPEN FOLDER', func=self._on_open_folder, width=110, left=left, enabled=False)
     67         left += 110 + MARGIN
     68         self.delete_button = self._construct_button(name='delete_button', label='DELETE', func=self._on_delete, width=80, left=left, enabled=False)
     69         left += 80 + MARGIN
     70         self.index_button = self._construct_button(name='index_button', label='SHOW INDEX', func=self._on_show_index, width=110, left=left)
     71 
     72     def _construct_button(self, name, label, func, width, left, variant = None, enabled = True):
     73         return eveui.Button(name=name, parent=self.button_container, state=eveui.State.normal, align=Align.CENTERLEFT, variant=variant or carbonui.ButtonVariant.GHOST, density=carbonui.Density.COMPACT, label=label, func=lambda x: func(), width=width, left=left, enabled=enabled)
     74 
     75     def _construct_preview(self):
     76         self.preview_container = Container(name='preview_container', parent=self.main_container, align=Align.TOALL, padTop=MARGIN, border_style='bright_blue')
     77         self.preview = Sprite(name='preview', parent=self.preview_container, align=Align.CENTER_PRESERVE_ASPECT, state=uiconst.UI_NORMAL, pickState=PickState.OFF)
     78 
     79     def _construct_path_label(self):
     80         self.path_label = EveLabelMedium(name='path_label', parent=self.main_container, align=Align.TOBOTTOM, padding=(0,
     81          MARGIN,
     82          0,
     83          MARGIN), text='No image cached yet.')
     84 
     85     def _construct_index_panel(self):
     86         self.index_panel = DragResizeCont(name='index_panel', parent=self.main_container, align=Align.TOBOTTOM, defaultSize=INDEX_PANEL_HEIGHT, minSize=INDEX_PANEL_MIN_PROPORTION, maxSize=INDEX_PANEL_MAX_PROPORTION, settingsID=INDEX_PANEL_SETTINGS_ID, show_line=True)
     87         self.index_text = EditPlainText(name='index_text', parent=self.index_panel.mainCont, align=Align.TOALL, readonly=True, ignoreTags=True, fontPath=fontconst.CONSOLA_PATH, fontsize=12)
     88         self._set_index_text(u'Index not read yet - press SHOW INDEX.')
     89 
     90     def _on_fetch(self):
     91         url = self.url_edit.GetValue()
     92         folder = self.folder_edit.GetValue()
     93         if not url:
     94             self.path_label.text = 'Enter an image URL first.'
     95             return
     96         image_id = url
     97         cache_path = image_cache_path(folder, image_id)
     98         self.path_label.text = 'Fetching...'
     99         self.fetch_button.enabled = False
    100         uthread2.StartTasklet(self._fetch_worker, image_id, url, cache_path, folder)
    101 
    102     def _fetch_worker(self, image_id, url, cache_path, folder):
    103         res_path = None
    104         status_code = None
    105         error_text = None
    106         outcome = None
    107         try:
    108             repo = get_repository()
    109             res_path = repo.cache_image(image_id, url, cache_path)
    110             status_code = repo.last_status_code
    111             error_text = repo.last_error
    112             outcome = repo.last_outcome
    113         except Exception as e:
    114             logger.exception('Image cache demo: failed to cache %s', url)
    115             error_text = u'%s' % (e,)
    116         finally:
    117             self._on_fetch_done(image_id, folder, res_path, status_code, error_text, outcome)
    118 
    119     def _on_fetch_done(self, image_id, folder, res_path, status_code = None, error_text = None, outcome = None):
    120         if self.destroyed:
    121             return
    122         self.fetch_button.enabled = True
    123         self._start_index_refresh(folder)
    124         if res_path is None:
    125             self.preview.texturePath = None
    126             text = u'Failed to fetch/cache the image.%s' % self._describe_status(status_code)
    127             if error_text:
    128                 text += u' %s' % error_text
    129             self.path_label.text = text
    130             self.open_button.enabled = False
    131             self.delete_button.enabled = False
    132             return
    133         self._last_cache_path = res_path
    134         self._last_image_id = image_id
    135         self._last_folder = folder
    136         self.preview.texturePath = res_path
    137         self.path_label.text = u'%s\n%s' % (self._describe_outcome(outcome, status_code, error_text), res_path)
    138         self.open_button.enabled = True
    139         self.delete_button.enabled = True
    140 
    141     @classmethod
    142     def _describe_outcome(cls, outcome, status_code, error_text):
    143         if outcome == OUTCOME_NOT_MODIFIED:
    144             return u'Already cached - ETag matched, not re-downloaded.%s' % cls._describe_status(status_code)
    145         if outcome == OUTCOME_SERVED_STALE:
    146             detail = cls._describe_status(status_code)
    147             if error_text:
    148                 detail += u' %s' % error_text
    149             return u'Could not refresh; showing previously cached copy.%s' % detail
    150         return u'Downloaded.%s' % cls._describe_status(status_code)
    151 
    152     @staticmethod
    153     def _describe_status(status_code):
    154         if status_code is None:
    155             return u''
    156         return u' (HTTP %s %s)' % (status_code, responses.get(status_code, u'?'))
    157 
    158     def _on_open_folder(self):
    159         if not self._last_cache_path:
    160             return
    161         folder_uri = self._last_cache_path.rsplit(u'/', 1)[0]
    162         fs_folder = blue.paths.ResolvePath(folder_uri)
    163         blue.os.ShellExecute(fs_folder)
    164 
    165     def _on_show_index(self):
    166         self._start_index_refresh(self.folder_edit.GetValue())
    167 
    168     def _on_delete(self):
    169         if not self._last_image_id:
    170             return
    171         try:
    172             get_repository().delete_image(self._last_image_id, self._last_cache_path)
    173         except Exception:
    174             logger.exception('Image cache demo: failed to delete %s', self._last_image_id)
    175             self.path_label.text = 'Failed to delete the cached image.'
    176             return
    177 
    178         folder = self._last_folder
    179         self.preview.texturePath = None
    180         self.path_label.text = 'Deleted: %s' % self._last_cache_path
    181         self._last_cache_path = None
    182         self._last_image_id = None
    183         self._last_folder = None
    184         self.open_button.enabled = False
    185         self.delete_button.enabled = False
    186         self._start_index_refresh(folder)
    187 
    188     def _start_index_refresh(self, folder):
    189         if not folder:
    190             return
    191         uthread2.StartTasklet(self._index_worker, folder)
    192 
    193     def _index_worker(self, folder):
    194         entries = []
    195         error_text = None
    196         try:
    197             entries = get_repository().list_entries(folder)
    198         except Exception as e:
    199             logger.exception('Image cache demo: failed to read the index of %s', folder)
    200             error_text = u'%s' % (e,)
    201 
    202         self._on_index_read(folder, entries, error_text)
    203 
    204     def _on_index_read(self, folder, entries, error_text = None):
    205         if self.destroyed:
    206             return
    207         index_path = u'%s/%s' % (folder, INDEX_FILENAME)
    208         if error_text:
    209             self._set_index_text(u'Could not read %s: %s' % (index_path, error_text))
    210             return
    211         lines = [u'%s - %s' % (index_path, self._describe_entry_count(len(entries)))]
    212         lines.extend((self._describe_entry(entry) for entry in entries))
    213         self._set_index_text(u'\n'.join(lines))
    214 
    215     def _set_index_text(self, text):
    216         self.index_text.SetValue(text, scrolltotop=1)
    217 
    218     @staticmethod
    219     def _describe_entry_count(count):
    220         if count == 1:
    221             return u'1 entry'
    222         return u'%s entries' % count
    223 
    224     @staticmethod
    225     def _describe_entry(entry):
    226         return u'%s\n    res_path: %s\n    e_tag: %s' % (entry.identifier.image_id, entry.attributes.res_path, entry.attributes.e_tag)