probeScannerPalette.py (38948B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\inflight\scannerFiles\probeScannerPalette.py 2 import blue 3 import eve.client.script.ui.shared.pointerTool.pointerToolConst as pConst 4 import eve.common.lib.appConst as appConst 5 import evetypes 6 import functools 7 import gametime 8 import localization 9 import probescanning.formations as formations 10 import trinity 11 import uthread 12 import uthread2 13 import weakref 14 from carbon.client.script.environment.AudioUtil import PlaySound 15 from carbon.common.script.sys import service 16 from carbon.common.script.util.format import FmtDist, FmtAmt 17 from carbon.common.script.util.timerstuff import AutoTimer 18 from carbonui import TextColor, uiconst 19 from carbonui.button.const import HEIGHT_COMPACT 20 from carbonui.control.buttonIcon import ButtonIcon 21 from carbonui.control.scrollContainer import ScrollContainer 22 from carbonui.control.slider import Slider 23 from carbonui.primitives.container import Container 24 from carbonui.primitives.containerAutoSize import ContainerAutoSize 25 from carbonui.primitives.layoutGrid import LayoutGrid 26 from carbonui.primitives.sprite import Sprite 27 from carbonui.uianimations import animations 28 from carbonui.uicore import uicore 29 from carbonui.window.segment.underlay import WindowSegmentUnderlay 30 from eve.client.script.ui import eveColor 31 from eve.client.script.ui.control import eveLabel 32 from eve.client.script.ui.control.eveLabel import EveLabelMedium, Label 33 from eve.client.script.ui.control.tooltips import ShortcutHint 34 from eve.client.script.ui.inflight.scannerFiles.scanResults import ScanResults 35 from eve.client.script.ui.inflight.scannerFiles.scannerToolsUIComponents import FilterBox, IgnoredBox, ProbeTooltipButtonRow, ProbeTooltipCheckboxRow, FormationButton 36 from eve.client.script.ui.inflight.scannerFiles.scannerUtil import IsProbeScanPanelEmbedded 37 from eve.client.script.ui.inflight.scannerfiltereditor import ScannerFilterEditor 38 from eve.client.script.ui.util.uix import QtyPopup 39 from eve.common.script.sys.idCheckers import IsAbyssalSpaceSystem, IsVoidSpaceSystem 40 from eveexceptions import UserError 41 from fsdBuiltData.common.iconIDs import GetIconFile 42 from inventorycommon.const import groupSurveyProbe 43 from localization import GetByLabel 44 from player_messaging.client.ui_message import prompt_player_fsd_dialog 45 from probescanning.analyzeButton import AnalyzeButton, AnalyzeButtonController 46 from probescanning.const import probeStateIdle, probeStateInactive, probeLauncherTypeID 47 from probescanning.scanBonusController import ScanBonusController 48 from probescanning.tooltips.scanBonusTooltip import ScanBonusTooltip 49 from probescanning.tooltips.scanProbeTooltip import ScanProbeTooltip 50 CORE_SCANNER_PROBE_TYPE_ID = 30013 51 52 def AddFilter(*args): 53 filterOptions = sm.GetService('scanSvc').GetFilterOptions() 54 customFilters = [ (filterName, filterID) for filterName, filterID in filterOptions if filterID > 0 ] 55 if len(customFilters) >= 25: 56 raise UserError('TooManyFilters') 57 editor = ScannerFilterEditor.Open() 58 editor.LoadData(None) 59 60 61 def UserErrorIfScanning(action, *args, **kwargs): 62 63 @functools.wraps(action) 64 def wrapper(*args, **kwargs): 65 if sm.GetService('scanSvc').IsScanning(): 66 raise UserError('ScanInProgressGeneric') 67 return action(*args, **kwargs) 68 69 return wrapper 70 71 72 class ProbeScannerPalette(Container): 73 __notifyevents__ = ['OnProbeAdded', 74 'OnProbeRemoved', 75 'OnProbeRangeUpdated', 76 'OnSystemScanFilterChanged', 77 'OnSystemScanBegun', 78 'OnSystemScanDone', 79 'OnNewScannerFilterSet', 80 'OnProbeStateUpdated', 81 'OnProbePositionsUpdated', 82 'OnScannerDisconnected', 83 'OnRefreshScanResults', 84 'OnBallparkSetState', 85 'OnReconnectToProbesAvailable', 86 'OnModuleFitted', 87 'OnModuleOnlineChange', 88 'OnChargeLoadedToModule', 89 'OnProbeScannerUndocked', 90 'OnProbeScannerDocked', 91 'OnSessionChanged'] 92 __disallowanalysisgroups = [groupSurveyProbe] 93 filteredBoxTooltip = None 94 isBeingTransformed = False 95 solarSystemView = None 96 97 def ApplyAttributes(self, attributes): 98 super(ProbeScannerPalette, self).ApplyAttributes(attributes) 99 sm.RegisterNotify(self) 100 self.window = attributes.Get('window', None) 101 self.updateTimer = None 102 self._filter_toggle_queue = set() 103 self.scanSvc = sm.GetService('scanSvc') 104 self.scanBonusController = ScanBonusController() 105 self.isAnimatingProbeIcon = False 106 self.ConstructLayout() 107 self.UpdateControls() 108 109 def UpdateControls(self): 110 self.UpdateLabels() 111 self.LoadProbeIcon() 112 self.UpdateSizeSlider() 113 self.ActivateBonusTooltips() 114 self.CheckButtonStates() 115 uthread.new(self.LoadResultList) 116 117 def _IsCompactHeaderWindow(self): 118 return self.window and self.window.compact and not self.window.stacked and not self.window.collapsed 119 120 def Reconstruct(self): 121 self.Flush() 122 self.probeStateCont.Flush() 123 self.ConstructLayout() 124 self.UpdateControls() 125 126 def ConstructLayout(self): 127 self.ConstructFilterCont() 128 self.ConstructBottomCont() 129 self.ConstructProbeStateCont() 130 self.ConstructFooter() 131 self.ConstructScroll() 132 133 def ConstructBottomCont(self): 134 if self.window is None: 135 pad_left = pad_right = pad_bottom = 4 136 else: 137 pad_left, _, pad_right, pad_bottom = self.window.content_padding 138 self.bottomContainer = Container(name='BottomContainer', parent=self, align=uiconst.TOBOTTOM, height=64, padTop=pad_bottom) 139 WindowSegmentUnderlay(bgParent=self.bottomContainer, padding=(-pad_left, 140 -pad_bottom, 141 -pad_right, 142 -pad_bottom)) 143 144 def ConstructProbeStateCont(self): 145 self.probeStateCont = Container(name='probeStateCont', parent=self.bottomContainer, align=uiconst.TOTOP, height=24, padBottom=4) 146 self.probeInfoContainer = ContainerAutoSize(name='probeInfoContainer', parent=self.probeStateCont, align=uiconst.TOLEFT, state=uiconst.UI_NORMAL, padLeft=4) 147 iconSize = 28 148 self.probeSprite = Sprite(name='ProbeSprite', parent=self.probeInfoContainer, align=uiconst.CENTERLEFT, state=uiconst.UI_DISABLED, pos=(1, 149 0, 150 iconSize, 151 iconSize)) 152 self.probesLaunchedLabel = EveLabelMedium(name='ProbesLaunchedLabel', parent=self.probeInfoContainer, align=uiconst.CENTERLEFT, left=iconSize + 4) 153 self.recoverButton = ButtonIcon(name='RecoverProbesButton', parent=self.probeStateCont, iconSize=16, width=16, padLeft=4, hint=GetByLabel('UI/Inflight/Scanner/RecoverActiveProbes'), texturePath='res:/UI/Texture/Classes/ProbeScanner/16/recoverProbesIcon.png', func=self.RecoverActiveProbes, align=uiconst.TORIGHT, uniqueUiName=pConst.UNIQUE_NAME_RECOVER_PROBES) 154 self.reconnectButton = ButtonIcon(hint=localization.GetByLabel('UI/Inflight/Scanner/ReconnectActiveProbes'), func=self.ReconnectToLostProbes, texturePath='res:/UI/Texture/Classes/ProbeScanner/16/reconnectProbesIcon.png', parent=self.probeStateCont, iconSize=16, width=16, align=uiconst.TORIGHT, padLeft=4) 155 self.ConstructBonusIcons() 156 if session.role & service.ROLE_QA: 157 self.probeInfoContainer.GetMenu = self.QAGetMenu 158 159 def OnSessionChanged(self, isRemote, session, change): 160 self.LoadResultList() 161 162 def QAGetMenu(self): 163 return [('QA: Set Probe duration in seconds', self.QASetProbeDuration)] 164 165 def QASetProbeDuration(self): 166 qty = QtyPopup(7200, 30, 1) 167 self.scanSvc.QAOverrideProbeExpiry(qty['qty'] * 1000) 168 169 def LoadProbeIcon(self): 170 typeID = self.scanSvc.GetActiveProbeTypeID() 171 if not typeID: 172 self.probeSprite.opacity = 0.3 173 self.probeInfoContainer.tooltipPanelClassInfo = ScanProbeTooltip(typeID, self.GetProbeExpiryTimeLeft) 174 typeID = probeLauncherTypeID 175 else: 176 self.probeSprite.opacity = 1.0 177 self.probeInfoContainer.tooltipPanelClassInfo = ScanProbeTooltip(typeID, self.GetProbeExpiryTimeLeft) 178 iconID = evetypes.GetIconID(typeID) 179 texturePath = GetIconFile(iconID) 180 self.probeSprite.texturePath = texturePath 181 182 def ConstructBonusIcons(self): 183 self.bonusesContainer = ContainerAutoSize(name='bonusesContainer', parent=self.probeStateCont, align=uiconst.TORIGHT, padRight=8) 184 self.strengthBonusContainer = ContainerAutoSize(name='strengthBonusContainer', parent=self.bonusesContainer, align=uiconst.TOLEFT, state=uiconst.UI_NORMAL) 185 self.strengthIcon = Sprite(name='strengthIcon', parent=Container(parent=self.strengthBonusContainer, align=uiconst.TOLEFT, width=16), align=uiconst.CENTER, texturePath='res:/UI/Texture/Classes/ProbeScanner/16/Strength.png', width=16, height=16, state=uiconst.UI_DISABLED, color=eveColor.LED_GREY) 186 self.strengthLabel = Label(parent=ContainerAutoSize(parent=self.strengthBonusContainer, align=uiconst.TOLEFT), align=uiconst.CENTER, state=uiconst.UI_DISABLED, padLeft=3) 187 self.deviationBonusContainer = ContainerAutoSize(name='deviationBonusContainer', parent=self.bonusesContainer, align=uiconst.TOLEFT, state=uiconst.UI_NORMAL, padLeft=6) 188 self.deviationIcon = Sprite(name='deviationIcon', parent=Container(parent=self.deviationBonusContainer, align=uiconst.TOLEFT, width=16), align=uiconst.CENTER, texturePath='res:/UI/Texture/Classes/ProbeScanner/16/Deviation.png', width=16, height=16, state=uiconst.UI_DISABLED, color=eveColor.LED_GREY) 189 self.deviationLabel = Label(parent=ContainerAutoSize(parent=self.deviationBonusContainer, align=uiconst.TOLEFT), align=uiconst.CENTER, state=uiconst.UI_DISABLED, padLeft=3) 190 self.durationBonusContainer = ContainerAutoSize(name='durationContainer', parent=self.bonusesContainer, align=uiconst.TOLEFT, state=uiconst.UI_NORMAL, padLeft=6) 191 self.durationIcon = Sprite(name='durationIcon', parent=Container(parent=self.durationBonusContainer, align=uiconst.TOLEFT, width=16), align=uiconst.CENTER, texturePath='res:/UI/Texture/Classes/ProbeScanner/16/Time.png', width=16, height=16, state=uiconst.UI_DISABLED, color=eveColor.LED_GREY) 192 self.durationLabel = Label(parent=ContainerAutoSize(parent=self.durationBonusContainer, align=uiconst.TOLEFT), align=uiconst.CENTER, state=uiconst.UI_DISABLED, padLeft=3) 193 194 def ActivateBonusTooltips(self): 195 self.strengthBonusContainer.tooltipPanelClassInfo = ScanBonusTooltip(name='Strength', iconPath='res:/UI/Texture/Classes/ProbeScanner/16/Strength.png', totalValueFunc=self.scanBonusController.GetTotalScanStrength, baseValueFunc=self.scanBonusController.GetBaseScanStrength, skillBonusFunc=self.scanBonusController.GetScanStrengthBonusFromSkills, implantBonusFunc=self.scanBonusController.GetScanStrengthBonusFromImplants, shipBonusFunc=self.scanBonusController.GetScanStrengthBonusFromShip, moduleBonusFunc=self.scanBonusController.GetScanStrengthBonusFromModules, totalBonusFunc=self.scanBonusController.GetTotalScanStrengthBonus, boosterBonusFunc=self.scanBonusController.GetScanStrengthBonusFromBoosters) 196 self.deviationBonusContainer.tooltipPanelClassInfo = ScanBonusTooltip(name='Deviation', iconPath='res:/UI/Texture/Classes/ProbeScanner/16/Deviation.png', totalValueFunc=self.scanBonusController.GetTotalScanDeviation, baseValueFunc=self.scanBonusController.GetBaseScanDeviation, skillBonusFunc=self.scanBonusController.GetScanDeviationBonusFromSkills, implantBonusFunc=self.scanBonusController.GetScanDeviationBonusFromImplants, shipBonusFunc=self.scanBonusController.GetScanDeviationBonusFromShip, moduleBonusFunc=self.scanBonusController.GetScanDeviationBonusFromModules, totalBonusFunc=self.scanBonusController.GetTotalScanDeviationBonus, boosterBonusFunc=self.scanBonusController.GetScanDeviationBonusFromBoosters) 197 self.durationBonusContainer.tooltipPanelClassInfo = ScanBonusTooltip(name='Duration', iconPath='res:/UI/Texture/Classes/ProbeScanner/16/Time.png', totalValueFunc=self.scanBonusController.GetTotalScanDuration, baseValueFunc=self.scanBonusController.GetBaseScanDuration, skillBonusFunc=self.scanBonusController.GetScanDurationBonusFromSkills, implantBonusFunc=self.scanBonusController.GetScanDurationBonusFromImplants, shipBonusFunc=self.scanBonusController.GetScanDurationBonusFromShip, moduleBonusFunc=self.scanBonusController.GetScanDurationBonusFromModules, totalBonusFunc=self.scanBonusController.GetTotalScanDurationBonus, boosterBonusFunc=self.scanBonusController.GetScanDurationBonusFromBoosters) 198 199 def DeactivateBonusTooltips(self): 200 self.strengthBonusContainer.tooltipPanelClassInfo = None 201 self.deviationBonusContainer.tooltipPanelClassInfo = None 202 self.durationBonusContainer.tooltipPanelClassInfo = None 203 204 def ConstructFilterCont(self): 205 padding = (0, 4, 0, 0) if self._IsCompactHeaderWindow() else 0 206 self.filterCont = Container(name='filterCont', parent=self.window.header.extra_content if self._IsCompactHeaderWindow() else self, align=uiconst.TOTOP, height=HEIGHT_COMPACT, padding=padding) 207 if not self._IsCompactHeaderWindow(): 208 EveLabelMedium(name='ScanResultsLabel', parent=self.filterCont, text=GetByLabel('UI/Inflight/Scanner/ScanResults'), align=uiconst.CENTERLEFT, left=2) 209 self.ignoredBox = IgnoredBox(name='IgnoredBox', parent=self.filterCont, text=GetByLabel('UI/Inflight/Scanner/Ignored', noIgnored=0), state=uiconst.UI_NORMAL, align=uiconst.TORIGHT, padLeft=4) 210 self.ignoredBox.LoadTooltipPanel = self.LoadIgnoredTooltipPanel 211 self.filteredBox = FilterBox(name='FilteredBox', parent=self.filterCont, text=GetByLabel('UI/Inflight/Scanner/Filtered', noFiltered=0), state=uiconst.UI_NORMAL, align=uiconst.TORIGHT, uniqueUiName=pConst.UNIQUE_NAME_FILTER_SCAN_RESULTS, padLeft=4) 212 self.filteredBox.LoadTooltipPanel = self.LoadFilterTooltipPanel 213 self.mapButton = ButtonIcon(name='solarSystemMapButton', parent=self.filterCont, align=uiconst.TOLEFT if self._IsCompactHeaderWindow() else uiconst.TORIGHT, width=24, iconSize=16, texturePath='res:/UI/Texture/Shared/Brackets/solarSystem.png', hint=GetByLabel('UI/Map/MapPallet/btnSolarsystemMap'), func=self.MapBtnClicked) 214 self.mapButton.display = not IsProbeScanPanelEmbedded() 215 216 def MapBtnClicked(self, *args): 217 uicore.cmd.GetCommandAndExecute('CmdToggleSolarSystemMap') 218 219 def ConstructFooter(self): 220 self.formationButtonsContainer = ContainerAutoSize(name='FormationButtonsContainer', parent=self.bottomContainer, align=uiconst.TOLEFT, uniqueUiName=pConst.UNIQUE_NAME_PROBE_FORMATIONS) 221 self.ConstructFormationButtons() 222 buttonCont = ContainerAutoSize(name='AnalyzeButtonContainer', uniqueUiName=pConst.UNIQUE_NAME_PROBE_REFRESH, parent=self.bottomContainer, align=uiconst.TORIGHT, padLeft=8) 223 self.primaryButtonController = AnalyzeButtonController(self.scanSvc) 224 self.primaryButton = AnalyzeButton(name='PrimaryButton', parent=buttonCont, align=uiconst.CENTER, cmd=uicore.cmd.commandMap.GetCommandByName('CmdRefreshProbeScan'), fixedwidth=100, controller=self.primaryButtonController) 225 sliderCont = Container(name='sliderCont', parent=Container(parent=self.bottomContainer, padLeft=8), align=uiconst.TORIGHT_PROP, width=1.0, maxWidth=120) 226 self.sliderLabel = EveLabelMedium(name='sliderLabel', align=uiconst.TOTOP, parent=sliderCont) 227 self.sizeSlider = Slider(name='SizeSlider', parent=sliderCont, align=uiconst.TOTOP, minValue=1, maxValue=8, on_dragging=self.UpdateProbeSizeFromSliderInput, callback=self.UpdateProbeSizeFromSliderInput, increments=[ i for i in range(1, 9) ], barHeight=10, uniqueUiName=pConst.UNIQUE_NAME_PROBE_SIZE_MODIFIER, hint=localization.GetByLabel('UI/Inflight/Scanner/ProbeSize')) 228 229 def ConstructFormationButtons(self): 230 self.formationButtonsByID = {} 231 buttonSize = 32 232 self.pinpointFormationButton = ButtonIcon(parent=self.formationButtonsContainer, fixedheight=buttonSize, iconSize=buttonSize, iconPadding=0, hint=GetByLabel('UI/Inflight/Scanner/LaunchPinpointFormation'), texturePath='res:/UI/Texture/classes/ProbeScanner/32/pinpointFormation.png', func=lambda *args: self.LaunchFormation(formations.PINPOINT_FORMATION), align=uiconst.TOLEFT, soundClick='msg_newscan_probe_formation_play') 233 self.formationButtonsByID[formations.PINPOINT_FORMATION] = self.pinpointFormationButton 234 self.spreadFormationButton = ButtonIcon(parent=self.formationButtonsContainer, iconSize=buttonSize, hint=GetByLabel('UI/Inflight/Scanner/LaunchSpreadFormation'), texturePath='res:/UI/Texture/classes/ProbeScanner/32/spreadFormation.png', func=lambda *args: self.LaunchFormation(formations.SPREAD_FORMATION), align=uiconst.TOLEFT, padLeft=2, soundClick='msg_newscan_probe_formation_play') 235 self.formationButtonsByID[formations.SPREAD_FORMATION] = self.spreadFormationButton 236 self.customFormationButton = FormationButton(name='CustomFormationButton', parent=self.formationButtonsContainer, width=buttonSize, iconSize=buttonSize, align=uiconst.TOLEFT, padLeft=2, launchFunc=self.LaunchFormation) 237 self.formationButtonsByID[formations.CUSTOM_FORMATION] = self.customFormationButton 238 self.centerOnSelfFormationButton = ButtonIcon(parent=self.formationButtonsContainer, iconSize=buttonSize, hint=GetByLabel('UI/Inflight/Scanner/CenterOnSelf'), texturePath='res:/UI/Texture/classes/ProbeScanner/32/centerProbes.png', func=lambda *args: self.LaunchFormation(formations.SELF_CENTER_FORMATION), align=uiconst.TOLEFT, padLeft=2, soundClick='msg_newscan_probe_formation_play') 239 self.formationButtonsByID[formations.SELF_CENTER_FORMATION] = self.centerOnSelfFormationButton 240 241 def OnProbeAdded(self, probe): 242 self.UpdateLabels() 243 self.UpdateSizeSlider() 244 self.LoadProbeIcon() 245 246 def OnProbeScannerUndocked(self): 247 self.mapButton.Show() 248 249 def OnProbeScannerDocked(self): 250 self.mapButton.Hide() 251 252 def UpdateSizeSlider(self): 253 probeData = self.scanSvc.GetProbeData() 254 if len(probeData) < 1: 255 median = 0 256 else: 257 rangeSteps = [ data.rangeStep for probe, data in probeData.iteritems() ] 258 median = self.GetMedian(rangeSteps) 259 self.sizeSlider.SetValue(median) 260 261 def GetMedian(self, list): 262 if len(list) < 1: 263 return None 264 sortedList = sorted(list) 265 listLength = len(list) 266 index = (listLength - 1) // 2 267 return sortedList[index] 268 269 def OnProbeRemoved(self, probe): 270 self.UpdateLabels() 271 self.UpdateSizeSlider() 272 self.LoadProbeIcon() 273 274 def OnProbeRangeUpdated(self, probeID, scanRange): 275 self.UpdateSizeSliderLabel() 276 self.UpdateSizeSlider() 277 278 def GetProbeScanRangeText(self, probeData): 279 probeScanRangeText = self.GetAverageSizeInAU(probeData) 280 probeScanRangeText = '' if probeScanRangeText == 0 else probeScanRangeText 281 return probeScanRangeText 282 283 def GetAverageSizeInAU(self, probeData): 284 if len(probeData) <= 0: 285 return 0 286 sizes = [] 287 for probeID, data in probeData.iteritems(): 288 sizes.append(data.scanRange) 289 290 medianSize = self.GetMedian(sizes) 291 if medianSize < appConst.AU: 292 probeScanRangeText = FmtDist(medianSize, maxdemicals=2) 293 else: 294 probeScanRangeText = FmtDist(medianSize, maxdemicals=0) 295 return probeScanRangeText 296 297 def UpdateProbeSizeFromSliderInput(self, slider): 298 probeData = self.scanSvc.GetProbeData() 299 if not probeData: 300 return 301 newRange = int(slider.GetValue()) 302 if len(probeData) <= 0: 303 medianRange = 0 304 else: 305 rangeSteps = [ data.rangeStep for probe, data in probeData.iteritems() ] 306 medianRange = self.GetMedian(rangeSteps) 307 if newRange != medianRange: 308 scale = 1.0 309 if newRange < medianRange: 310 scale = 0.5 311 elif newRange > medianRange: 312 scale = 2.0 313 for x in range(abs(medianRange - newRange)): 314 self.scanSvc.ScaleFormation(scale) 315 316 self.UpdateSizeSliderLabel() 317 318 def UpdateSizeSliderLabel(self, *args): 319 self.sliderLabel.SetText(self.GetProbeScanRangeText(self.scanSvc.GetProbeData())) 320 321 def ConstructScroll(self): 322 self.scanResultsContainer = Container(name='ScanResultsContainer', parent=self, align=uiconst.TOALL, clipChildren=True, padTop=0 if self._IsCompactHeaderWindow() else 4) 323 self.resultScroll = ScanResults(name='resultScroll', parent=self.scanResultsContainer, id='probescannerwindow_resultScrollList', palette=self) 324 325 def Close(self, *args): 326 sm.UnregisterNotify(self) 327 super(ProbeScannerPalette, self).Close() 328 329 def GetSolarSystemView(self): 330 from eve.client.script.ui.shared.mapView.solarSystemViewPanel import SolarSystemViewPanel 331 solarSystemView = SolarSystemViewPanel.GetIfOpen() 332 if solarSystemView and not solarSystemView.destroyed: 333 return solarSystemView 334 335 def ToggleFilterShortcut(self, vkey): 336 localShortCuts = (uiconst.VK_1, 337 uiconst.VK_2, 338 uiconst.VK_3, 339 uiconst.VK_4, 340 uiconst.VK_5, 341 uiconst.VK_6) 342 if vkey not in localShortCuts: 343 return False 344 filterIndex = localShortCuts.index(vkey) 345 self.ToggleFilter(filterIndex) 346 return True 347 348 def _ToggleShowAnomalies(self): 349 toggleState = not settings.user.ui.Get('scannerShowAnomalies', True) 350 settings.user.ui.Set('scannerShowAnomalies', toggleState) 351 if toggleState: 352 self.scanSvc.ShowAnomalies() 353 else: 354 self.scanSvc.StopShowingAnomalies() 355 uthread.new(self.ReloadFilteredBoxTooltip) 356 357 def ToggleFilter(self, filterIndex): 358 if filterIndex in self._filter_toggle_queue: 359 self._filter_toggle_queue.remove(filterIndex) 360 else: 361 self._filter_toggle_queue.add(filterIndex) 362 self._UpdateFiltersThrottled() 363 364 def _ToggleFilter(self, filterIndex): 365 filterOptions = self.scanSvc.GetFilterOptions() 366 filterOptionIDs = [ filterID for filterName, filterID in filterOptions if filterID < 0 ] 367 filterID = filterOptionIDs[filterIndex - 1] 368 activeFilters = self.scanSvc.GetActiveFilterSet() 369 if filterID in activeFilters: 370 self.scanSvc.RemoveFromActiveFilterSet(filterID) 371 else: 372 self.scanSvc.AddToActiveFilterSet(filterID) 373 uthread.new(self.ReloadFilteredBoxTooltip) 374 375 @uthread2.debounce(wait=0.1, leading=True) 376 def _UpdateFiltersThrottled(self): 377 while self._filter_toggle_queue: 378 filter_index = self._filter_toggle_queue.pop() 379 if filter_index == 0: 380 self._ToggleShowAnomalies() 381 else: 382 self._ToggleFilter(filter_index) 383 384 def OnSystemScanFilterChanged(self, *args): 385 self.LoadResultList() 386 387 def OnSystemScanBegun(self): 388 self.Refresh() 389 self.resultScroll.on_system_scan_started() 390 391 def OnSystemScanDone(self): 392 self.Refresh() 393 self.resultScroll.on_system_scan_ended() 394 395 def GetProbesTooltipPointer(self): 396 return uiconst.POINT_TOP_3 397 398 def LoadHelpTooltip(self, tooltipPanel, *args): 399 tooltipPanel.columns = 2 400 tooltipPanel.margin = (12, 4, 4, 4) 401 tooltipPanel.cellPadding = (1, 1, 1, 1) 402 tooltipPanel.AddLabelMedium(text=GetByLabel('UI/Inflight/Scanner/HelpToModify'), wrapWidth=120) 403 tooltipPanel.AddLabelMedium(text=GetByLabel('UI/Inflight/Scanner/HelpHold'), align=uiconst.CENTERRIGHT) 404 tooltipPanel.AddLabelMedium(text=GetByLabel('UI/Inflight/Scanner/FormationProbeRange'), wrapWidth=120) 405 tooltipPanel.AddCell(ShortcutHint(text=trinity.app.GetKeyNameText(uiconst.VK_MENU), align=uiconst.TOPRIGHT), cellPadding=(6, 1, 1, 1)) 406 tooltipPanel.AddLabelMedium(text=GetByLabel('UI/Inflight/Scanner/FormationSpread'), wrapWidth=120) 407 tooltipPanel.AddCell(ShortcutHint(text=trinity.app.GetKeyNameText(uiconst.VK_CONTROL), align=uiconst.TOPRIGHT), cellPadding=(6, 1, 1, 1)) 408 tooltipPanel.AddLabelMedium(text=GetByLabel('UI/Inflight/Scanner/OneProbe'), wrapWidth=120) 409 tooltipPanel.AddCell(ShortcutHint(text=trinity.app.GetKeyNameText(uiconst.VK_SHIFT), align=uiconst.TOPRIGHT), cellPadding=(6, 1, 1, 1)) 410 411 def LoadFilterTooltipPanel(self, tooltipPanel, *args): 412 return self._LoadFilterTooltipPanel(tooltipPanel, *args) 413 414 def _LoadFilterTooltipPanel(self, tooltipPanel, *args): 415 tooltipPanel.Flush() 416 tooltipPanel.columns = 4 417 tooltipPanel.state = uiconst.UI_NORMAL 418 tooltipPanel.cellPadding = (5, 2, 5, 2) 419 tooltipPanel.margin = (0, 1, 0, 1) 420 header = eveLabel.EveLabelLarge(text=GetByLabel('UI/Inflight/Scanner/ShowResultsFor'), align=uiconst.CENTERLEFT, color=TextColor.HIGHLIGHT, padding=(0, 2, 0, 2)) 421 tooltipPanel.AddCell(header, colSpan=tooltipPanel.columns, cellPadding=(7, 3, 5, 3)) 422 tooltipPanel.AddRow(rowClass=ProbeTooltipCheckboxRow, text=GetByLabel('UI/Inflight/Scanner/AnomalySiteFilterLabel'), checked=settings.user.ui.Get('scannerShowAnomalies', True), func=self.OnShowAnomaliesCheckBoxChange, filterIndex=1) 423 filterOptions = self.scanSvc.GetFilterOptions() 424 activeFilters = self.scanSvc.GetActiveFilterSet() 425 standardFilters = [ (filterName, filterID) for filterName, filterID in filterOptions if filterID < 0 ] 426 for i, (filterName, filterID) in enumerate(standardFilters): 427 if filterID < 0: 428 tooltipPanel.AddRow(rowClass=ProbeTooltipCheckboxRow, text=filterName, checked=filterID in activeFilters, settingsKey=filterID, func=self.OnFilterCheckBoxChange, filterIndex=i + 2) 429 430 customFilters = [ (filterName, filterID) for filterName, filterID in filterOptions if filterID > 0 ] 431 if customFilters: 432 for filterName, filterID in customFilters: 433 if filterID > 0: 434 tooltipPanel.AddRow(rowClass=ProbeTooltipCheckboxRow, text=filterName, checked=filterID in activeFilters, settingsKey=filterID, func=self.OnFilterCheckBoxChange, deleteFunc=(self.DeleteFilter, (filterID,)), editFunc=(self.EditFilter, (filterID,))) 435 436 buttonRow = tooltipPanel.AddRow(rowClass=ProbeTooltipButtonRow, text=GetByLabel('UI/Inflight/Scanner/CreateNewFilter'), texturePath='res:/UI/Texture/Classes/ProbeScanner/saveformationProbesIcon.png', func=AddFilter) 437 self.filteredBoxTooltip = weakref.ref(tooltipPanel) 438 439 def ReloadFilteredBoxTooltip(self): 440 if not self.filteredBoxTooltip or self.destroyed: 441 return 442 filteredBoxTooltip = self.filteredBoxTooltip() 443 if filteredBoxTooltip is not None: 444 self._LoadFilterTooltipPanel(filteredBoxTooltip) 445 446 def LoadIgnoredTooltipPanel(self, tooltipPanel, *args): 447 tooltipPanel.Flush() 448 tooltipPanel.margin = 3 449 resultsIgnored = self.scanSvc.GetIgnoredResultsDesc() 450 if not resultsIgnored: 451 return 452 scroll = ScrollContainer(align=uiconst.TOPLEFT, parent=tooltipPanel) 453 scroll.isTabStop = False 454 subGrid = LayoutGrid(parent=scroll, align=uiconst.TOPLEFT, columns=4) 455 rowOrder = [] 456 if len(resultsIgnored) > 1: 457 row = subGrid.AddRow(rowClass=ProbeTooltipButtonRow, text=GetByLabel('UI/Inflight/Scanner/ClearAllIgnoredResults'), texturePath='res:/UI/Texture/Classes/ProbeScanner/clearIgnoredMenu.png', func=self.ClearIgnoredResults, width=200) 458 rowOrder.append(row) 459 ids = sorted(resultsIgnored) 460 for id, desc in ids: 461 if desc: 462 displayDesc = GetByLabel('UI/Inflight/Scanner/ResultIdAndDesc', id=id, desc=desc) 463 else: 464 displayDesc = id 465 row = subGrid.AddRow(rowClass=ProbeTooltipButtonRow, text=displayDesc, texturePath='res:/UI/Texture/Classes/ProbeScanner/clearIgnoredMenu.png', func=self.UnIgnoreResult, funcArgs=(id,), width=200) 466 rowOrder.append(row) 467 468 subGrid.RefreshGridLayout() 469 scroll.width = subGrid.width + (5 if len(rowOrder) > 10 else 0) 470 totalHeight = 0 471 for row in rowOrder[:10]: 472 totalHeight += row.height 473 474 scroll.height = totalHeight 475 tooltipPanel.state = uiconst.UI_NORMAL 476 self.ignoredTooltip = weakref.ref(tooltipPanel) 477 478 def ClearIgnoredResults(self, *args): 479 self.scanSvc.ClearIgnoredResults() 480 ignoredTooltip = self.ignoredTooltip() 481 if ignoredTooltip is not None: 482 self.LoadIgnoredTooltipPanel(ignoredTooltip) 483 484 def UnIgnoreResult(self, resultID, *args): 485 self.scanSvc.ShowIgnoredResult(resultID) 486 ignoredTooltip = self.ignoredTooltip() 487 if ignoredTooltip is not None: 488 self.LoadIgnoredTooltipPanel(ignoredTooltip) 489 490 def OnShowAnomaliesCheckBoxChange(self, settingsKey, settingState, *args): 491 settings.user.ui.Set('scannerShowAnomalies', settingState) 492 if settingState: 493 self.scanSvc.ShowAnomalies() 494 else: 495 self.scanSvc.StopShowingAnomalies() 496 self.LoadResultList() 497 498 def OnClick(self, *args): 499 uicore.registry.SetFocus(self.resultScroll) 500 501 def DeleteFilter(self, settingsKey): 502 response = prompt_player_fsd_dialog('DeleteFilter', {'filterName': self.scanSvc.resultFilter.GetFilterName(settingsKey)}, uiconst.YESNO, suppress=uiconst.ID_YES) 503 if response != uiconst.ID_YES: 504 return 505 self.scanSvc.DeleteFilter(settingsKey) 506 self.scanSvc.RemoveFromActiveFilterSet(settingsKey) 507 uthread.new(self.ReloadFilteredBoxTooltip) 508 509 def OnFilterCheckBoxChange(self, settingsKey, settingState, *args): 510 if settingState: 511 self.scanSvc.AddToActiveFilterSet(settingsKey) 512 else: 513 self.scanSvc.RemoveFromActiveFilterSet(settingsKey) 514 515 def ValidateProbesState(self, probeIDs, isEntryButton = False): 516 probeData = self.scanSvc.GetProbeData() 517 for probeID in probeIDs: 518 if probeID in probeData: 519 probe = probeData[probeID] 520 if isEntryButton: 521 if probe.state not in (probeStateIdle, probeStateInactive): 522 return False 523 elif probe.state != probeStateIdle: 524 return False 525 526 return True 527 528 def LoadCurrentSolarSystem(self): 529 self.LoadResultList() 530 531 def OnNewScannerFilterSet(self, *args): 532 self.LoadResultList() 533 534 def OnProbePositionsUpdated(self): 535 self.CheckButtonStates() 536 537 def OnScannerDisconnected(self): 538 self.LoadResultList() 539 self.CheckButtonStates() 540 self.UpdateLabels() 541 542 def LaunchFormation(self, formationID): 543 if formationID == formations.SELF_CENTER_FORMATION: 544 self.scanSvc.CenterProbesOnMe(formationID) 545 else: 546 self.scanSvc.MoveProbesToFormation(formationID) 547 self.UpdateSizeSlider() 548 self.UpdateLabels() 549 550 def EditFilter(self, filterID, *args): 551 editor = ScannerFilterEditor.Open() 552 editor.LoadData(filterID) 553 554 def LoadResultList(self): 555 if self.destroyed: 556 return 557 self.resultScroll.load_results() 558 559 def CheckButtonStates(self): 560 if self.destroyed: 561 return 562 canClaim = self.scanSvc.CanClaimProbes() 563 if canClaim: 564 self.recoverButton.Enable() 565 else: 566 self.recoverButton.Disable() 567 probes = self.scanSvc.GetActiveProbes() 568 allIdle = self.ValidateProbesState(probes) 569 if probes and allIdle: 570 self.recoverButton.Enable() 571 self.sizeSlider.Show() 572 else: 573 self.recoverButton.Disable() 574 self.sizeSlider.Hide() 575 for formationID, button in self.formationButtonsByID.iteritems(): 576 try: 577 canLaunch = self.scanSvc.CanLaunchFormation(formationID) 578 except KeyError: 579 if allIdle: 580 button.Enable() 581 else: 582 button.Disable() 583 continue 584 585 if canLaunch and allIdle: 586 button.Enable() 587 else: 588 button.Disable() 589 590 self.UpdateProbeInfo() 591 self.buttonRefreshTimer = AutoTimer(interval=500, method=self.CheckButtonStates) 592 593 def GetProbeHandler(self): 594 solarSystemView = self.GetSolarSystemView() 595 if solarSystemView and solarSystemView.mapView: 596 solarSystemHandler = solarSystemView.mapView.currentSolarsystem 597 if solarSystemHandler and solarSystemHandler.solarsystemID == session.solarsystemid2: 598 return solarSystemHandler.probeHandler 599 600 def RefreshProbeResults(self): 601 probeHandler = self.GetProbeHandler() 602 if probeHandler: 603 probeHandler.SetResultFilter([], update=True) 604 605 @UserErrorIfScanning 606 def ReconnectToLostProbes(self, *args): 607 self.scanSvc.ReconnectToLostProbes() 608 609 @UserErrorIfScanning 610 def RecoverActiveProbes(self, *args): 611 probes = self.scanSvc.GetActiveProbes() 612 self.scanSvc.RecoverProbes(probes) 613 self.StopAnimatingProbeIcon() 614 615 @UserErrorIfScanning 616 def Analyze(self, *args): 617 if IsAbyssalSpaceSystem(session.solarsystemid2): 618 key = 'ProbeScannerAbyssalSpaceWarning' 619 response = prompt_player_fsd_dialog(key, buttons=uiconst.YESNO, suppress=uiconst.ID_YES) 620 if response == uiconst.ID_NO: 621 self.CheckButtonStates() 622 return 623 elif IsVoidSpaceSystem(session.solarsystemid2): 624 raise UserError('ProbeScannerVoidSpaceHint') 625 try: 626 self.scanSvc.RequestScans() 627 except UserError as e: 628 self.CheckButtonStates() 629 raise e 630 finally: 631 self.LoadResultList() 632 633 def Confirm(self, *args): 634 uthread.new(self.primaryButtonController.click) 635 636 def Refresh(self): 637 self.refreshTimer = AutoTimer(interval=200, method=self.DoRefresh) 638 639 def DoRefresh(self): 640 self.refreshTimer = None 641 if self.destroyed: 642 return 643 self.CheckButtonStates() 644 self.LoadResultList() 645 self.UpdateLabels() 646 647 def UpdateLabels(self): 648 self.UpdateSizeSliderLabel() 649 self.UpdateProbeInfo() 650 651 def UpdateProbeInfoText(self): 652 probeData = self.scanSvc.GetProbeData() 653 numProbes = len(probeData) 654 if numProbes: 655 text = 'X %s' % numProbes 656 elif not self.scanSvc.HasOnlineProbeLauncher(): 657 text = GetByLabel('UI/Inflight/Scanner/NoLauncherFitted') 658 elif self.scanSvc.GetChargesInProbeLauncher(): 659 text = GetByLabel('UI/Inflight/Scanner/NoProbesLaunched') 660 else: 661 text = GetByLabel('UI/Inflight/Scanner/NoProbesInLauncher') 662 self.probesLaunchedLabel.SetText(text) 663 color = Label.default_color if numProbes else eveColor.WARNING_ORANGE 664 self.probesLaunchedLabel.SetRGBA(*color) 665 666 def UpdateBonusLabelsText(self): 667 if self.scanSvc.GetProbeData(): 668 self.strengthLabel.SetText(self.FormatBonusAmount(self.scanBonusController.GetTotalScanStrength(), fractions=1)) 669 self.deviationLabel.SetText(self.FormatBonusAmount(self.scanBonusController.GetTotalScanDeviation(), fractions=3, suffix=' AU')) 670 self.durationLabel.SetText(self.FormatBonusAmount(self.scanBonusController.GetTotalScanDuration(), fractions=0, suffix='s')) 671 672 def UpdateBonusLabelVisibility(self): 673 if self.scanSvc.GetProbeData(): 674 self.bonusesContainer.Show() 675 self.strengthLabel.display = self.deviationLabel.display = self.durationLabel.display = False 676 else: 677 self.bonusesContainer.Hide() 678 679 def UpdateReconnectButtonVisibility(self): 680 if self.scanSvc.GetProbeData(): 681 self.reconnectButton.Hide() 682 elif self.scanSvc.HasOnlineProbeLauncher(): 683 self.reconnectButton.Show() 684 else: 685 self.reconnectButton.Hide() 686 687 def FormatBonusAmount(self, amount, fractions, suffix = ''): 688 if amount == 0: 689 return '-' 690 else: 691 return FmtAmt(amount, showFraction=fractions) + suffix 692 693 def GetProbeExpiryTimeLeft(self): 694 probeData = self.scanSvc.GetProbeData() 695 if len(probeData) <= 0: 696 return None 697 else: 698 timeLeft = 0 699 for probeID, data in probeData.iteritems(): 700 timeLeft = long(gametime.GetSecondsUntilSimTime(data.expiry)) * gametime.SEC 701 break 702 703 if timeLeft <= 0: 704 self.StopAnimatingProbeIcon() 705 return timeLeft 706 if timeLeft >= gametime.HOUR: 707 self.StopAnimatingProbeIcon() 708 return localization.formatters.FormatTimeIntervalShortWritten(timeLeft, showFrom='day', showTo='minute') 709 if timeLeft <= gametime.MIN * 5: 710 self.StartBlinkingProbeIcon() 711 return localization.formatters.FormatTimeIntervalShortWritten(timeLeft, showFrom='day', showTo='second') 712 713 def StartBlinkingProbeIcon(self): 714 if not self.isAnimatingProbeIcon: 715 PlaySound('msg_newscan_probe_expire_play') 716 animations.BlinkIn(self.probeSprite, duration=1, loops=uiconst.ANIM_REPEAT, startVal=0.1, curveType=uiconst.ANIM_BOUNCE) 717 self.isAnimatingProbeIcon = True 718 719 def StopAnimatingProbeIcon(self): 720 if self.isAnimatingProbeIcon: 721 animations.StopAllAnimations(self.probeSprite) 722 self.isAnimatingProbeIcon = False 723 724 def OnRefreshScanResults(self): 725 self.Refresh() 726 727 def OnBallparkSetState(self): 728 self.Refresh() 729 730 def ShowFilteredAndIgnored(self, filtered, ignored, filteredAnomalies): 731 self.filteredBox.SetText(GetByLabel('UI/Inflight/Scanner/Filtered', noFiltered=filtered + filteredAnomalies)) 732 self.ignoredBox.UpdateIgnoredAmount(ignored) 733 734 def OnReconnectToProbesAvailable(self): 735 self.CheckButtonStates() 736 self.UpdateProbeInfo() 737 738 def OnModuleFitted(self, charId, flag, typeID, groupID): 739 self.UpdateProbeInfo() 740 741 def OnModuleOnlineChange(self, item, oldValue, newValue): 742 self.UpdateProbeInfo() 743 744 def UpdateProbeInfo(self): 745 if not self.destroyed: 746 self.UpdateBonusLabelVisibility() 747 self.UpdateBonusLabelsText() 748 self.UpdateReconnectButtonVisibility() 749 self.UpdateProbeInfoText() 750 self.GetProbeExpiryTimeLeft() 751 752 def OnChargeLoadedToModule(self): 753 blue.synchro.Sleep(1000) 754 self.UpdateProbeInfo() 755 self.LoadProbeIcon()