рдиреЛрдб рдЗрдВрдЯрд░рдлрд╝реЗрд╕ рдмрдирд╛рдиреЗ рдХреЗ рд▓рд┐рдП WxPython рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХрд╛ рдПрдХ рдЙрджрд╛рд╣рд░рдгред рднрд╛рдЧ 3: рд╕реБрд╡рд┐рдзрд╛рдУрдВ + рдХреАрдмреЛрд░реНрдб рдкреНрд░рд╕рдВрд╕реНрдХрд░рдг рдХреЛ рдЬреЛрдбрд╝рдиреЗ рдХреЗ рд▓рд┐рдП рдЬрд╛рд░реА рд╣реИ

рд▓реЗрдЦреЛрдВ рдХреА рдПрдХ рдЫреЛрдЯреА рд╢реНрд░реГрдВрдЦрд▓рд╛ рдореЗрдВ рд╣рдо рдПрдХ рдпреВрдЬрд░ рдЗрдВрдЯрд░рдлреЗрд╕ рдХреЛ рд╡рд┐рдХрд╕рд┐рдд рдХрд░рдиреЗ рдХреЗ рдПрдХ рдмрд╣реБрдд рд╣реА рд╡рд┐рд╢рд┐рд╖реНрдЯ рдХрд╛рд░реНрдп рдХреЛ рд╣рд▓ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП WxPython рдХреЗ рдЙрдкрдпреЛрдЧ рдХрд╛ рд╡рд░реНрдгрди рдХрд░реЗрдВрдЧреЗ, рдФрд░ рдЗрд╕ рд╕рдорд╛рдзрд╛рди рдХреЛ рд╕рд╛рд░реНрд╡рднреМрдорд┐рдХ рдХреИрд╕реЗ рдмрдирд╛рдПрдВрдЧреЗред рдпрд╣ рдЯреНрдпреВрдЯреЛрд░рд┐рдпрд▓ рдЙрди рд▓реЛрдЧреЛрдВ рдХреЗ рд▓рд┐рдП рдмрдирд╛рдпрд╛ рдЧрдпрд╛ рд╣реИ рдЬреЛ рдкрд╣рд▓реЗ рд╕реЗ рд╣реА рдЗрд╕ рдкреБрд╕реНрддрдХрд╛рд▓рдп рдХрд╛ рдЕрдзреНрдпрдпрди рдХрд░рдирд╛ рд╢реБрд░реВ рдХрд░ рдЪреБрдХреЗ рд╣реИрдВ рдФрд░ рд╕рд░рд▓ рдЙрджрд╛рд╣рд░рдгреЛрдВ рдХреА рддреБрд▓рдирд╛ рдореЗрдВ рдХреБрдЫ рдЕрдзрд┐рдХ рдЬрдЯрд┐рд▓ рдФрд░ рд╕рдордЧреНрд░ рджреЗрдЦрдирд╛ рдЪрд╛рд╣рддреЗ рд╣реИрдВ (рд╣рд╛рд▓рд╛рдВрдХрд┐ рдпрд╣ рдЕрдкреЗрдХреНрд╖рд╛рдХреГрдд рд╕рд░рд▓ рдЪреАрдЬреЛрдВ рд╕реЗ рд╢реБрд░реВ рд╣реЛрддрд╛ рд╣реИ)ред

рдкрд┐рдЫрд▓реЗ рднрд╛рдЧ рдореЗрдВ, рд╣рдордиреЗ рдорд╛рдЙрд╕ рдШрдЯрдирд╛рдУрдВ рдХреЗ рдкреНрд░рд╕рдВрд╕реНрдХрд░рдг рдХреЛ рд▓рд╛рдЧреВ рдХрд┐рдпрд╛, рдФрд░ рдЕрдм рд╣рдо рдПрдХ рдФрд░ рд╡рд┐рд╢реЗрд╖рддрд╛ рдЬреЛрдбрд╝реЗрдВрдЧреЗ рдФрд░ рдХреАрдмреЛрд░реНрдб рд╕реЗ рдШрдЯрдирд╛рдУрдВ рдХреЗ рдкреНрд░рд╕рдВрд╕реНрдХрд░рдг рдХреЛ рд▓рд╛рдЧреВ рдХрд░реЗрдВрдЧреЗред

