561 lines
20 KiB
Python
561 lines
20 KiB
Python
# interface.py
|
|
import wx
|
|
import wx.dataview as dv
|
|
import pcbnew
|
|
from .backend import manager
|
|
from .backend.thumbnails import ThumbnailGenerator
|
|
|
|
class LogDialog(wx.Dialog):
|
|
def __init__(self, parent, title, log_text):
|
|
super().__init__(parent, title=title, size=(600, 400), style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER)
|
|
sizer = wx.BoxSizer(wx.VERTICAL)
|
|
text = wx.TextCtrl(self, style=wx.TE_MULTILINE | wx.TE_READONLY | wx.HSCROLL)
|
|
text.SetValue(log_text)
|
|
sizer.Add(text, 1, wx.EXPAND | wx.ALL, 10)
|
|
btn = wx.Button(self, wx.ID_OK, "Close")
|
|
sizer.Add(btn, 0, wx.ALIGN_CENTER | wx.BOTTOM, 10)
|
|
self.SetSizer(sizer)
|
|
btn.Bind(wx.EVT_BUTTON, lambda e: self.EndModal(wx.ID_OK))
|
|
|
|
class DeleteDialog(wx.Dialog):
|
|
def __init__(self, parent):
|
|
super().__init__(parent, title="Delete Layer", size=(400, 200))
|
|
sizer = wx.BoxSizer(wx.VERTICAL)
|
|
lbl = wx.StaticText(self, label="How should items associated with this layer be handled?")
|
|
sizer.Add(lbl, 0, wx.ALL, 15)
|
|
self.cb_remember = wx.CheckBox(self, label="Remember my choice (Don't ask again)")
|
|
sizer.Add(self.cb_remember, 0, wx.LEFT | wx.RIGHT | wx.BOTTOM, 15)
|
|
btn_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
|
btn_del_all = wx.Button(self, id=wx.ID_YES, label="Delete Layer & Items")
|
|
btn_del_layer = wx.Button(self, id=wx.ID_NO, label="Delete Layer Only")
|
|
btn_cancel = wx.Button(self, id=wx.ID_CANCEL, label="Cancel")
|
|
btn_sizer.Add(btn_del_all, 0, wx.ALL, 5)
|
|
btn_sizer.Add(btn_del_layer, 0, wx.ALL, 5)
|
|
btn_sizer.Add(btn_cancel, 0, wx.ALL, 5)
|
|
sizer.Add(btn_sizer, 0, wx.ALIGN_CENTER | wx.ALL, 10)
|
|
self.SetSizer(sizer)
|
|
self.Fit()
|
|
btn_del_all.Bind(wx.EVT_BUTTON, self.OnButton)
|
|
btn_del_layer.Bind(wx.EVT_BUTTON, self.OnButton)
|
|
btn_cancel.Bind(wx.EVT_BUTTON, self.OnButton)
|
|
|
|
def OnButton(self, event):
|
|
self.EndModal(event.GetId())
|
|
|
|
class LayerDropTarget(wx.TextDropTarget):
|
|
def __init__(self, panel):
|
|
super().__init__()
|
|
self.panel = panel
|
|
|
|
def OnDragOver(self, x, y, defResult):
|
|
# Provide visual feedback by selecting the row under the cursor
|
|
item, col = self.panel.list.HitTest(wx.Point(x, y))
|
|
if item.IsOk():
|
|
row = self.panel.list.ItemToRow(item)
|
|
self.panel.list.SelectRow(row)
|
|
return defResult
|
|
|
|
def OnDropText(self, x, y, data):
|
|
try:
|
|
source_idx = int(data)
|
|
item, col = self.panel.list.HitTest(wx.Point(x, y))
|
|
|
|
if not item.IsOk():
|
|
target_idx = self.panel.list.GetItemCount()
|
|
else:
|
|
target_idx = self.panel.list.ItemToRow(item)
|
|
|
|
if source_idx != target_idx:
|
|
# Capture active layer object to restore selection index later
|
|
active_layer = None
|
|
if manager.active_layer_index is not None and 0 <= manager.active_layer_index < len(manager.layers):
|
|
active_layer = manager.layers[manager.active_layer_index]
|
|
|
|
# Perform the move
|
|
if 0 <= source_idx < len(manager.layers):
|
|
layer_obj = manager.layers.pop(source_idx)
|
|
|
|
# Adjust target if we removed an item before it
|
|
if source_idx < target_idx:
|
|
target_idx -= 1
|
|
|
|
# Clamp
|
|
if target_idx < 0: target_idx = 0
|
|
if target_idx > len(manager.layers): target_idx = len(manager.layers)
|
|
|
|
manager.layers.insert(target_idx, layer_obj)
|
|
|
|
# Restore active layer index
|
|
if active_layer:
|
|
try:
|
|
manager.active_layer_index = manager.layers.index(active_layer)
|
|
except ValueError:
|
|
manager.active_layer_index = None
|
|
|
|
manager.save()
|
|
self.panel.RefreshList()
|
|
self.panel.list.SelectRow(target_idx)
|
|
return True
|
|
except Exception as e:
|
|
print(f"Drop Error: {e}")
|
|
return False
|
|
|
|
class LayerPanel(wx.Panel):
|
|
def __init__(self, parent):
|
|
super().__init__(parent)
|
|
|
|
sizer = wx.BoxSizer(wx.VERTICAL)
|
|
|
|
# Toolbar
|
|
btn_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
|
self.btn_new = wx.Button(self, label="New")
|
|
self.btn_add = wx.Button(self, label="Add")
|
|
self.btn_paste = wx.Button(self, label="Paste")
|
|
self.btn_del = wx.Button(self, label="Del")
|
|
self.btn_settings = wx.Button(self, label="⚙", size=(30, 24))
|
|
|
|
btn_sizer.Add(self.btn_new, 0, wx.ALL, 2)
|
|
btn_sizer.Add(self.btn_add, 0, wx.ALL, 2)
|
|
btn_sizer.Add(self.btn_paste, 0, wx.ALL, 2)
|
|
btn_sizer.Add(self.btn_del, 0, wx.ALL, 2)
|
|
btn_sizer.Add(self.btn_settings, 0, wx.ALL, 2)
|
|
|
|
# Sort Toolbar
|
|
sort_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
|
self.btn_up = wx.Button(self, label="▲", size=(30, 24))
|
|
self.btn_down = wx.Button(self, label="▼", size=(30, 24))
|
|
sort_sizer.Add(self.btn_up, 0, wx.ALL, 2)
|
|
sort_sizer.Add(self.btn_down, 0, wx.ALL, 2)
|
|
|
|
# DataViewListCtrl
|
|
# Use DV_ROW_LINES for row separators
|
|
self.list = dv.DataViewListCtrl(self, style=wx.BORDER_THEME | dv.DV_ROW_LINES)
|
|
|
|
# Columns: Active (Toggle), Visible (Toggle), Preview (IconText), Name (Text), Items (Text)
|
|
self.col_active = self.list.AppendToggleColumn("Act", width=35)
|
|
self.col_vis = self.list.AppendToggleColumn("Vis", width=35)
|
|
# Use IconTextColumn for preview to avoid AppendBitmapColumn issues
|
|
self.col_thumb = self.list.AppendIconTextColumn("Preview", width=85, mode=dv.DATAVIEW_CELL_INERT)
|
|
self.col_name = self.list.AppendTextColumn("Name", width=150, mode=dv.DATAVIEW_CELL_EDITABLE)
|
|
self.col_items = self.list.AppendTextColumn("Items", width=50)
|
|
|
|
# Drag and Drop
|
|
self.drop_target = LayerDropTarget(self)
|
|
self.list.SetDropTarget(self.drop_target)
|
|
|
|
sizer.Add(btn_sizer, 0, wx.EXPAND)
|
|
sizer.Add(sort_sizer, 0, wx.EXPAND)
|
|
sizer.Add(self.list, 1, wx.EXPAND)
|
|
self.SetSizer(sizer)
|
|
|
|
# Bindings
|
|
self.Bind(wx.EVT_BUTTON, self.OnNew, self.btn_new)
|
|
self.Bind(wx.EVT_BUTTON, self.OnAddSel, self.btn_add)
|
|
self.Bind(wx.EVT_BUTTON, self.OnPasteSel, self.btn_paste)
|
|
self.Bind(wx.EVT_BUTTON, self.OnDelete, self.btn_del)
|
|
self.Bind(wx.EVT_BUTTON, self.OnSettings, self.btn_settings)
|
|
self.Bind(wx.EVT_BUTTON, self.OnUp, self.btn_up)
|
|
self.Bind(wx.EVT_BUTTON, self.OnDown, self.btn_down)
|
|
|
|
self.list.Bind(dv.EVT_DATAVIEW_ITEM_VALUE_CHANGED, self.OnValueChanged)
|
|
self.list.Bind(dv.EVT_DATAVIEW_ITEM_EDITING_DONE, self.OnRenameDone)
|
|
self.list.Bind(dv.EVT_DATAVIEW_ITEM_CONTEXT_MENU, self.OnContextMenu)
|
|
self.list.Bind(dv.EVT_DATAVIEW_ITEM_BEGIN_DRAG, self.OnBeginDrag)
|
|
|
|
def RefreshList(self):
|
|
self.list.DeleteAllItems()
|
|
board = pcbnew.GetBoard()
|
|
|
|
for i, l in enumerate(manager.layers):
|
|
is_active = (manager.active_layer_index == i)
|
|
is_visible = l.visible
|
|
|
|
# Generate thumbnail
|
|
items_to_draw = l.on_board_items if l.visible else []
|
|
bmp = ThumbnailGenerator.generate_thumbnail(items_to_draw, board, 80, 40)
|
|
|
|
# Convert Bitmap to Icon for DataViewIconText
|
|
icon = wx.Icon()
|
|
icon.CopyFromBitmap(bmp)
|
|
preview_data = dv.DataViewIconText("", icon)
|
|
|
|
count_str = str(len(l.on_board_items) if l.visible else len(l.stored_items))
|
|
|
|
# Append [Active, Visible, IconText, Name, Count]
|
|
self.list.AppendItem([is_active, is_visible, preview_data, l.name, count_str])
|
|
|
|
# Force UI update
|
|
self.list.Refresh()
|
|
self.list.Update()
|
|
|
|
def OnValueChanged(self, event):
|
|
col = event.GetColumn()
|
|
item = event.GetItem()
|
|
if not item.IsOk(): return
|
|
|
|
row = self.list.ItemToRow(item)
|
|
if row < 0 or row >= len(manager.layers): return
|
|
|
|
layer = manager.layers[row]
|
|
|
|
# Column 0: Active
|
|
if col == 0:
|
|
# This is a radio-like behavior.
|
|
# The event has already toggled the value in the model, but we need to enforce single selection.
|
|
is_checked = self.list.GetToggleValue(row, 0)
|
|
|
|
if is_checked:
|
|
manager.set_active_layer(row)
|
|
# Uncheck others
|
|
for r in range(self.list.GetItemCount()):
|
|
if r != row:
|
|
self.list.SetToggleValue(False, r, 0)
|
|
else:
|
|
# If user unchecks the active layer, we have no active layer
|
|
if manager.active_layer_index == row:
|
|
manager.set_active_layer(None)
|
|
|
|
# Refresh list to update thumbnails if needed (active layer might be drawn differently)
|
|
wx.CallAfter(self.RefreshList)
|
|
|
|
# Column 1: Visible
|
|
elif col == 1:
|
|
is_visible = self.list.GetToggleValue(row, 1)
|
|
# Use CallAfter to decouple heavy logic from event handler
|
|
wx.CallAfter(self.DoToggleVis, row, is_visible)
|
|
|
|
def DoToggleVis(self, row, is_visible):
|
|
layer = manager.layers[row]
|
|
|
|
manager.clear_log()
|
|
|
|
if is_visible and not layer.visible:
|
|
layer.show()
|
|
elif not is_visible and layer.visible:
|
|
layer.hide()
|
|
|
|
pcbnew.Refresh()
|
|
self.RefreshList()
|
|
|
|
# Check if state matches intent
|
|
if layer.visible != is_visible:
|
|
# State mismatch! Show log.
|
|
dlg = LogDialog(self, "Layer Toggle Error", manager.get_log_text())
|
|
dlg.ShowModal()
|
|
dlg.Destroy()
|
|
|
|
def OnRenameDone(self, event):
|
|
item = event.GetItem()
|
|
if not item.IsOk(): return
|
|
row = self.list.ItemToRow(item)
|
|
|
|
# Use CallAfter to ensure the model has updated the text value
|
|
wx.CallAfter(self._SaveRename, row)
|
|
|
|
def _SaveRename(self, row):
|
|
if 0 <= row < len(manager.layers):
|
|
new_name = self.list.GetTextValue(row, 3)
|
|
manager.layers[row].name = new_name
|
|
manager.save()
|
|
|
|
def OnBeginDrag(self, event):
|
|
item = event.GetItem()
|
|
if not item.IsOk(): return
|
|
|
|
row = self.list.ItemToRow(item)
|
|
|
|
# Create drag source
|
|
source = wx.DropSource(self.list)
|
|
data = wx.TextDataObject(str(row))
|
|
source.SetData(data)
|
|
source.DoDragDrop(wx.Drag_DefaultMove)
|
|
|
|
def OnNew(self, event):
|
|
dlg = wx.TextEntryDialog(self, "Layer Name:", "New Layer")
|
|
if dlg.ShowModal() == wx.ID_OK:
|
|
manager.create_layer(dlg.GetValue())
|
|
self.RefreshList()
|
|
dlg.Destroy()
|
|
|
|
def OnAddSel(self, event):
|
|
row = self.list.GetSelectedRow()
|
|
if row == -1: return
|
|
layer = manager.layers[row]
|
|
if not layer.visible:
|
|
wx.MessageBox("Unhide layer first.")
|
|
return
|
|
|
|
manager.clear_log()
|
|
count, msg = layer.add_selection()
|
|
|
|
if count == 0:
|
|
dlg = LogDialog(self, "Add Selection Log", manager.get_log_text())
|
|
dlg.ShowModal()
|
|
dlg.Destroy()
|
|
else:
|
|
wx.MessageBox(msg)
|
|
|
|
self.RefreshList()
|
|
|
|
def GetClipboardText(self):
|
|
text = ""
|
|
if wx.TheClipboard.Open():
|
|
try:
|
|
if wx.TheClipboard.IsSupported(wx.DataFormat(wx.DF_TEXT)):
|
|
data = wx.TextDataObject()
|
|
if wx.TheClipboard.GetData(data):
|
|
text = data.GetText()
|
|
except: pass
|
|
finally:
|
|
wx.TheClipboard.Close()
|
|
return text
|
|
|
|
def OnPasteSel(self, event):
|
|
row = self.list.GetSelectedRow()
|
|
if row == -1: return
|
|
layer = manager.layers[row]
|
|
if not layer.visible:
|
|
wx.MessageBox("Unhide layer first.")
|
|
return
|
|
|
|
manager.clear_log()
|
|
text = self.GetClipboardText()
|
|
if not text:
|
|
wx.MessageBox("Clipboard empty or invalid.")
|
|
else:
|
|
count, msg = layer.add_from_text(text)
|
|
if count == 0:
|
|
dlg = LogDialog(self, "Paste Log", manager.get_log_text())
|
|
dlg.ShowModal()
|
|
dlg.Destroy()
|
|
else:
|
|
wx.MessageBox(msg)
|
|
|
|
self.RefreshList()
|
|
|
|
def OnDelete(self, event):
|
|
row = self.list.GetSelectedRow()
|
|
if row == -1: return
|
|
|
|
mode = manager.settings.get("delete_mode", "ask")
|
|
delete_items = False
|
|
|
|
if mode == "ask":
|
|
dlg = DeleteDialog(self)
|
|
res = dlg.ShowModal()
|
|
remember = dlg.cb_remember.GetValue()
|
|
dlg.Destroy()
|
|
if res == wx.ID_CANCEL: return
|
|
delete_items = (res == wx.ID_YES)
|
|
if remember:
|
|
manager.settings["delete_mode"] = "delete" if delete_items else "keep"
|
|
manager.save()
|
|
elif mode == "delete":
|
|
delete_items = True
|
|
elif mode == "keep":
|
|
delete_items = False
|
|
|
|
manager.delete_layer(row, delete_items)
|
|
self.RefreshList()
|
|
|
|
def OnUp(self, event):
|
|
row = self.list.GetSelectedRow()
|
|
if row == -1: return
|
|
if manager.move_layer_up(row):
|
|
self.RefreshList()
|
|
self.list.SelectRow(row - 1)
|
|
|
|
def OnDown(self, event):
|
|
row = self.list.GetSelectedRow()
|
|
if row == -1: return
|
|
if manager.move_layer_down(row):
|
|
self.RefreshList()
|
|
self.list.SelectRow(row + 1)
|
|
|
|
def OnSettings(self, event):
|
|
menu = wx.Menu()
|
|
menu.Append(wx.ID_ANY, "Deletion Behavior:", kind=wx.ITEM_NORMAL).Enable(False)
|
|
|
|
cur_mode = manager.settings.get("delete_mode", "ask")
|
|
id_ask = wx.NewIdRef()
|
|
id_del = wx.NewIdRef()
|
|
id_keep = wx.NewIdRef()
|
|
|
|
mi_ask = menu.AppendRadioItem(id_ask, "Ask every time")
|
|
mi_del = menu.AppendRadioItem(id_del, "Always Delete Items")
|
|
mi_keep = menu.AppendRadioItem(id_keep, "Always Keep Items")
|
|
|
|
if cur_mode == "delete": mi_del.Check()
|
|
elif cur_mode == "keep": mi_keep.Check()
|
|
else: mi_ask.Check()
|
|
|
|
menu.AppendSeparator()
|
|
id_analyze = wx.NewIdRef()
|
|
menu.Append(id_analyze, "Analyze Clipboard")
|
|
id_debug = wx.NewIdRef()
|
|
menu.Append(id_debug, "Show Debug Info")
|
|
|
|
menu.AppendSeparator()
|
|
id_recover = wx.NewIdRef()
|
|
menu.Append(id_recover, "Recover Lost Items")
|
|
|
|
self.Bind(wx.EVT_MENU, lambda e: self.SetMode("ask"), id=id_ask)
|
|
self.Bind(wx.EVT_MENU, lambda e: self.SetMode("delete"), id=id_del)
|
|
self.Bind(wx.EVT_MENU, lambda e: self.SetMode("keep"), id=id_keep)
|
|
self.Bind(wx.EVT_MENU, self.OnAnalyze, id=id_analyze)
|
|
self.Bind(wx.EVT_MENU, self.OnDebug, id=id_debug)
|
|
self.Bind(wx.EVT_MENU, self.OnRecover, id=id_recover)
|
|
|
|
self.PopupMenu(menu)
|
|
menu.Destroy()
|
|
|
|
def SetMode(self, mode):
|
|
manager.settings["delete_mode"] = mode
|
|
manager.save()
|
|
|
|
def OnAnalyze(self, event):
|
|
manager.clear_log()
|
|
text = self.GetClipboardText()
|
|
if text: manager.analyze_clipboard_text(text)
|
|
dlg = LogDialog(self, "Analysis Log", manager.get_log_text())
|
|
dlg.ShowModal()
|
|
dlg.Destroy()
|
|
|
|
def OnDebug(self, event):
|
|
# Show full log in LogDialog
|
|
dlg = LogDialog(self, "Debug Info", manager.get_log_text())
|
|
dlg.ShowModal()
|
|
dlg.Destroy()
|
|
|
|
def OnRecover(self, event):
|
|
count, msg = manager.recover_orphans()
|
|
wx.MessageBox(msg, "Recovery Result")
|
|
self.RefreshList()
|
|
|
|
def OnContextMenu(self, event):
|
|
item = event.GetItem()
|
|
if not item.IsOk(): return
|
|
|
|
row = self.list.ItemToRow(item)
|
|
self.list.SelectRow(row)
|
|
|
|
layer = manager.layers[row]
|
|
menu = wx.Menu()
|
|
|
|
is_active = (manager.active_layer_index == row)
|
|
lbl_active = "Deactivate" if is_active else "Set Active"
|
|
item_act = menu.Append(wx.ID_ANY, lbl_active)
|
|
|
|
lbl_vis = "Hide Layer" if layer.visible else "Show Layer"
|
|
item_vis = menu.Append(wx.ID_ANY, lbl_vis)
|
|
|
|
menu.AppendSeparator()
|
|
|
|
item_add = menu.Append(wx.ID_ANY, "Add Selection")
|
|
item_paste = menu.Append(wx.ID_ANY, "Add from Clipboard")
|
|
item_rem = menu.Append(wx.ID_ANY, "Remove Selection")
|
|
item_insp = menu.Append(wx.ID_ANY, "Inspect Content")
|
|
menu.AppendSeparator()
|
|
|
|
item_del_all = menu.Append(wx.ID_ANY, "Delete Layer & Items")
|
|
item_del_keep = menu.Append(wx.ID_ANY, "Delete Layer (Keep Items)")
|
|
item_clr = menu.Append(wx.ID_ANY, "Clear Items (Keep Layer)")
|
|
|
|
self.Bind(wx.EVT_MENU, lambda e: self.ToggleActive(row), item_act)
|
|
self.Bind(wx.EVT_MENU, lambda e: self.ToggleLayerVis(row), item_vis)
|
|
self.Bind(wx.EVT_MENU, lambda e: self.OnAddSel(None), item_add)
|
|
self.Bind(wx.EVT_MENU, lambda e: self.OnPasteSel(None), item_paste)
|
|
self.Bind(wx.EVT_MENU, lambda e: self.OnRemSel(row), item_rem)
|
|
self.Bind(wx.EVT_MENU, lambda e: self.OnInspect(row), item_insp)
|
|
self.Bind(wx.EVT_MENU, lambda e: self.DoDelete(row, True), item_del_all)
|
|
self.Bind(wx.EVT_MENU, lambda e: self.DoDelete(row, False), item_del_keep)
|
|
self.Bind(wx.EVT_MENU, lambda e: self.DoClear(row), item_clr)
|
|
|
|
self.PopupMenu(menu)
|
|
menu.Destroy()
|
|
|
|
def ToggleActive(self, row):
|
|
if manager.active_layer_index == row:
|
|
manager.set_active_layer(None)
|
|
self.list.SetToggleValue(False, row, 0)
|
|
else:
|
|
manager.set_active_layer(row)
|
|
for r in range(self.list.GetItemCount()):
|
|
self.list.SetToggleValue(r == row, r, 0)
|
|
self.RefreshList()
|
|
|
|
def ToggleLayerVis(self, row):
|
|
# Use CallAfter to decouple heavy logic from event handler
|
|
wx.CallAfter(self._ToggleLayerVisImpl, row)
|
|
|
|
def _ToggleLayerVisImpl(self, row):
|
|
layer = manager.layers[row]
|
|
if layer.visible: layer.hide()
|
|
else: layer.show()
|
|
pcbnew.Refresh()
|
|
self.RefreshList()
|
|
|
|
def OnRemSel(self, row):
|
|
layer = manager.layers[row]
|
|
manager.clear_log()
|
|
count, msg = layer.remove_selection()
|
|
if count == 0:
|
|
dlg = LogDialog(self, "Remove Selection Log", manager.get_log_text())
|
|
dlg.ShowModal()
|
|
dlg.Destroy()
|
|
else:
|
|
wx.MessageBox(msg)
|
|
self.RefreshList()
|
|
|
|
def OnInspect(self, row):
|
|
layer = manager.layers[row]
|
|
wx.MessageBox(layer.inspect(), "Layer Inspection")
|
|
|
|
def DoDelete(self, row, delete_items):
|
|
manager.delete_layer(row, delete_items)
|
|
self.RefreshList()
|
|
|
|
def DoClear(self, row):
|
|
manager.layers[row].clear_items()
|
|
self.RefreshList()
|
|
|
|
class LayerWindow(wx.Frame):
|
|
def __init__(self):
|
|
parent = None
|
|
try:
|
|
for w in wx.GetTopLevelWindows():
|
|
if "PCB Editor" in w.GetTitle():
|
|
parent = w
|
|
break
|
|
except: pass
|
|
|
|
super().__init__(parent, title="Logical Layers", size=(500, 600),
|
|
style=wx.DEFAULT_FRAME_STYLE | wx.FRAME_FLOAT_ON_PARENT)
|
|
|
|
self.panel = LayerPanel(self)
|
|
self.Bind(wx.EVT_CLOSE, self.OnClose)
|
|
self.Bind(wx.EVT_SHOW, self.OnShow)
|
|
self.Bind(wx.EVT_ACTIVATE, self.OnActivate)
|
|
|
|
def OnShow(self, event):
|
|
if event.IsShown():
|
|
manager.load()
|
|
self.panel.RefreshList()
|
|
event.Skip()
|
|
|
|
def OnClose(self, event):
|
|
self.Hide()
|
|
|
|
def OnActivate(self, event):
|
|
if event.GetActive():
|
|
self.SetTransparent(255) # Opaque
|
|
else:
|
|
self.SetTransparent(180) # ~70% Opaque (30% Transparent)
|
|
event.Skip()
|
|
|
|
_win = None
|
|
def ShowWindow():
|
|
global _win
|
|
if not _win: _win = LayerWindow()
|
|
_win.Show()
|
|
_win.Raise() |