add CHANGELOG, icon_full, update gitignore
This commit is contained in:
+45
-87
@@ -12,18 +12,28 @@ from dtc_client import DTCClient, fills_to_trades, monitor_realtime
|
||||
from tralgo_api import build_session, send_trade
|
||||
|
||||
try:
|
||||
from PIL import Image, ImageDraw, ImageTk
|
||||
from PIL import Image, ImageTk
|
||||
_PIL_AVAILABLE = True
|
||||
except ImportError:
|
||||
_PIL_AVAILABLE = False
|
||||
|
||||
try:
|
||||
import pystray
|
||||
_PYSTRAY_AVAILABLE = True
|
||||
except ImportError:
|
||||
_PYSTRAY_AVAILABLE = False
|
||||
_ICON_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "icon.png")
|
||||
_ICON_FULL_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "icon_full.png")
|
||||
|
||||
_ICON_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "icon.png")
|
||||
|
||||
def _load_icon(path, size):
|
||||
"""Open icon, crop transparent padding, add 8 % margin, resize to size×size."""
|
||||
img = Image.open(path).convert("RGBA")
|
||||
bbox = img.getbbox()
|
||||
if bbox:
|
||||
img = img.crop(bbox)
|
||||
m = max(img.width, img.height) // 12 # ~8 % margin
|
||||
canvas_sz = max(img.width, img.height) + 2 * m
|
||||
canvas = Image.new("RGBA", (canvas_sz, canvas_sz), (0, 0, 0, 0))
|
||||
ox = (canvas_sz - img.width) // 2
|
||||
oy = (canvas_sz - img.height) // 2
|
||||
canvas.paste(img, (ox, oy), img)
|
||||
return canvas.resize((size, size), Image.LANCZOS)
|
||||
|
||||
|
||||
# ── Config window ─────────────────────────────────────────────────────────────
|
||||
@@ -335,7 +345,8 @@ class ManualImportWindow:
|
||||
|
||||
def _run():
|
||||
all_fills = []
|
||||
error_msg = None
|
||||
dtc_error = None
|
||||
|
||||
try:
|
||||
client = DTCClient(host, port)
|
||||
client.connect()
|
||||
@@ -345,27 +356,27 @@ class ManualImportWindow:
|
||||
all_fills.extend(fills)
|
||||
client.disconnect()
|
||||
except Exception as e:
|
||||
error_msg = str(e)
|
||||
dtc_error = str(e)
|
||||
|
||||
if error_msg:
|
||||
self.win.after(0, lambda: self._search_done([], error_msg))
|
||||
if dtc_error and not all_fills:
|
||||
self.win.after(0, lambda: self._search_done([], dtc_error))
|
||||
return
|
||||
|
||||
# Filter by date range
|
||||
filtered = []
|
||||
filtered_fills = []
|
||||
for f in all_fills:
|
||||
if not f.get("datetime"):
|
||||
filtered.append(f)
|
||||
filtered_fills.append(f)
|
||||
continue
|
||||
d = f["datetime"].strftime("%Y-%m-%d")
|
||||
if date_from and d < date_from:
|
||||
continue
|
||||
if date_to and d > date_to:
|
||||
continue
|
||||
filtered.append(f)
|
||||
filtered_fills.append(f)
|
||||
|
||||
trades = fills_to_trades(filtered, instruments)
|
||||
self.win.after(0, lambda: self._search_done(trades, None))
|
||||
all_trades = fills_to_trades(filtered_fills, instruments)
|
||||
err = dtc_error if not all_trades else None
|
||||
self.win.after(0, lambda: self._search_done(all_trades, err))
|
||||
|
||||
threading.Thread(target=_run, daemon=True).start()
|
||||
|
||||
@@ -515,12 +526,12 @@ class App:
|
||||
def __init__(self):
|
||||
self.root = tk.Tk()
|
||||
self.root.withdraw()
|
||||
self.root.title("GizerBridge")
|
||||
self.root.tk.call("wm", "iconname", self.root, "gizerbridge")
|
||||
if _PIL_AVAILABLE:
|
||||
try:
|
||||
_base = Image.open(_ICON_PATH).convert("RGBA")
|
||||
# multiple sizes → WM picks best for taskbar / alt-tab
|
||||
self._wm_icons = [
|
||||
ImageTk.PhotoImage(_base.resize((sz, sz), Image.LANCZOS))
|
||||
ImageTk.PhotoImage(_load_icon(_ICON_PATH, sz))
|
||||
for sz in (256, 128, 64, 48, 32)
|
||||
]
|
||||
self.root.iconphoto(True, *self._wm_icons)
|
||||
@@ -541,7 +552,6 @@ class App:
|
||||
|
||||
def run(self):
|
||||
self.root.after(200, self._process_queue)
|
||||
self._setup_tray()
|
||||
|
||||
if not self.config.get("api_key"):
|
||||
self.root.after(100, lambda: ConfigWindow(
|
||||
@@ -554,56 +564,6 @@ class App:
|
||||
|
||||
def _set_icon_color(self, color):
|
||||
self._status_color = color
|
||||
if hasattr(self, "_tray") and _PIL_AVAILABLE:
|
||||
try:
|
||||
self._tray.icon = self._make_tray_image(color)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# ── Pystray helpers ───────────────────────────────────────────────────────
|
||||
|
||||
def _make_tray_image(self, color="yellow"):
|
||||
"""256×256 logo with a small colored status dot in the bottom-right."""
|
||||
img = Image.open(_ICON_PATH).convert("RGBA").resize((256, 256), Image.LANCZOS)
|
||||
dot_colors = {
|
||||
"green": (0, 200, 0, 255),
|
||||
"red": (220, 0, 0, 255),
|
||||
"yellow": (230, 190, 0, 255),
|
||||
}
|
||||
c = dot_colors.get(color, (160, 160, 160, 255))
|
||||
draw = ImageDraw.Draw(img)
|
||||
r = 30
|
||||
x0, y0 = 256 - 2*r - 8, 256 - 2*r - 8
|
||||
draw.ellipse([x0, y0, x0 + 2*r, y0 + 2*r],
|
||||
fill=c, outline=(0, 0, 0, 160), width=3)
|
||||
return img
|
||||
|
||||
def _setup_tray(self):
|
||||
if not _PYSTRAY_AVAILABLE or not _PIL_AVAILABLE:
|
||||
return
|
||||
menu = pystray.Menu(
|
||||
pystray.MenuItem("GizerBridge", self._tray_show, default=True),
|
||||
pystray.Menu.SEPARATOR,
|
||||
pystray.MenuItem("Manueller Import",
|
||||
lambda: self._queue.put(self._open_manual_import)),
|
||||
pystray.MenuItem("Einstellungen",
|
||||
lambda: self._queue.put(self._open_config)),
|
||||
pystray.MenuItem("Log",
|
||||
lambda: self._queue.put(self._open_log)),
|
||||
pystray.Menu.SEPARATOR,
|
||||
pystray.MenuItem("Beenden",
|
||||
lambda: self._queue.put(self._quit)),
|
||||
)
|
||||
self._tray = pystray.Icon(
|
||||
"gizerbridge",
|
||||
self._make_tray_image("yellow"),
|
||||
"GizerBridge",
|
||||
menu,
|
||||
)
|
||||
self._tray.run_detached()
|
||||
|
||||
def _tray_show(self, *_):
|
||||
self._queue.put(lambda: (self.root.deiconify(), self.root.lift()))
|
||||
|
||||
# ── Main window ───────────────────────────────────────────────────────────
|
||||
|
||||
@@ -621,14 +581,14 @@ class App:
|
||||
hdr.pack(fill=tk.X)
|
||||
if _PIL_AVAILABLE:
|
||||
try:
|
||||
_img = Image.open(_ICON_PATH).convert("RGBA")
|
||||
_img = _img.resize((128, 128), Image.LANCZOS)
|
||||
self._hdr_photo = ImageTk.PhotoImage(_img)
|
||||
tk.Label(hdr, image=self._hdr_photo).pack(side=tk.LEFT, padx=(0, 8))
|
||||
self._hdr_photo = ImageTk.PhotoImage(_load_icon(_ICON_FULL_PATH, 128))
|
||||
tk.Label(hdr, image=self._hdr_photo).pack(side=tk.LEFT)
|
||||
except Exception:
|
||||
pass
|
||||
tk.Label(hdr, text="GizerBridge",
|
||||
font=("", 13, "bold")).pack(side=tk.LEFT)
|
||||
tk.Label(hdr, text="GizerBridge",
|
||||
font=("", 13, "bold")).pack(side=tk.LEFT)
|
||||
else:
|
||||
tk.Label(hdr, text="GizerBridge",
|
||||
font=("", 13, "bold")).pack(side=tk.LEFT)
|
||||
|
||||
# ── Status row ──
|
||||
sf = tk.Frame(self.root, padx=14)
|
||||
@@ -652,8 +612,7 @@ class App:
|
||||
tk.Button(bf, text=text, width=15,
|
||||
command=cmd).pack(side=tk.LEFT, padx=(0, 6))
|
||||
|
||||
close_action = self.root.withdraw if _PYSTRAY_AVAILABLE else self._quit
|
||||
self.root.protocol("WM_DELETE_WINDOW", close_action)
|
||||
self.root.protocol("WM_DELETE_WINDOW", self._quit)
|
||||
|
||||
# ── Monitor thread ────────────────────────────────────────────────────────
|
||||
|
||||
@@ -687,8 +646,6 @@ class App:
|
||||
accounts = cfg.get("accounts", [])
|
||||
instruments = self._active_instruments(cfg)
|
||||
|
||||
self._log(f"Verbinde mit Sierra Chart DTC {host}:{port}…")
|
||||
|
||||
def _on_trade(trade):
|
||||
if trade["trade_id"] in self.sent:
|
||||
return
|
||||
@@ -708,6 +665,12 @@ class App:
|
||||
self._update_status_msg(err_msg)
|
||||
self._queue.put(lambda: self._set_icon_color("red"))
|
||||
|
||||
if not accounts:
|
||||
self._log("Keine Konten konfiguriert.")
|
||||
return
|
||||
|
||||
self._log(f"Verbinde mit Sierra Chart DTC {host}:{port}…")
|
||||
|
||||
def _on_ready(existing_ids):
|
||||
self.sent.update(existing_ids)
|
||||
save_sent(self.sent)
|
||||
@@ -830,11 +793,6 @@ class App:
|
||||
def _quit(self):
|
||||
self._stop.set()
|
||||
save_sent(self.sent)
|
||||
if hasattr(self, "_tray"):
|
||||
try:
|
||||
self._tray.stop()
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
self.root.quit()
|
||||
self.root.destroy()
|
||||
|
||||
Reference in New Issue
Block a user