рднрд╛рдЧ 1: рдбреНрд░рд╛ рдХрд░рдирд╛ рд╕реАрдЦрдирд╛
рднрд╛рдЧ 2: рдорд╛рдЙрд╕ рдИрд╡реЗрдВрдЯреНрд╕ рдХреЛ рд╣реИрдВрдбрд▓ рдХрд░рдирд╛
рднрд╛рдЧ 3: рд╕реБрд╡рд┐рдзрд╛рдУрдВ + рдХреАрдмреЛрд░реНрдб рдкреНрд░рд╕рдВрд╕реНрдХрд░рдг рдХреЛ рдЬреЛрдбрд╝рдиреЗ рдХреЗ рд▓рд┐рдП рдЬрд╛рд░реА рд╣реИ
рднрд╛рдЧ 4: рдбреНрд░реИрдЧ рдПрдВрдб рдбреНрд░реЙрдк рдХреЛ рд▓рд╛рдЧреВ рдХрд░рдирд╛
рднрд╛рдЧ 5: рдиреЛрдб рдХрдиреЗрдХреНрдЯ рдХрд░реЗрдВ

рдХреМрди рдкрд░рд╡рд╛рд╣ рдХрд░рддрд╛ рд╣реИ, рдмрд┐рд▓реНрд▓реА рдХреЗ рдиреАрдЪреЗ рдЖрдкрдХрд╛ рд╕реНрд╡рд╛рдЧрдд рд╣реИ ...


7. рд╡рд╕реНрддреБрдУрдВ рдХрд╛ рдЪрдпрди рдХрд░реЗрдВ


