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

drawingarea.py (17368B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\packages\drawingpolygon\drawingarea.py
      2 from drawingpolygon.cursors import CURSOR_SELECTOR_POSITION
      3 from drawingpolygon.polygon import Polygon
      4 from drawingpolygon.xbutton import XButton
      5 from carbonui import uiconst
      6 from carbonui.primitives.container import Container
      7 from carbonui.uicore import uicore
      8 from signals import Signal
      9 from uthread2 import StartTasklet
     10 
     11 class DrawingArea(Container):
     12     default_max_polygons = 8
     13 
     14     def ApplyAttributes(self, attributes):
     15         super(DrawingArea, self).ApplyAttributes(attributes)
     16         self.max_polygons = attributes.get('max_polygons', self.default_max_polygons)
     17         self.buffer_size = attributes.get('buffer_size')
     18         self.on_polygon_added = Signal(signalName='on_polygon_added')
     19         self.on_polygon_deleted = Signal(signalName='on_polygon_deleted')
     20         self.on_polygons_changed = Signal(signalName='on_polygons_changed')
     21         self.on_wip_polygon_cleared = Signal(signalName='on_wip_polygon_cleared')
     22         self.on_polygon_started = Signal(signalName='on_polygon_started')
     23         self.on_polygon_selected = Signal(signalName='on_polygon_selected')
     24         self.on_polygon_deselected = Signal(signalName='on_polygon_deselected')
     25         self.on_point_added = Signal(signalName='on_point_added')
     26         self.buffer = Container(name='buffer_container', parent=self.parent, align=uiconst.CENTER, idx=0)
     27         self.content = DrawingContent(name='content_container', parent=self, align=uiconst.CENTER, state=uiconst.UI_NORMAL, drawing_area=self, buffer_area=self.buffer)
     28         self.x_button = None
     29 
     30     def SetSize(self, width, height):
     31         super(DrawingArea, self).SetSize(width, height)
     32         self.buffer.width = self.width + 2 * self.buffer_size
     33         self.buffer.height = self.height + 2 * self.buffer_size
     34         self.content.SetSize(width, height)
     35 
     36     def get_polygons(self):
     37         return [ polygon.get_data() for polygon in self.content.polygons ]
     38 
     39     def get_time_spent_per_polygon(self):
     40         return [ polygon.get_time_spent() for polygon in self.content.polygons ]
     41 
     42     def get_mouse_position(self):
     43         return self._constrain_to_area(uicore.uilib.x, uicore.uilib.y)
     44 
     45     def transform_coordinates_to_proportions(self, x, y):
     46         new_x, new_y = (0, 0)
     47         if self.width:
     48             new_x = float(x) / self.width
     49         if self.height:
     50             new_y = float(y) / self.height
     51         return [new_x, 1.0 - new_y]
     52 
     53     def transform_proportions_to_coordinates(self, x, y):
     54         return (x * self.width, y * self.height)
     55 
     56     def _constrain_to_area(self, x, y):
     57         min_x, min_y = (0, 0)
     58         max_x, max_y = self.width, self.height
     59         return (sorted([min_x, max_x, x - self.absoluteLeft])[1], sorted([min_y, max_y, y - self.absoluteTop])[1])
     60 
     61     def is_position_in_area(self, x, y):
     62         min_x, min_y = (0, 0)
     63         max_x, max_y = self.width, self.height
     64         return min_x <= x <= max_x and min_y <= y <= max_y
     65 
     66     def is_there_another_vertex_in_range(self, polygon, vertex):
     67         for other_vertex in polygon.vertices:
     68             if other_vertex.order != vertex.order:
     69                 if vertex.is_within_range(other_vertex):
     70                     return True
     71 
     72         return False
     73 
     74     def add_x_button(self, button_function, x, y):
     75         self.remove_x_button()
     76         self.x_button = XButton(name='x_button', parent=self.buffer, align=uiconst.TOPLEFT, button_function=button_function)
     77         self.x_button.left = self.buffer_size + x + 5
     78         self.x_button.top = self.buffer_size + y - self.x_button.height - 5
     79 
     80     def remove_x_button(self):
     81         if self.x_button:
     82             self.x_button.Close()
     83             self.x_button = None
     84 
     85     def toggle_polygon_selection(self, index):
     86         self.content.toggle_polygon_selection(index)
     87 
     88     def inject_click(self, x, y):
     89         self.content.on_click(x, y)
     90 
     91     def change_perimeter_color(self, perimeter_color):
     92         self.content.change_perimeter_color(perimeter_color)
     93 
     94     def inject_polygon(self, vertices, send_to_back = None, show_vertices = None, show_fill = None, perimeter_width = None, perimeter_color = None, fill_color = None, fill_bg_color = None):
     95         self.content.inject_polygon(vertices, send_to_back, show_vertices, show_fill, perimeter_width, perimeter_color, fill_color, fill_bg_color)
     96 
     97     def Enable(self, *args):
     98         super(DrawingArea, self).Enable(*args)
     99         self.buffer.SetState(uiconst.UI_PICKCHILDREN)
    100         self.content.SetState(uiconst.UI_NORMAL)
    101 
    102     def Disable(self, *args):
    103         super(DrawingArea, self).Disable(*args)
    104         self.buffer.Disable(*args)
    105         self.content.Disable(*args)
    106         self.remove_x_button()
    107 
    108     def Close(self):
    109         self.buffer.Close()
    110         super(DrawingArea, self).Close()
    111 
    112     def on_number_of_polygons_changed(self, number_of_polygons):
    113         self.on_polygons_changed(number_of_polygons)
    114 
    115 
    116 class CannotAddMorePolygons(Exception):
    117     pass
    118 
    119 
    120 class DrawingContent(Container):
    121     MINIMUM_POINTS = 3
    122 
    123     def ApplyAttributes(self, attributes):
    124         super(DrawingContent, self).ApplyAttributes(attributes)
    125         self.drawing_area = attributes.get('drawing_area')
    126         self.buffer_area = attributes.get('buffer_area')
    127         self.on_number_of_polygons_changed = Signal(signalName='on_number_of_polygons_changed')
    128         self.on_number_of_polygons_changed.connect(self.drawing_area.on_number_of_polygons_changed)
    129         self.on_polygon_added = Signal(signalName='on_polygon_added')
    130         self.on_polygon_added.connect(self.drawing_area.on_polygon_added)
    131         self.on_polygon_deleted = Signal(signalName='on_polygon_deleted')
    132         self.on_polygon_deleted.connect(self.drawing_area.on_polygon_deleted)
    133         self.on_wip_polygon_cleared = Signal(signalName='on_wip_polygon_cleared')
    134         self.on_wip_polygon_cleared.connect(self.drawing_area.on_wip_polygon_cleared)
    135         self.on_polygon_started = Signal(signalName='on_polygon_started')
    136         self.on_polygon_started.connect(self.drawing_area.on_polygon_started)
    137         self.on_polygon_selected = Signal(signalName='on_polygon_selected')
    138         self.on_polygon_selected.connect(self.drawing_area.on_polygon_selected)
    139         self.on_polygon_deselected = Signal(signalName='on_polygon_deselected')
    140         self.on_polygon_deselected.connect(self.drawing_area.on_polygon_deselected)
    141         self.on_point_added = Signal(signalName='on_point_added')
    142         self.on_point_added.connect(self.drawing_area.on_point_added)
    143         self.cursor = CURSOR_SELECTOR_POSITION
    144         self.polygons = []
    145         self.active_polygon = None
    146         self.selected_polygon = None
    147         self._selection_update_thread = None
    148         self._on_internal_click_thread = None
    149         self._on_external_click_thread = None
    150         self.mouse_up_cookie = uicore.event.RegisterForTriuiEvents(uiconst.UI_MOUSEUP, self.on_general_click)
    151         self.mouse_move_cookie = uicore.event.RegisterForTriuiEvents(uiconst.UI_MOUSEMOVE, self.on_general_move)
    152         self.esc_cookie = uicore.event.RegisterForTriuiEvents(uiconst.UI_KEYUP, self.on_general_key_up)
    153 
    154     def Close(self):
    155         self._release_control()
    156         uicore.event.UnregisterForTriuiEvents(self.mouse_up_cookie)
    157         uicore.event.UnregisterForTriuiEvents(self.mouse_move_cookie)
    158         uicore.event.UnregisterForTriuiEvents(self.esc_cookie)
    159         super(DrawingContent, self).Close()
    160 
    161     def Disable(self, *args):
    162         super(DrawingContent, self).Disable(*args)
    163         self.clear()
    164         self.select_all_polygons()
    165 
    166     def SetSize(self, width, height):
    167         previous_width, previous_height = self.width, self.height
    168         super(DrawingContent, self).SetSize(width, height)
    169         if previous_width < 1 or previous_height < 1:
    170             return
    171         x_scale = float(self.width) / previous_width
    172         y_scale = float(self.height) / previous_height
    173         for polygon in self.polygons:
    174             polygon.scale(x_scale, y_scale)
    175 
    176         self.clear()
    177 
    178     def clear(self):
    179         self._clear_selection()
    180         self._clear_wip_polygon()
    181         self._set_active_polygon(None)
    182 
    183     def _add_new_polygon(self):
    184         if self.drawing_area.max_polygons and len(self.polygons) >= self.drawing_area.max_polygons:
    185             raise CannotAddMorePolygons()
    186         new_polygon = self._add_polygon()
    187         self._set_active_polygon(new_polygon)
    188         self.on_polygon_started()
    189 
    190     def change_perimeter_color(self, perimeter_color):
    191         for polygon in self.polygons:
    192             polygon.set_perimeter_color(perimeter_color)
    193 
    194     def inject_polygon(self, points, send_to_back = None, show_vertices = None, show_fill = None, perimeter_width = None, perimeter_color = None, fill_color = None, fill_bg_color = None):
    195         polygon = self._add_polygon(show_vertices, show_fill, perimeter_width, perimeter_color, fill_color, fill_bg_color)
    196         for x_proportion, y_proportion in points:
    197             x, y = self.drawing_area.transform_proportions_to_coordinates(x_proportion, y_proportion)
    198             polygon.add_point(x, y)
    199 
    200         polygon.autocomplete_polygon()
    201         if send_to_back:
    202             polygon.set_arrangement_order(-1)
    203 
    204     def select_all_polygons(self):
    205         for polygon in self.polygons:
    206             polygon.select()
    207 
    208     def invalidate_polygons(self, invalid_polygons):
    209         for polygon in self.polygons:
    210             is_valid = polygon.order not in invalid_polygons
    211             polygon.update_validation(is_valid)
    212 
    213     def _add_polygon(self, show_vertices = None, show_fill = None, perimeter_width = None, perimeter_color = None, fill_color = None, fill_bg_color = None):
    214         new_polygon = Polygon(parent=self, area=self.drawing_area, order=len(self.polygons), show_vertices=show_vertices, show_fill=show_fill, perimeter_width=perimeter_width, perimeter_color=perimeter_color, fill_color_selected=fill_color, fill_bg_color_selected=fill_bg_color)
    215         new_polygon.on_edit_started.connect(self._on_polygon_edit_started)
    216         new_polygon.on_deleted.connect(self._on_polygon_deleted)
    217         new_polygon.on_completed.connect(self._on_polygon_completed)
    218         new_polygon.on_wip_segment_added.connect(self._take_control)
    219         new_polygon.on_wip_segment_removed.connect(self._release_control)
    220         self.polygons.append(new_polygon)
    221         return new_polygon
    222 
    223     def _set_active_polygon(self, polygon):
    224         self.active_polygon = polygon
    225         if self.active_polygon and not self.active_polygon.is_complete:
    226             for polygon in self.polygons:
    227                 if polygon == self.active_polygon:
    228                     polygon.enable()
    229                 else:
    230                     polygon.disable()
    231 
    232         else:
    233             for polygon in self.polygons:
    234                 polygon.enable()
    235 
    236     def _on_polygon_edit_started(self, polygon):
    237         if self._selection_update_thread:
    238             self._selection_update_thread.kill()
    239         self._selection_update_thread = StartTasklet(self._update_polygon_selection, polygon)
    240 
    241     def _on_polygon_completed(self, polygon):
    242         if self._selection_update_thread:
    243             self._selection_update_thread.kill()
    244         if self.active_polygon:
    245             self._deselect_polygon(self.active_polygon)
    246         self._set_active_polygon(None)
    247         self.on_polygon_added(polygon.order)
    248         self.on_number_of_polygons_changed(len(self.polygons))
    249 
    250     def _on_polygon_deleted(self, index):
    251         if index >= len(self.polygons):
    252             return
    253         polygon_to_delete = self.polygons[index]
    254         is_complete = polygon_to_delete.is_complete
    255         if self.active_polygon == polygon_to_delete:
    256             self._set_active_polygon(None)
    257         if self.selected_polygon == polygon_to_delete:
    258             self.selected_polygon = None
    259         del self.polygons[index]
    260         for order, polygon in enumerate(self.polygons):
    261             polygon.set_order(order)
    262 
    263         self._update_polygon_arrangement()
    264         self.on_number_of_polygons_changed(len(self.polygons))
    265         self.on_polygon_deleted(index, is_complete)
    266 
    267     def toggle_polygon_selection(self, index):
    268         if index >= len(self.polygons):
    269             return
    270         polygon = self.polygons[index]
    271         if self.selected_polygon == polygon:
    272             self._deselect_polygon(polygon)
    273             if self.active_polygon == polygon:
    274                 self._set_active_polygon(None)
    275         else:
    276             self._clear_selection()
    277             self._select_polygon(polygon)
    278             self._set_active_polygon(polygon)
    279 
    280     def _select_polygon(self, polygon):
    281         polygon.select()
    282         self.selected_polygon = polygon
    283         self._update_polygon_arrangement()
    284         self.on_polygon_selected(polygon.order)
    285 
    286     def _deselect_polygon(self, polygon):
    287         polygon.deselect()
    288         self.selected_polygon = None
    289         self._update_polygon_arrangement()
    290         self.on_polygon_deselected(polygon.order)
    291 
    292     def _clear_selection(self):
    293         if self.selected_polygon:
    294             self._deselect_polygon(self.selected_polygon)
    295 
    296     def _update_polygon_arrangement(self):
    297         for polygon in reversed(self.polygons):
    298             if polygon == self.selected_polygon:
    299                 polygon.set_arrangement_order(0)
    300             else:
    301                 polygon.set_arrangement_order(-1)
    302 
    303     def _update_polygon_selection(self, polygon):
    304         try:
    305             if self.active_polygon == polygon:
    306                 return
    307             if self.active_polygon and not self.active_polygon.destroyed:
    308                 if not self.active_polygon.is_complete:
    309                     return
    310                 self._deselect_polygon(self.active_polygon)
    311                 self._set_active_polygon(None)
    312             if not polygon.destroyed:
    313                 self._set_active_polygon(polygon)
    314                 self._select_polygon(self.active_polygon)
    315         finally:
    316             self._selection_update_thread = None
    317 
    318     def _clear_wip_polygon(self):
    319         if self.active_polygon and not self.active_polygon.is_complete:
    320             self.active_polygon.Close()
    321             self._set_active_polygon(None)
    322             self.on_wip_polygon_cleared()
    323 
    324     def _take_control(self):
    325         uicore.registry.RequestControl(self)
    326 
    327     def _release_control(self):
    328         uicore.registry.ReleaseControl(self)
    329 
    330     def OnClick(self, *args):
    331         x, y = self.drawing_area.get_mouse_position()
    332         self.on_click(x, y)
    333 
    334     def on_click(self, x, y):
    335         if self._on_internal_click_thread:
    336             self._on_internal_click_thread.kill()
    337         self._on_internal_click_thread = StartTasklet(self._process_internal_left_click, x, y)
    338 
    339     def _is_left_click(self, vkey):
    340         if vkey == uiconst.MOUSELEFT:
    341             return True
    342         return False
    343 
    344     def on_general_click(self, obj, eventID, param):
    345         vkey, flag = param
    346         x, y = uicore.uilib.x - self.absoluteLeft, uicore.uilib.y - self.absoluteTop
    347         is_left_click = self._is_left_click(vkey)
    348         if self.drawing_area.is_position_in_area(x, y):
    349             if is_left_click:
    350                 return True
    351             self.on_internal_right_click()
    352         elif is_left_click:
    353             self.on_external_left_click()
    354         return True
    355 
    356     def on_internal_right_click(self):
    357         if self._on_internal_click_thread:
    358             self._on_internal_click_thread.kill()
    359         self._on_internal_click_thread = StartTasklet(self._process_internal_right_click)
    360 
    361     def on_external_left_click(self):
    362         if self._on_external_click_thread:
    363             self._on_external_click_thread.kill()
    364         self._on_external_click_thread = StartTasklet(self._process_external_left_click)
    365 
    366     def _process_internal_left_click(self, x, y):
    367         try:
    368             if self.active_polygon:
    369                 if self.active_polygon.is_complete:
    370                     self._deselect_polygon(self.active_polygon)
    371                     self._add_new_polygon()
    372             else:
    373                 self._add_new_polygon()
    374             if self.active_polygon and not self.active_polygon.is_complete:
    375                 self.active_polygon.add_point(x, y)
    376                 self.on_point_added()
    377         except CannotAddMorePolygons:
    378             return
    379         finally:
    380             self._on_internal_click_thread = None
    381 
    382     def _process_internal_right_click(self):
    383         try:
    384             self._clear_wip_polygon()
    385         finally:
    386             self._on_general_click_thread = None
    387 
    388     def _process_external_left_click(self):
    389         try:
    390             if self.active_polygon:
    391                 if self.active_polygon.get_point_amount() >= self.MINIMUM_POINTS:
    392                     self.active_polygon.autocomplete_polygon()
    393                 else:
    394                     self._clear_wip_polygon()
    395         finally:
    396             self._on_general_click_thread = None
    397 
    398     def on_general_move(self, *args):
    399         x, y = uicore.uilib.x - self.absoluteLeft, uicore.uilib.y - self.absoluteTop
    400         if not self.drawing_area.is_position_in_area(x, y):
    401             return True
    402         if self.active_polygon:
    403             self.active_polygon.move_cursor()
    404         return True
    405 
    406     def on_general_key_up(self, _window, _event_id, key_data, *args):
    407         key, _ = key_data
    408         if key == uiconst.VK_ESCAPE:
    409             self._clear_wip_polygon()
    410         return True