def __init__(self, *args, **kwargs) -> None: super().__init__(*args, **kwargs) self._id_to_window = {} # type: typing.Dict[int, wx.Frame] menu_item = self.Append(wx.ID_ANY, item='Reset window positions') self.Bind(wx.EVT_MENU, self.OnResetWindowPositions, menu_item) # A separator between the window menu items and the other # extra windows. self.AppendSeparator() # Add item to launch valueLogViewer (XXX: this should be # handled by some sort of plugin system and not hardcoded). from cockpit.util import valueLogger from cockpit.util import csv_plotter menu_item = self.Append(wx.ID_ANY, "Launch ValueLogViewer") logs = valueLogger.ValueLogger.getLogFiles() if not logs: menu_item.Enable(False) else: shell = sys.platform == 'win32' args = ['python', csv_plotter.__file__] + logs self.Bind(wx.EVT_MENU, lambda e: subprocess.Popen(args, shell=shell), menu_item) # This is only for the piDIO and executor, both of which are a # window to set lines high/low. We should probably have a # general window for this which we could use for all executor # handlers (probably piDIO device should provide an executor # handler). for obj in chain(depot.getAllHandlers(), depot.getAllDevices()): if hasattr(obj, 'showDebugWindow'): label = 'debug %s (%s)' % (obj.name, obj.__class__.__name__) menu_item = self.Append(wx.ID_ANY, label) self.Bind(wx.EVT_MENU, lambda e, obj=obj: obj.showDebugWindow(), menu_item) # When the menu is created the windows don't exist yet so we # will update it each time the menu is open. self.Bind(wx.EVT_MENU_OPEN, self.OnMenuOpen)
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Find out what devices we have to work with. lightToggles = depot.getHandlersOfType(depot.LIGHT_TOGGLE) ## Maps LightSource handlers to their associated panels of controls. self.lightToPanel = dict() # Construct the UI. # Sizer for all controls. We'll split them into bottom half (light # sources) and top half (everything else). self.Sizer = wx.BoxSizer(wx.VERTICAL) # Panel for holding the non-lightsource controls. topPanel = wx.Panel(self) self.topPanel = topPanel topSizer = wx.BoxSizer(wx.VERTICAL) # A row of buttons for various actions we know we can take. buttonSizer = wx.BoxSizer(wx.HORIZONTAL) # Abort button abortButton = wx.Button(topPanel, wx.ID_ANY, "abort") abortButton.SetLabelMarkup( "<span foreground='red'><big><b>ABORT</b></big></span>") abortButton.Bind(wx.EVT_BUTTON, lambda event: events.publish(events.USER_ABORT)) buttonSizer.Add(abortButton, 1, wx.EXPAND) # Experiment & review buttons for lbl, fn in (("Single-site\nexperiment", lambda evt: singleSiteExperiment.showDialog(self)), ("Multi-site\nexperiment", lambda evt: multiSiteExperiment.showDialog(self)), ("View last\nfile", self.onViewLastFile)): btn = wx.Button(topPanel, wx.ID_ANY, lbl) btn.Bind(wx.EVT_BUTTON, fn) buttonSizer.Add(btn, 1, wx.EXPAND) # Video mode button videoButton = wx.ToggleButton(topPanel, wx.ID_ANY, "Live") videoButton.Bind(wx.EVT_TOGGLEBUTTON, lambda evt: cockpit.interfaces.imager.videoMode()) events.subscribe(cockpit.events.VIDEO_MODE_TOGGLE, lambda state: videoButton.SetValue(state)) buttonSizer.Add(videoButton, 1, wx.EXPAND) # Snap image button snapButton = wx.Button(topPanel, wx.ID_ANY, "Snap\nimage") snapButton.Bind( wx.EVT_BUTTON, lambda evt: cockpit.interfaces.imager.imager.takeImage()) buttonSizer.Add(snapButton, 1, wx.EXPAND) # Increase font size in top row buttons. for w in [child.GetWindow() for child in buttonSizer.Children]: w.SetFont(w.GetFont().Larger()) topSizer.Add(buttonSizer) topSizer.AddSpacer(ROW_SPACER) # Make UIs for any other handlers / devices and insert them into # our window, if possible. # Light power things will be handled later. lightPowerThings = depot.getHandlersOfType(depot.LIGHT_POWER) lightPowerThings.sort(key=lambda l: l.wavelength) # Camera UIs are drawn seperately. Currently, they are drawn first, # but this separation may make it easier to implement cameras in # ordered slots, giving the user control over exposure order. cameraThings = depot.getHandlersOfType(depot.CAMERA) # Ignore anything that is handled specially. ignoreThings = lightToggles + lightPowerThings ignoreThings += cameraThings # Remove ignoreThings from the full list of devices. otherThings = list(depot.getAllDevices()) otherThings.sort(key=lambda d: d.__class__.__name__) otherThings.extend(depot.getAllHandlers()) rowSizer = wx.WrapSizer(wx.HORIZONTAL) # Add objective control # If only one objective device (usual), add to end of top row, # otherwise add to start of 2nd row. hs = depot.getHandlersOfType(depot.OBJECTIVE) if len(hs) == 1: buttonSizer.Add(mainPanels.ObjectiveControls(self.topPanel), flag=wx.LEFT, border=2) else: rowSizer.Add(mainPanels.ObjectiveControls(self.topPanel), flag=wx.EXPAND) rowSizer.AddSpacer(COL_SPACER) ignoreThings.extend(hs) # Make the UI elements for the cameras. rowSizer.Add(mainPanels.CameraControlsPanel(self.topPanel), flag=wx.EXPAND) rowSizer.AddSpacer(COL_SPACER) # Add light controls. lightfilters = sorted(depot.getHandlersOfType(depot.LIGHT_FILTER)) ignoreThings.extend(lightfilters) # Add filterwheel controls. rowSizer.Add(mainPanels.FilterControls(self.topPanel), flag=wx.EXPAND) # Make the UI elements for eveything else. for thing in ignoreThings: if thing in otherThings: otherThings.remove(thing) for thing in sorted(otherThings): if depot.getHandler(thing, depot.CAMERA): # Camera UIs already drawn. continue item = thing.makeUI(topPanel) if item is not None: itemsizer = wx.BoxSizer(wx.VERTICAL) itemsizer.Add( cockpit.gui.mainPanels.PanelLabel(topPanel, thing.name)) itemsizer.Add(item, 1, wx.EXPAND) if rowSizer.GetChildren(): # Add a spacer. rowSizer.AddSpacer(COL_SPACER) rowSizer.Add(itemsizer, flag=wx.EXPAND) topSizer.Add(rowSizer) topPanel.SetSizerAndFit(topSizer) self.Sizer.Add(topPanel, flag=wx.EXPAND) self.Sizer.AddSpacer(ROW_SPACER) ## Panel for holding light sources. self.Sizer.Add(mainPanels.LightControlsPanel(self), flag=wx.EXPAND) keyboard.setKeyboardHandlers(self) self.joystick = joystick.Joystick(self) self.SetDropTarget(viewFileDropTarget.ViewFileDropTarget(self)) # Show the list of windows on right-click. self.Bind(wx.EVT_CONTEXT_MENU, lambda event: keyboard.martialWindows(self))
def martialWindows(parent): menu = wx.Menu() menu_item = menu.Append(wx.ID_ANY, "Reset window positions") parent.Bind(wx.EVT_MENU, lambda e: wx.GetApp().SetWindowPositions(), menu_item) main_window = wx.GetApp().GetTopWindow() for window in wx.GetTopLevelWindows(): if window is main_window: # Don't show this for the main window. continue if not window.GetTitle(): # Sometimes we get bogus top-level windows; no idea why. # Just skip them. # \todo Figure out where these windows come from and either get # rid of them or fix them so they don't cause trouble here. continue subMenu = wx.Menu() def show_or_hide(evt, w=window): # The window might be hidden but maybe it's just iconized # (minimized), or maybe it's both. If it's iconized we # need to restore it first if w.IsIconized(): w.Restore() w.Show() else: w.Show(not w.IsShown()) def raise_window(evt, w=window): # At least on Mac we need to call Show before Raise in # case the window is hidden (see issue #599). It is not # yet clear what is wx expected behaviour. See upstream # issue https://trac.wxwidgets.org/ticket/18762 w.Show() w.Raise() menu_item = subMenu.Append(wx.ID_ANY, "Show/Hide") parent.Bind(wx.EVT_MENU, show_or_hide, menu_item) menu_item = subMenu.Append(wx.ID_ANY, "Raise to top") parent.Bind(wx.EVT_MENU, raise_window, menu_item) menu_item = subMenu.Append(wx.ID_ANY, "Move to mouse") parent.Bind(wx.EVT_MENU, lambda e, w=window: w.SetPosition(wx.GetMousePosition()), menu_item) menu.AppendSubMenu(subMenu, window.GetTitle()) # Add item to launch valueLogViewer. from subprocess import Popen from sys import platform from cockpit.util import valueLogger from cockpit.util import csv_plotter menu_item = menu.Append(wx.ID_ANY, "Launch ValueLogViewer") logs = valueLogger.ValueLogger.getLogFiles() if not logs: menu_item.Enable(False) else: shell = platform == 'win32' args = ['python', csv_plotter.__file__] + logs parent.Bind(wx.EVT_MENU, lambda e: Popen(args, shell=shell), menu_item) for d in filter(lambda x: hasattr(x, "showDebugWindow"), chain(depot.getAllHandlers(), depot.getAllDevices())): menu_item = menu.Append( wx.ID_ANY, 'debug %s %s' % (d.__class__.__name__, d.name)) parent.Bind(wx.EVT_MENU, lambda e, d=d: d.showDebugWindow(), menu_item) cockpit.gui.guiUtils.placeMenuAtMouse(parent, menu)
def martialWindows(parent): primaryWindows = wx.GetApp().primaryWindows secondaryWindows = wx.GetApp().secondaryWindows otherWindows = [w for w in wx.GetTopLevelWindows() if w not in (primaryWindows + secondaryWindows)] # windows = wx.GetTopLevelWindows() menu = wx.Menu() menuId = 1000 menu.Append(menuId, "Reset window positions") parent.Bind(wx.EVT_MENU, lambda e: wx.GetApp().SetWindowPositions(), id= menuId) menuId += 1 #for i, window in enumerate(windows): for i, window in enumerate(primaryWindows): if not window.GetTitle(): # Sometimes we get bogus top-level windows; no idea why. # Just skip them. # \todo Figure out where these windows come from and either get # rid of them or fix them so they don't cause trouble here. continue subMenu = wx.Menu() subMenu.Append(menuId, "Raise to top") parent.Bind(wx.EVT_MENU, lambda e, window = window: window.Raise(),id=menuId) menuId += 1 subMenu.Append(menuId, "Move to mouse") parent.Bind(wx.EVT_MENU, lambda e, window = window: window.SetPosition(wx.GetMousePosition()), id=menuId) menuId += 1 subMenu.Append(menuId, "Move to top-left corner") parent.Bind(wx.EVT_MENU, lambda e, window = window: window.SetPosition((0, 0)), id=menuId) menuId += 1 # Some windows have very long titles (e.g. the Macro Stage View), # so just take the first 50 characters. if version.LooseVersion(wx.__version__) < version.LooseVersion('4'): menu.AppendMenu(menuId, str(window.GetTitle())[:50], subMenu) else: menu.AppendSubMenu(subMenu, str(window.GetTitle())[:50]) menuId += 1 menu.AppendSeparator() for i, window in enumerate(secondaryWindows): if not window.GetTitle(): # Sometimes we get bogus top-level windows; no idea why. # Just skip them. # \todo Figure out where these windows come from and either get # rid of them or fix them so they don't cause trouble here. continue subMenu = wx.Menu() subMenu.Append(menuId, "Show/Hide") parent.Bind(wx.EVT_MENU, lambda e, window = window: ((window.Restore() and (cockpit.util.userConfig.setValue('windowState'+window.GetTitle(), 1))) if window.IsIconized() else ((window.Show(not window.IsShown()) ) and (cockpit.util.userConfig.setValue('windowState'+window.GetTitle(),0)))), id=menuId) menuId += 1 subMenu.Append(menuId, "Move to mouse") parent.Bind(wx.EVT_MENU, lambda e, window = window: window.SetPosition(wx.GetMousePosition()), id=menuId) menuId += 1 # Some windows have very long titles (e.g. the Macro Stage View), # so just take the first 50 characters. if version.LooseVersion(wx.__version__) < version.LooseVersion('4'): menu.AppendMenu(menuId, str(window.GetTitle())[:50], subMenu) else: menu.AppendSubMenu(subMenu, str(window.GetTitle())[:50]) menuId += 1 # Add item to launch valueLogViewer. from subprocess import Popen from sys import platform from cockpit.util import valueLogger from cockpit.util import csv_plotter menu.Append(menuId, "Launch ValueLogViewer") logs = valueLogger.ValueLogger.getLogFiles() if not logs: menu.Enable(menuId, False) else: shell = platform == 'win32' args = ['python', csv_plotter.__file__] + logs parent.Bind(wx.EVT_MENU, lambda e: Popen(args, shell=shell), id = menuId) menuId += 1 menu.AppendSeparator() for i, window in enumerate(otherWindows): if not window.GetTitle() or not window.IsShown(): # Sometimes we get bogus top-level windows; no idea why. # Just skip them. # Also, some windows are hidden rather than destroyed # (e.g. the experiment setup window). Skip those, too. # \todo Figure out where these windows come from and either get # rid of them or fix them so they don't cause trouble here. continue subMenu = wx.Menu() subMenu.Append(menuId, "Raise to top") parent.Bind(wx.EVT_MENU, lambda e, window = window: window.Raise(), id=menuId) menuId += 1 subMenu.Append(menuId, "Move to mouse") parent.Bind(wx.EVT_MENU, lambda e, window = window: window.SetPosition(wx.GetMousePosition()), id=menuId) menuId += 1 subMenu.Append(menuId, "Move to top-left corner") parent.Bind(wx.EVT_MENU, lambda e, window = window: window.SetPosition((0, 0)) , id=menuId) menuId += 1 # Some windows have very long titles (e.g. the Macro Stage View), # so just take the first 50 characters. try: menu.Append(menuId, str(window.GetTitle())[:50], subMenu) except TypeError as e: # Sometimes, windows created late (e.g. wx InspectionTool) cause # a weird error here: menu.Append throws a type error, insisting # it needs a String or Unicode type, despite being passed a String # or Unicode type. print ("Omitting %s from window - weird wx string/unicode type error." % window.GetTitle()) menuId += 1 for d in filter(lambda x: hasattr(x, "showDebugWindow"), chain(depot.getAllHandlers(), depot.getAllDevices())): menu.Append(menuId, 'debug %s %s' % (d.__class__.__name__, d.name )) parent.Bind(wx.EVT_MENU, lambda e, d=d: d.showDebugWindow(), id=menuId) menuId += 1 cockpit.gui.guiUtils.placeMenuAtMouse(parent, menu)