рдкрд┐рдЫрд▓реА рдмрд╛рд░ рд╣рдордиреЗ рд╕реАрдЦрд╛ рдХрд┐ рдХреИрд╕реЗ рдиреЛрдбреНрд╕ рдХреЛ рд╕реНрдерд╛рдирд╛рдВрддрд░рд┐рдд рдХрд░рдирд╛ рд╣реИ, рдЕрдм рдпрд╣ рдЬрд╛рдирдиреЗ рдХрд╛ рд╕рдордп рд╣реИ рдХрд┐ рдЙрдиреНрд╣реЗрдВ рдХреИрд╕реЗ рд╣рдЯрд╛рдпрд╛ рдЬрд╛рдП, рдФрд░ рди рдХреЗрд╡рд▓ рдЙрдиреНрд╣реЗрдВ, рдмрд▓реНрдХрд┐ рдХреИрдирд╡рд╛рд╕ рдкрд░ рдХрд┐рд╕реА рднреА рдСрдмреНрдЬреЗрдХреНрдЯ рдХреЛред рд╣рдЯрд╛рдиреЗ рдХреЗ рд▓рд┐рдП рд╕рд╣реА рд╣реИ, рдЖрдкрдХреЛ рдкрд╣рд▓реЗ рд╕рдВрдХреЗрдд рджреЗрдирд╛ рдЪрд╛рд╣рд┐рдП рдХрд┐ рд╣рдо рдХреНрдпрд╛ рд╣рдЯрд╛рдирд╛ рдЪрд╛рд╣рддреЗ рд╣реИрдВ, рдЕрд░реНрдерд╛рддреНред рдПрдХ рд╡рд╕реНрддреБ рдХрд╛ рдЪрдпрди рдХрд░реЗрдВ, рдЗрд╕рд▓рд┐рдП рд╣рдо рд╡рд┐рд▓реЛрдкрди рдХреЗ рд╕рд╛рде рдирд╣реАрдВ, рдмрд▓реНрдХрд┐ рдЪрдпрди рдХреЗ рд╕рд╛рде рд╢реБрд░реВ рдХрд░реЗрдВрдЧреЗред
рд╣рд░ рдХреЛрдИ рдЖрдорддреМрд░ рдкрд░ рд╣рд╛рдЗрд▓рд╛рдЗрдЯ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд╕рд┐рдВрдЧрд▓ рдХреНрд▓рд┐рдХ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддрд╛ рд╣реИ, рд▓реЗрдХрд┐рди рдЬрд╣рд╛рдВ рддрдХ тАЛтАЛрдореИрдВ рдЬрд╛рдирддрд╛ рд╣реВрдВ, wxPython рдореЗрдВ рдПрдХ рд╡рд┐рд╢реЗрд╖ рд╕рд┐рдВрдЧрд▓ рдХреНрд▓рд┐рдХ рдЗрд╡реЗрдВрдЯ рдирд╣реАрдВ рд╣реИ, рдЗрд╕рд▓рд┐рдП рд╣рдо рдЗрд╕реЗ рд╕реНрд╡рдпрдВ рдЯреНрд░реИрдХ рдХрд░реЗрдВрдЧреЗред рд╣рдо рдЗрд╕реЗ рд╕рдмрд╕реЗ рд╕рд░рд▓ рддрд░реАрдХреЗ рд╕реЗ рдХрд░реЗрдВрдЧреЗ: рдпрджрд┐ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдмрд╛рдИрдВ рдорд╛рдЙрд╕ рдмрдЯрди рдкрд░ рдХреНрд▓рд┐рдХ рдХрд░рддрд╛ рд╣реИ рдФрд░ рдЗрд╕реЗ рдЙрд╕реА рдкрд┐рдХреНрд╕реЗрд▓ рдХреЗ рдКрдкрд░ рдЬрд╛рд░реА рдХрд░рддрд╛ рд╣реИ, рддреЛ рдпрд╣ рдПрдХ рдХреНрд▓рд┐рдХ рд╣реИред рдРрд╕рд╛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рд╣рдореЗрдВ рдпрд╣ рдпрд╛рдж рд░рдЦрдирд╛ рд╣реЛрдЧрд╛ рдХрд┐ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдиреЗ рдмрдЯрди рдкрд░ рдХрд╣рд╛рдБ рдХреНрд▓рд┐рдХ рдХрд┐рдпрд╛ (рд╣рдо рдЗрд╕реЗ "self._lastLeftDownPos" рдореЗрдВ рд╕рд╣реЗрдЬреЗрдВрдЧреЗ), рдФрд░ рдЬрдм рдЬрд╛рд░реА рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рддреЛ рдЪрдпрдирд┐рдд рдСрдмреНрдЬреЗрдХреНрдЯ рдХреА рддреБрд▓рдирд╛ рдХрд░реЗрдВ рдФрд░ рд╕рд╣реЗрдЬреЗрдВ:
def OnMouseLeftUp(self, evt): #Selection if (self._lastLeftDownPos and self._lastLeftDownPos[0] == evt.GetPosition()[0] and self._lastLeftDownPos[1] == evt.GetPosition()[1] and self._objectUnderCursor and self._objectUnderCursor.selectable): self._selectedObject = self._objectUnderCursor self.Render() 

рдареАрдХ рд╣реИ, рддрд╛рдХрд┐ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рджреЗрдЦрддрд╛ рд╣реИ рдХрд┐ рдЙрд╕рдиреЗ рд╡рд╣рд╛рдВ рдХреНрдпрд╛ рд╣рд╛рдЗрд▓рд╛рдЗрдЯ рдХрд┐рдпрд╛ рдерд╛, рдХреИрдирд╡рд╛рд╕ рд░реЗрдВрдбрд░рд┐рдВрдЧ рдХреЛрдб рдореЗрдВ, рдирд┐рдореНрди рдЯреБрдХрдбрд╝рд╛ рдЬреЛрдбрд╝реЗрдВ:
  if self._selectedObject: gc.PushState() self._selectedObject.RenderSelection(gc) gc.PopState() 

