added portrait_mode.py
This commit is contained in:
@@ -0,0 +1,586 @@
|
||||
from PySide6.QtWidgets import (
|
||||
QApplication, QWidget, QVBoxLayout, QHBoxLayout,
|
||||
QLabel, QFrame, QPushButton, QScrollArea, QStackedWidget,
|
||||
QSizePolicy,
|
||||
)
|
||||
from PySide6.QtCore import Qt, QPointF, QRectF
|
||||
from PySide6.QtGui import (
|
||||
QPainter, QColor, QPen, QLinearGradient,
|
||||
QFont, QFontMetrics,
|
||||
)
|
||||
import sys
|
||||
import math
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Colour palette
|
||||
# ---------------------------------------------------------------------------
|
||||
BG = "#071018"
|
||||
CARD_BG = "#0E1A26"
|
||||
ACCENT = "#62D8C8"
|
||||
ACCENT_DIM = "#1A3A36"
|
||||
TEXT = "#F5F7FA"
|
||||
SUBTEXT = "#8A9BB0"
|
||||
BUTTON_BG = "#132131"
|
||||
LED_OFF = "#1C2E3E"
|
||||
LED_ON = ACCENT
|
||||
ACTIVE_STEP = "#FFFFFF"
|
||||
|
||||
|
||||
STYLE = f"""
|
||||
QWidget {{
|
||||
background: {BG};
|
||||
color: {TEXT};
|
||||
font-family: 'Inter', 'SF Pro Display', Arial, sans-serif;
|
||||
font-size: 14px;
|
||||
}}
|
||||
QScrollArea {{
|
||||
border: none;
|
||||
background: transparent;
|
||||
}}
|
||||
QScrollBar:vertical {{
|
||||
background: {CARD_BG};
|
||||
width: 4px;
|
||||
border-radius: 2px;
|
||||
}}
|
||||
QScrollBar::handle:vertical {{
|
||||
background: {ACCENT_DIM};
|
||||
border-radius: 2px;
|
||||
min-height: 20px;
|
||||
}}
|
||||
QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {{
|
||||
height: 0px;
|
||||
}}
|
||||
"""
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Orbit / camera placeholder widget
|
||||
# ---------------------------------------------------------------------------
|
||||
class OrbitWidget(QWidget):
|
||||
def paintEvent(self, event):
|
||||
p = QPainter(self)
|
||||
p.setRenderHint(QPainter.Antialiasing)
|
||||
|
||||
w, h = self.width(), self.height()
|
||||
cx, cy = w / 2, h / 2
|
||||
|
||||
# Background gradient
|
||||
bg = QLinearGradient(0, 0, w, h)
|
||||
bg.setColorAt(0, QColor("#0D2A2C"))
|
||||
bg.setColorAt(1, QColor("#0D1E2E"))
|
||||
p.fillRect(self.rect(), bg)
|
||||
|
||||
# Corner brackets
|
||||
pen = QPen(QColor(ACCENT))
|
||||
pen.setWidth(2)
|
||||
p.setPen(pen)
|
||||
size = 28
|
||||
margin = 24
|
||||
for x, y, dx, dy in [
|
||||
(margin, margin, 1, 1),
|
||||
(w - margin, margin, -1, 1),
|
||||
(margin, h - margin, 1, -1),
|
||||
(w - margin, h - margin, -1, -1),
|
||||
]:
|
||||
p.drawLine(x, y, x + dx * size, y)
|
||||
p.drawLine(x, y, x, y + dy * size)
|
||||
|
||||
# Orbit rings
|
||||
pen.setWidth(1)
|
||||
pen.setColor(QColor(ACCENT + "80")) # semi-transparent
|
||||
p.setPen(pen)
|
||||
for r in [38, 68, 98]:
|
||||
p.drawEllipse(QPointF(cx, cy), r, r)
|
||||
|
||||
# Centre dot
|
||||
p.setBrush(QColor(ACCENT))
|
||||
p.setPen(Qt.NoPen)
|
||||
p.drawEllipse(QPointF(cx, cy), 9, 9)
|
||||
|
||||
# Orbiting dots on outer ring
|
||||
p.setBrush(QColor("#F5FFFF"))
|
||||
for angle in range(0, 360, 45):
|
||||
rad = math.radians(angle)
|
||||
ox = cx + math.cos(rad) * 98
|
||||
oy = cy + math.sin(rad) * 98
|
||||
p.drawEllipse(QPointF(ox, oy), 4, 4)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# LED step indicator
|
||||
# ---------------------------------------------------------------------------
|
||||
class LEDStages(QWidget):
|
||||
STEPS = ["Mount", "Centre", "Raster", "Collect"]
|
||||
|
||||
def __init__(self, active_step: int = 1, parent=None):
|
||||
"""
|
||||
active_step: 0-based index of the currently active step.
|
||||
Steps before active_step are shown as 'done' (filled accent),
|
||||
active_step is highlighted, later steps are dim.
|
||||
"""
|
||||
super().__init__(parent)
|
||||
self._active = active_step
|
||||
self.setFixedHeight(72)
|
||||
|
||||
def set_active_step(self, step: int):
|
||||
self._active = step
|
||||
self.update()
|
||||
|
||||
def paintEvent(self, event):
|
||||
p = QPainter(self)
|
||||
p.setRenderHint(QPainter.Antialiasing)
|
||||
|
||||
w = self.width()
|
||||
n = len(self.STEPS)
|
||||
step_w = w / n
|
||||
led_r = 10
|
||||
cy = 44 # vertical centre of LEDs
|
||||
label_y = 16
|
||||
|
||||
for i, name in enumerate(self.STEPS):
|
||||
cx = step_w * i + step_w / 2
|
||||
|
||||
# --- connector line to next step ---
|
||||
if i < n - 1:
|
||||
next_cx = step_w * (i + 1) + step_w / 2
|
||||
line_color = QColor(ACCENT) if i < self._active else QColor(LED_OFF)
|
||||
pen = QPen(line_color, 2)
|
||||
p.setPen(pen)
|
||||
p.drawLine(
|
||||
QPointF(cx + led_r + 3, cy),
|
||||
QPointF(next_cx - led_r - 3, cy),
|
||||
)
|
||||
|
||||
# --- LED circle ---
|
||||
p.setPen(Qt.NoPen)
|
||||
if i < self._active:
|
||||
# completed
|
||||
p.setBrush(QColor(ACCENT))
|
||||
p.drawEllipse(QPointF(cx, cy), led_r, led_r)
|
||||
# tick mark
|
||||
pen = QPen(QColor(BG), 2)
|
||||
pen.setCapStyle(Qt.RoundCap)
|
||||
p.setPen(pen)
|
||||
p.drawLine(
|
||||
QPointF(cx - 4, cy),
|
||||
QPointF(cx - 1, cy + 3),
|
||||
)
|
||||
p.drawLine(
|
||||
QPointF(cx - 1, cy + 3),
|
||||
QPointF(cx + 4, cy - 3),
|
||||
)
|
||||
elif i == self._active:
|
||||
# active — bright with glow ring
|
||||
glow_pen = QPen(QColor(ACCENT + "55"), 4)
|
||||
p.setPen(glow_pen)
|
||||
p.setBrush(Qt.NoBrush)
|
||||
p.drawEllipse(QPointF(cx, cy), led_r + 4, led_r + 4)
|
||||
p.setPen(Qt.NoPen)
|
||||
p.setBrush(QColor(ACTIVE_STEP))
|
||||
p.drawEllipse(QPointF(cx, cy), led_r, led_r)
|
||||
else:
|
||||
# pending
|
||||
p.setBrush(QColor(LED_OFF))
|
||||
p.drawEllipse(QPointF(cx, cy), led_r, led_r)
|
||||
|
||||
# --- label ---
|
||||
label_color = QColor(ACCENT) if i <= self._active else QColor(SUBTEXT)
|
||||
p.setPen(label_color)
|
||||
font = QFont("Inter", 10)
|
||||
if i == self._active:
|
||||
font.setWeight(QFont.Weight.DemiBold)
|
||||
p.setFont(font)
|
||||
fm = QFontMetrics(font)
|
||||
text_w = fm.horizontalAdvance(name)
|
||||
p.drawText(
|
||||
QRectF(cx - text_w / 2 - 4, 0, text_w + 8, label_y + 2),
|
||||
Qt.AlignCenter,
|
||||
name,
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Transport / control button
|
||||
# ---------------------------------------------------------------------------
|
||||
class ControlButton(QPushButton):
|
||||
def __init__(self, symbol: str, primary: bool = False, parent=None):
|
||||
super().__init__(parent)
|
||||
self._symbol = symbol
|
||||
self._primary = primary
|
||||
self._hovered = False
|
||||
size = 64 if primary else 52
|
||||
self.setFixedSize(size, size)
|
||||
self.setMouseTracking(True)
|
||||
|
||||
def enterEvent(self, event):
|
||||
self._hovered = True
|
||||
self.update()
|
||||
|
||||
def leaveEvent(self, event):
|
||||
self._hovered = False
|
||||
self.update()
|
||||
|
||||
def paintEvent(self, event):
|
||||
p = QPainter(self)
|
||||
p.setRenderHint(QPainter.Antialiasing)
|
||||
rect = self.rect()
|
||||
cx, cy = rect.width() / 2, rect.height() / 2
|
||||
r = min(rect.width(), rect.height()) / 2 - 2
|
||||
|
||||
if self._primary:
|
||||
if self._hovered:
|
||||
p.setBrush(QColor("#FFFFFF"))
|
||||
else:
|
||||
p.setBrush(QColor(ACCENT))
|
||||
p.setPen(Qt.NoPen)
|
||||
p.drawEllipse(QPointF(cx, cy), r, r)
|
||||
sym_color = QColor(BG)
|
||||
else:
|
||||
if self._hovered:
|
||||
p.setBrush(QColor(ACCENT))
|
||||
else:
|
||||
p.setBrush(QColor(BUTTON_BG))
|
||||
p.setPen(Qt.NoPen)
|
||||
p.drawEllipse(QPointF(cx, cy), r, r)
|
||||
sym_color = QColor(TEXT) if not self._hovered else QColor(BG)
|
||||
|
||||
p.setPen(QPen(sym_color, 2))
|
||||
font = QFont("Arial", 16 if self._primary else 13)
|
||||
p.setFont(font)
|
||||
p.drawText(rect, Qt.AlignCenter, self._symbol)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Queue item card
|
||||
# ---------------------------------------------------------------------------
|
||||
class QueueItemCard(QFrame):
|
||||
def __init__(
|
||||
self,
|
||||
index: int | str,
|
||||
title: str,
|
||||
subtitle: str,
|
||||
frames_done: int = 0,
|
||||
frames_total: int = 0,
|
||||
is_next: bool = False,
|
||||
parent=None,
|
||||
):
|
||||
super().__init__(parent)
|
||||
self._index = index
|
||||
self._title = title
|
||||
self._subtitle = subtitle
|
||||
self._frames_done = frames_done
|
||||
self._frames_total = frames_total
|
||||
self._is_next = is_next
|
||||
|
||||
self.setFixedHeight(72)
|
||||
self.setStyleSheet(f"""
|
||||
QFrame {{
|
||||
background: {"#112030" if is_next else "#0C1720"};
|
||||
border-radius: 14px;
|
||||
border: {"1px solid " + ACCENT_DIM if is_next else "none"};
|
||||
}}
|
||||
""")
|
||||
self._build_layout()
|
||||
|
||||
def _build_layout(self):
|
||||
layout = QHBoxLayout(self)
|
||||
layout.setContentsMargins(14, 0, 14, 0)
|
||||
layout.setSpacing(12)
|
||||
|
||||
# Index badge / play icon
|
||||
badge = QLabel()
|
||||
badge.setFixedSize(32, 32)
|
||||
badge.setAlignment(Qt.AlignCenter)
|
||||
if self._is_next:
|
||||
badge.setText("▶")
|
||||
badge.setStyleSheet(f"""
|
||||
color: {ACCENT};
|
||||
background: {ACCENT_DIM};
|
||||
border-radius: 16px;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
""")
|
||||
else:
|
||||
badge.setText(str(self._index))
|
||||
badge.setStyleSheet(f"""
|
||||
color: {SUBTEXT};
|
||||
background: {BUTTON_BG};
|
||||
border-radius: 16px;
|
||||
font-size: 12px;
|
||||
""")
|
||||
layout.addWidget(badge)
|
||||
|
||||
# Text block
|
||||
text_col = QVBoxLayout()
|
||||
text_col.setSpacing(2)
|
||||
text_col.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
title_lbl = QLabel(self._title)
|
||||
title_lbl.setStyleSheet(f"color: {TEXT}; font-size: 13px; font-weight: 600; background: transparent;")
|
||||
sub_lbl = QLabel(self._subtitle)
|
||||
sub_lbl.setStyleSheet(f"color: {SUBTEXT}; font-size: 11px; background: transparent;")
|
||||
|
||||
text_col.addWidget(title_lbl)
|
||||
text_col.addWidget(sub_lbl)
|
||||
layout.addLayout(text_col, stretch=1)
|
||||
|
||||
# Frame count + mini bar
|
||||
right_col = QVBoxLayout()
|
||||
right_col.setSpacing(4)
|
||||
right_col.setContentsMargins(0, 0, 0, 0)
|
||||
right_col.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
|
||||
|
||||
if self._frames_total > 0:
|
||||
frame_lbl = QLabel(f"{self._frames_done:,} / {self._frames_total:,}")
|
||||
frame_lbl.setAlignment(Qt.AlignRight)
|
||||
color = ACCENT if self._is_next else SUBTEXT
|
||||
frame_lbl.setStyleSheet(f"color: {color}; font-size: 11px; background: transparent;")
|
||||
right_col.addWidget(frame_lbl)
|
||||
|
||||
bar = MiniProgressBar(self._frames_done, self._frames_total)
|
||||
right_col.addWidget(bar)
|
||||
|
||||
layout.addLayout(right_col)
|
||||
|
||||
|
||||
class MiniProgressBar(QWidget):
|
||||
def __init__(self, done: int, total: int, parent=None):
|
||||
super().__init__(parent)
|
||||
self._done = done
|
||||
self._total = total
|
||||
self.setFixedSize(80, 4)
|
||||
|
||||
def paintEvent(self, event):
|
||||
p = QPainter(self)
|
||||
p.setRenderHint(QPainter.Antialiasing)
|
||||
w, h = self.width(), self.height()
|
||||
# background track
|
||||
p.setBrush(QColor(LED_OFF))
|
||||
p.setPen(Qt.NoPen)
|
||||
p.drawRoundedRect(0, 0, w, h, 2, 2)
|
||||
# fill
|
||||
if self._total > 0:
|
||||
fill_w = max(4, int(w * self._done / self._total))
|
||||
p.setBrush(QColor(ACCENT))
|
||||
p.drawRoundedRect(0, 0, fill_w, h, 2, 2)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Main SAM Camera widget
|
||||
# ---------------------------------------------------------------------------
|
||||
class SampleCamera(QWidget):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.setWindowFlags(Qt.FramelessWindowHint)
|
||||
self.setAttribute(Qt.WA_TranslucentBackground)
|
||||
self.resize(390, 860)
|
||||
self.setStyleSheet(STYLE)
|
||||
|
||||
self._stack = QStackedWidget()
|
||||
self._player_page = self._build_player()
|
||||
self._queue_page = self._build_queue()
|
||||
self._stack.addWidget(self._player_page)
|
||||
self._stack.addWidget(self._queue_page)
|
||||
|
||||
root = QVBoxLayout(self)
|
||||
root.setContentsMargins(12, 12, 12, 12)
|
||||
root.addWidget(self._stack)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Player page
|
||||
# ------------------------------------------------------------------
|
||||
def _build_player(self) -> QWidget:
|
||||
page = QWidget()
|
||||
layout = QVBoxLayout(page)
|
||||
layout.setSpacing(10)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
# ── Title ──────────────────────────────────────────────────────
|
||||
title = QLabel("S A M C A M E R A")
|
||||
title.setAlignment(Qt.AlignCenter)
|
||||
title.setStyleSheet(f"color: {ACCENT}; font-size: 13px; letter-spacing: 3px; font-weight: 600;")
|
||||
layout.addWidget(title)
|
||||
|
||||
# ── Camera card ────────────────────────────────────────────────
|
||||
cam_card = QFrame()
|
||||
cam_card.setStyleSheet(f"QFrame {{ background: {CARD_BG}; border-radius: 18px; }}")
|
||||
cam_card_layout = QVBoxLayout(cam_card)
|
||||
cam_card_layout.setContentsMargins(0, 0, 0, 0)
|
||||
cam_card_layout.setSpacing(0)
|
||||
orbit = OrbitWidget()
|
||||
orbit.setMinimumHeight(220)
|
||||
cam_card_layout.addWidget(orbit)
|
||||
layout.addWidget(cam_card)
|
||||
|
||||
# ── Sample name ────────────────────────────────────────────────
|
||||
name_lbl = QLabel("Crystal Plate 14 · Well C7")
|
||||
name_lbl.setStyleSheet(f"color: {TEXT}; font-size: 18px; font-weight: 700;")
|
||||
sub_lbl = QLabel("Serial MX · 1 kHz · Beamline X06SA")
|
||||
sub_lbl.setStyleSheet(f"color: {SUBTEXT}; font-size: 12px;")
|
||||
layout.addWidget(name_lbl)
|
||||
layout.addWidget(sub_lbl)
|
||||
|
||||
# ── LED step indicator ─────────────────────────────────────────
|
||||
self._leds = LEDStages(active_step=1) # 0=Mount done, 1=Centre active
|
||||
layout.addWidget(self._leds)
|
||||
|
||||
# ── Transport controls ─────────────────────────────────────────
|
||||
ctrl_frame = QFrame()
|
||||
ctrl_frame.setStyleSheet(f"QFrame {{ background: {CARD_BG}; border-radius: 18px; }}")
|
||||
ctrl_layout = QHBoxLayout(ctrl_frame)
|
||||
ctrl_layout.setContentsMargins(16, 12, 16, 12)
|
||||
ctrl_layout.setSpacing(0)
|
||||
|
||||
buttons = [
|
||||
("⏮", False),
|
||||
("⏪", False),
|
||||
("⏸", True), # primary / highlighted
|
||||
("⏩", False),
|
||||
("⏭", False),
|
||||
]
|
||||
for sym, primary in buttons:
|
||||
btn = ControlButton(sym, primary=primary)
|
||||
ctrl_layout.addWidget(btn, alignment=Qt.AlignCenter)
|
||||
if not primary:
|
||||
ctrl_layout.addStretch(1)
|
||||
layout.addWidget(ctrl_frame)
|
||||
|
||||
# ── Up next header ─────────────────────────────────────────────
|
||||
up_next_row = QHBoxLayout()
|
||||
up_next_lbl = QLabel("UP NEXT")
|
||||
up_next_lbl.setStyleSheet(f"color: {ACCENT}; font-size: 11px; letter-spacing: 2px; font-weight: 700;")
|
||||
samples_lbl = QLabel("5 SAMPLES")
|
||||
samples_lbl.setStyleSheet(f"color: {SUBTEXT}; font-size: 11px; letter-spacing: 1px;")
|
||||
up_next_row.addWidget(up_next_lbl)
|
||||
up_next_row.addStretch()
|
||||
up_next_row.addWidget(samples_lbl)
|
||||
layout.addLayout(up_next_row)
|
||||
|
||||
# ── Queue preview cards ────────────────────────────────────────
|
||||
queue_items = [
|
||||
("Crystal Plate 14 · Well C8", "Serial MX · 1 kHz", 4100, 6000, True),
|
||||
("Crystal Plate 15 · Grid Scan", "Raster · 10 Hz", 0, 2000, False),
|
||||
("Crystal Plate 13 · Well A1", "Serial MX · 1 kHz", 0, 5000, False),
|
||||
("Crystal Plate 13 · Well B3", "Serial MX · 1 kHz", 0, 4500, False),
|
||||
("Crystal Plate 14 · Well D5", "Serial MX · 1 kHz", 0, 6000, False),
|
||||
]
|
||||
|
||||
queue_widget = QWidget()
|
||||
queue_layout = QVBoxLayout(queue_widget)
|
||||
queue_layout.setSpacing(6)
|
||||
queue_layout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
for i, (title, sub, done, total, is_next) in enumerate(queue_items):
|
||||
card = QueueItemCard(
|
||||
index="▶" if is_next else i + 1,
|
||||
title=title,
|
||||
subtitle=sub,
|
||||
frames_done=done,
|
||||
frames_total=total,
|
||||
is_next=is_next,
|
||||
)
|
||||
queue_layout.addWidget(card)
|
||||
|
||||
layout.addWidget(queue_widget)
|
||||
|
||||
# ── View full queue button ────────────────────────────────────
|
||||
view_btn = self._accent_button("VIEW FULL QUEUE ☰")
|
||||
view_btn.clicked.connect(lambda: self._stack.setCurrentWidget(self._queue_page))
|
||||
layout.addWidget(view_btn)
|
||||
|
||||
return page
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Queue / full list page
|
||||
# ------------------------------------------------------------------
|
||||
def _build_queue(self) -> QWidget:
|
||||
page = QWidget()
|
||||
layout = QVBoxLayout(page)
|
||||
layout.setSpacing(10)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
title = QLabel("SAMPLE QUEUE")
|
||||
title.setAlignment(Qt.AlignCenter)
|
||||
title.setStyleSheet(f"color: {ACCENT}; font-size: 13px; letter-spacing: 3px; font-weight: 700;")
|
||||
layout.addWidget(title)
|
||||
|
||||
scroll = QScrollArea()
|
||||
scroll.setWidgetResizable(True)
|
||||
scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
|
||||
|
||||
inner = QWidget()
|
||||
inner_layout = QVBoxLayout(inner)
|
||||
inner_layout.setSpacing(6)
|
||||
inner_layout.setContentsMargins(0, 4, 0, 4)
|
||||
|
||||
for i in range(1, 20):
|
||||
card = QueueItemCard(
|
||||
index=i,
|
||||
title=f"Crystal Plate {i} · Well C{i}",
|
||||
subtitle="Serial MX · 1 kHz",
|
||||
frames_done=0,
|
||||
frames_total=6000,
|
||||
)
|
||||
inner_layout.addWidget(card)
|
||||
|
||||
scroll.setWidget(inner)
|
||||
layout.addWidget(scroll, stretch=1)
|
||||
|
||||
back_btn = self._accent_button("← BACK TO CAMERA")
|
||||
back_btn.clicked.connect(lambda: self._stack.setCurrentWidget(self._player_page))
|
||||
layout.addWidget(back_btn)
|
||||
|
||||
return page
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Helper: styled accent button
|
||||
# ------------------------------------------------------------------
|
||||
@staticmethod
|
||||
def _accent_button(text: str) -> QPushButton:
|
||||
btn = QPushButton(text)
|
||||
btn.setFixedHeight(48)
|
||||
btn.setStyleSheet(f"""
|
||||
QPushButton {{
|
||||
background: transparent;
|
||||
border: 1.5px solid {ACCENT};
|
||||
border-radius: 14px;
|
||||
color: {ACCENT};
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 1.5px;
|
||||
}}
|
||||
QPushButton:hover {{
|
||||
background: {ACCENT_DIM};
|
||||
}}
|
||||
QPushButton:pressed {{
|
||||
background: {ACCENT};
|
||||
color: {BG};
|
||||
}}
|
||||
""")
|
||||
return btn
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Entry point
|
||||
# ---------------------------------------------------------------------------
|
||||
def export_png():
|
||||
app = QApplication.instance() or QApplication(sys.argv)
|
||||
ui = SampleCamera()
|
||||
ui.show()
|
||||
app.processEvents()
|
||||
|
||||
ui._stack.setCurrentWidget(ui._player_page)
|
||||
app.processEvents()
|
||||
ui.grab().save("sam_camera_player.png")
|
||||
|
||||
ui._stack.setCurrentWidget(ui._queue_page)
|
||||
app.processEvents()
|
||||
ui.grab().save("sam_camera_queue.png")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = QApplication(sys.argv)
|
||||
ui = SampleCamera()
|
||||
ui.show()
|
||||
sys.exit(app.exec())
|
||||
Reference in New Issue
Block a user