рдХреНрдпрд╛ рд╡рд╣ рд╕рдм рд╣реИ? рд╕рдм рдХреБрдЫ рдирд╣реАрдВ, рдПрдХ рдЪреМрдХрд╕ рдкрд╛рдардХ рдиреЗ рджреЗрдЦрд╛ рдХрд┐ рд╣рдордиреЗ рдЬрд╛рдБрдЪ рдХреА рдХрд┐ рдХреНрдпрд╛ рд╡рд╕реНрддреБ рдЪрдпрди рдпреЛрдЧреНрдп рд╣реИ рдФрд░ рдирдИ рд░реЗрдВрдбрд░рд╕реЗрд▓реЗрдХреНрд╢рди рд╡рд┐рдзрд┐ рдХреЛ рдХрд╣рд╛ рдЬрд╛рддрд╛ рд╣реИ, рдЬреЛ рд╕рднреА рдЪрдпрдирд┐рдд рд╡рд╕реНрддреБрдУрдВ рдкрд░ рд╣реЛрдиреА рдЪрд╛рд╣рд┐рдПред рдЗрд╕рд▓рд┐рдП, рдЖрдВрджреЛрд▓рди рдХреА рдЫрд╡рд┐ рдФрд░ рд╕рдорд╛рдирддрд╛ рдореЗрдВ, рд╣рдо рдПрдХ рдирдпрд╛ рдЖрдзрд╛рд░ рд╡рд░реНрдЧ рдЬреЛрдбрд╝реЗрдВрдЧреЗ, рдЬрд┐рд╕рдореЗрдВ рд╕реЗ рдЪрдпрдирд┐рдд рд╕рднреА рд╡рд╕реНрддреБрдУрдВ рдХреЛ рдЗрдирд╣реЗрд░рд┐рдЯ рдХрд┐рдпрд╛ рдЬрд╛рдПрдЧрд╛:
 class SelectableObject(CanvasObject): def __init__(self, **kwargs): super(SelectableObject, self).__init__(**kwargs) self.selectable = True def RenderSelection(self, gc): """ RenderHighlighting method should draw an object with a selection border around it. """ raise NotImplementedError() 

рд╡реИрд╕реЗ, рдЪреВрдВрдХрд┐ рд╣рдорд╛рд░реЗ рдЖрдзрд╛рд░ рд╡рд░реНрдЧ рдмрдбрд╝реЗ рдФрд░ рдЕрдзрд┐рдХ рдЬрдЯрд┐рд▓ рд╣реЛрддреЗ рдЬрд╛ рд░рд╣реЗ рд╣реИрдВ, рдЕрдм рдЙрдирдХреЗ рдбрд┐рдЬрд╝рд╛рдЗрдирд░ рдХрдИ рдорд╛рдкрджрдВрдбреЛрдВ рдХреЛ рдкрд╕рдВрдж рдХрд░рддреЗ рд╣реИрдВ рдФрд░ рдЙрдиреНрд╣реЗрдВ рдПрдХ рд╢рдмреНрджрдХреЛрд╢ рдХреЗ рд░реВрдк рдореЗрдВ рд╡рдВрд╢рд╛рдиреБрдХреНрд░рдо рдкрджрд╛рдиреБрдХреНрд░рдо рдкрд╛рд╕ рдХрд░рддреЗ рд╣реИрдВ, рдЬреЛ рдХрд┐ рд╕реНрдЯреНрд░рд┐рдВрдЧ рд╣реИ "рд╕реБрдкрд░ (рд╕реЗрд▓реЗрдмрд▓рдСрдмреНрдЬреЗрдХреНрдЯ, рд╕реНрд╡) ред__ init __ (* * * kwargs)ред "
рдпрд╣ рдХреЗрд╡рд▓ рдирд┐рдореНрди рдХреЛрдб рдХреЛ рдиреЛрдб рдореЗрдВ рдЬреЛрдбрд╝рдиреЗ рдХреЗ рд▓рд┐рдП рдмрдирд╛ рд╣реБрдЖ рд╣реИ:
  def RenderSelection(self, gc): gc.SetBrush(wx.Brush('#888888', wx.TRANSPARENT)) gc.SetPen(wx.Pen('#CC0000', 3, wx.DOT)) gc.DrawRectangle(self.position[0]-2, self.position[1]-2, self.boundingBoxDimensions[0]+4, self.boundingBoxDimensions[1]+4) 

рдЬреЛ рд╡рд╕реНрддреБ рдХреЗ рдЪрд╛рд░реЛрдВ рдУрд░ рдЪрдпрди рдХреЛ рдкреНрд░рд╕реНрддреБрдд рдХрд░рддрд╛ рд╣реИ рдФрд░ рдпрджрд┐ рдЖрдк рдХрд┐рд╕реА рдПрдХ рдиреЛрдб рдкрд░ рдХреНрд▓рд┐рдХ рдХрд░рддреЗ рд╣реИрдВ рддреЛ рдЖрдк рдЗрд╕ рддрд░рд╣ рдХрд╛ рдкрд░рд┐рдгрд╛рдо рджреЗрдЦ рд╕рдХрддреЗ рд╣реИрдВ:

рд╡рд░реНрддрдорд╛рди рдХреЛрдб, рд╣рдореЗрд╢рд╛ рдХреА рддрд░рд╣, GitHub рдкрд░ рд╕рдВрдмрдВрдзрд┐рдд рдкреНрд░рддрд┐рдмрджреНрдз рдореЗрдВ рдкрд╛рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ ред

8. рд╡рд╕реНрддреБрдУрдВ рдХреЛ рд╣рдЯрд╛рдирд╛


рддреЛ рд╣рдо рд╡рд╕реНрддреБрдУрдВ рдХреЛ рд╣рдЯрд╛рдиреЗ рдХреЗ рд▓рд┐рдП рдорд┐рд▓рд╛ред рд╣рдЯрд╛рдиреЗ рдХреЗ рд▓рд┐рдП, рд╣рдореЗрдВ рдЙрд╕ рд╕рдордп рдЯреНрд░реИрдХ рдХрд░рдирд╛ рд╣реЛрдЧрд╛ рдЬрдм рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдбрд┐рд▓реАрдЯ рдмрдЯрди рдкрд░ рдХреНрд▓рд┐рдХ рдХрд░рддрд╛ рд╣реИ рдФрд░ рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рд╣рдЯрд╛рдП рдЬрд╛рдиреЗ рдкрд░ рдЖрдЗрдЯрдо рдХреЛ рд╣рдЯрд╛ рджреЗрддрд╛ рд╣реИред
рд╕рдмрд╕реЗ рдкрд╣рд▓реЗ, рд╣рдореЗрдВ рдХреАрдмреЛрд░реНрдб рдмрдЯрди рдкрд░ рдХреНрд▓рд┐рдХ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдПрдХ рдИрд╡реЗрдВрдЯ рд╣реИрдВрдбрд▓рд░ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ, рдЬрд┐рд╕реЗ рд╣рдо рдирд┐рдореНрдирд╛рдиреБрд╕рд╛рд░ рдкрдВрдЬреАрдХреГрдд рдХрд░реЗрдВрдЧреЗ:
  self.Bind(wx.EVT_KEY_DOWN, self.OnKeyPress) 

рдФрд░ рд╣реИрдВрдбрд▓рд░ рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рдХреЛрдб рдирд┐рд╖реНрдкрд╛рджрд┐рдд рдХрд░реЗрдЧрд╛:
  def OnKeyPress(self, evt): if evt.GetKeyCode() == wx.WXK_DELETE: if self._selectedObject and self._selectedObject.deletable: self._selectedObject.Delete() if self._selectedObject in self._canvasObjects: self._canvasObjects.remove(self._selectedObject) self._selectedObject = None else: evt.Skip() #Update object under cursor pos = self.CalcUnscrolledPosition(evt.GetPosition()).Get() self._objectUnderCursor = self.FindObjectUnderPoint(pos) self.Render() 

рдпрд╣рд╛рдВ рд╣рдо, wx.WXK_DELETE рдХреЛрдб рдХреЗ рд╕рд╛рде рдПрдХ рдмрдЯрди рджрдмрд╛рдП рдЬрд╛рдиреЗ рдХреА рд╕реНрдерд┐рддрд┐ рдореЗрдВ, рдЬрд╛рдВрдЪ рд▓реЗрдВ рдХрд┐ рдХреНрдпрд╛ рд╡рд╕реНрддреБ рдХрд╛ рдЪрдпрди рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИ рдФрд░ рдХреНрдпрд╛ рдпрд╣ рд╣рдЯрд╛ рджрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИред рдпрджрд┐ рдХреЛрдИ рд╣реИ, рддреЛ рд╣рдо рдЙрд╕реЗ рдЦреВрдмрд╕реВрд░рддреА рд╕реЗ рд╕реВрдЪрд┐рдд рдХрд░рддреЗ рд╣реИрдВ рдХрд┐ рдпрд╣ рдЙрд╕рдХреЗ рд░рд┐рдЯрд╛рдпрд░ рд╣реЛрдиреЗ (рдбрд┐рд▓реАрдЯ рдХреЙрд▓ рдХрд░рдХреЗ) рдФрд░ рд╕реВрдЪреА рд╕реЗ рдХреИрдирд╡рд╛рд╕ рдкрд░ рд╡рд╕реНрддреБрдУрдВ рдХреЛ рд╣рдЯрд╛рдиреЗ рдХрд╛ рд╕рдордп рд╣реЛрдЧрд╛ред evt.Skip () рд╕рд┐рд╕реНрдЯрдо рдХреЛ рдЗрд╕ рдШрдЯрдирд╛ рдХреЗ рд▓рд┐рдП рдЕрдиреНрдп рд╣реИрдВрдбрд▓рд░ рдХреЛ рдХрд╣реАрдВ рдФрд░ рдкрдВрдЬреАрдХреГрдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХрд╣рддрд╛ рд╣реИред

рдЕрдм, рдкрд╣рд▓реЗ рд╕реЗ рд╣реА рдкрд╛рд░рдВрдкрд░рд┐рдХ рд░реВрдк рд╕реЗ, рдпрд╣ рд╣рдЯрд╛рдИ рдЧрдИ рд╡рд╕реНрддреБрдУрдВ рдХреЗ рд▓рд┐рдП рдПрдХ рдЖрдзрд╛рд░ рд╡рд░реНрдЧ рдЬреЛрдбрд╝рдиреЗ рдХреЗ рд▓рд┐рдП рдмрдирд╛ рд╣реБрдЖ рд╣реИ:
 class DeletableObject(CanvasObject): def __init__(self, **kwargs): super(DeletableObject, self).__init__(**kwargs) self.deletable = True def Delete(self): """ Delete method is called when an object is deleted from a canvas. """ raise NotImplementedError() 

рдФрд░ рдЖрдк рдХреИрдирд╡рд╛рд╕ рд╕реЗ рд╡рд╕реНрддреБрдУрдВ рдХреЛ рд╣рдЯрд╛рдиреЗ рдХрд╛ рдЖрдирдВрдж рд▓реЗ рд╕рдХрддреЗ рд╣реИрдВред рд╕рдЪреНрдЪрд╛рдИ рдмрд╣реБрдд рд▓рдВрдмреА рдирд╣реАрдВ рд╣реИ, рдХреНрдпреЛрдВрдХрд┐ рд╡рд╕реНрддреБрдУрдВ рдХреЛ рдЬреЛрдбрд╝рдирд╛ рдХреЗрд╡рд▓ рдЕрдЧрд▓реЗ рднрд╛рдЧ рдореЗрдВ рд╣реЛрдЧрд╛ :)
рдХреЛрдб рдХрд╛ рд╡рд░реНрддрдорд╛рди рд╕рдВрд╕реНрдХрд░рдг GitHub рдкрд░ рдЗрд╕ рдкреНрд░рддрд┐рдмрджреНрдз рдореЗрдВ рд░рд╣рддрд╛ рд╣реИ ред

рдкреБрдирд╢реНрдЪ: рдЯрд╛рдЗрдкреЛ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдПрдХ рд╡реНрдпрдХреНрддрд┐рдЧрдд рдореЗрдВ рд▓рд┐рдЦреЗрдВред

Source: https://habr.com/ru/post/In201608/


All Articles