Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Photom_GUI #156

Draft
wants to merge 20 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
486 changes: 486 additions & 0 deletions copylot/gui/_qt/custom_widgets/photom_live_window.py

Large diffs are not rendered by default.

50 changes: 50 additions & 0 deletions copylot/gui/_qt/custom_widgets/qt_logger.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import textwrap

from PyQt5.QtCore import Qt, QSize
from PyQt5.QtWidgets import (
QPlainTextEdit,
QGroupBox,
QVBoxLayout,
QScrollArea,
QWidget,
QApplication,
)

import logging


class QtLogger(logging.Handler):
def __init__(self, widget):
super().__init__()
self.widget = widget

def emit(self, record):
msg = self.format(record)
self.widget.log_txt.appendPlainText(msg)


class QtLogBox(QGroupBox):
def __init__(self, title: str):
super().__init__()
self.setTitle(title)
# self.setStyleSheet('QGroupBox::title {subcontrol-origin: margin; background: transparent;}')
self.setStyleSheet('QGroupBox {font-size: 14pt;}')
# self.setMaximumHeight(200)
# Add a log_txt to display text.
self.log_txt = QPlainTextEdit()
self.log_txt.setMinimumSize(QSize(200, 200))
self.log_txt.setLineWidth(1)
# self.log_txt.setAlignment(Qt.AlignTop)
self.log_txt.setLineWrapMode(QPlainTextEdit.WidgetWidth)
self.log_txt.setReadOnly(True)

# Define a scroll area
self.scroll = QScrollArea()
self.scroll.setWidget(self.log_txt)
self.scroll.setWidgetResizable(True)

# Configure layout
layout = QVBoxLayout(self)
layout.setContentsMargins(0, 0, 0, 0)
layout.addWidget(self.scroll)
self.setLayout(layout)
154 changes: 154 additions & 0 deletions copylot/gui/_qt/dockables/photom_control.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
from qtpy.QtWidgets import (
QWidget,
QApplication,
QComboBox,
QPushButton,
QVBoxLayout,
QLabel,
QGroupBox,
QGridLayout,
QSlider,
QTabWidget,
QDesktopWidget,
QMessageBox,
)
from qtpy.QtCore import Qt, Signal, Slot
import time

from copylot.gui._qt.job_runners.worker import Worker
from copylot.gui._qt.photom_control.tab_manager import TabManager
from copylot.gui._qt.custom_widgets.photom_live_window import LiveViewWindow
from copylot.gui._qt.photom_control.utils.affinetransform import AffineTransform
from copylot.gui._qt.photom_control.helper_functions.messagebox import MessageBox
from copylot.gui._qt.custom_widgets.qt_logger import QtLogger, QtLogBox
from copylot.gui._qt.photom_control.logger import configure_logger
import os
import logging

logger = logging.getLogger('photom')

# This flag disables the loading of the DACs and lasers.
demo_mode = True
dac_mode = False


if not demo_mode:
from copylot.hardware.lasers.vortran import vortran
from copylot.hardware.mirrors.optotune import mirror

serial_num = ''
laser_port = ''
mirror_port = ''
# from copylot.hardware.lasers import

print('Running in the UI demo mode. (from ControlPanel)')


# TODO: setup a filelogger location
class PhotomControlDockWidget(QWidget):
# TODO: Need to define the threads needed
# thread_launching = Signal()
def __init__(self, parent, threadpool):
super(QWidget, self).__init__(parent)

# Variables for testing purposes to run demo mode or dac mode
self.demo_mode = demo_mode
self.dac_mode = dac_mode

# Get the number of screens and make the live view
num_screens = QDesktopWidget().screenCount()
# Determine window sizes
self.liveViewGeo = (
QDesktopWidget().screenGeometry(num_screens - 1).left(),
QDesktopWidget().screenGeometry(num_screens - 1).top(),
min(QDesktopWidget().screenGeometry(num_screens - 1).width() - 400, 800),
min(QDesktopWidget().screenGeometry(num_screens - 1).height(), 800),
)
print(f'liveViewGeo {self.liveViewGeo}')
self.ctrlPanelGeo = (
QDesktopWidget().screenGeometry(num_screens - 1).left()
+ self.liveViewGeo[2],
QDesktopWidget().screenGeometry(num_screens - 1).top(),
400,
self.liveViewGeo[3],
)
print(f'ctrlPanelGeo {self.ctrlPanelGeo}')
self.parent = parent
self.threadpool = threadpool
self.state_tracker = False
# ===============================================
# Laser and galvo
self.current_laser = (
0 # this variable is used for knowing which laser to calibrate
)

# Scan pattern selection
self.current_scan_shape = 0
self.current_scan_pattern = 0

if self.demo_mode:
self.mirror_0 = 0
else:
# Initialize the laser and the galvo
self.laser_0 = vortran.VortranLaser(
serial_number=serial_num, port=laser_port
)
self.mirror_0 = mirror.OptoMirror(com_port=mirror_port)
if self.dac_mode:
pass
pass

# =============================================
# Create folder for saving calibration matrix and other stuff
self.savepath_configs = 'stored_configs'
# self.savepath_scshot = 'screenshot'
os.makedirs(self.savepath_configs, exist_ok=True)
# os.makedirs(self.savepath_scshot, exist_ok=True)

# Set the main Layout
self.main_layout = QGridLayout()

# Photom overlay window
self.window1 = LiveViewWindow(self)
self.tabmanager = TabManager(self)
# Buttons
self.sl_opacity = QSlider(Qt.Horizontal)
self.sl_opacity.setRange(0, 100)
self.sl_opacity.setValue(int(self.window1.opacity * 100))
self.opacity_indicator = QLabel(f'Opacity {0.0 * 100} %')
self.sl_opacity.valueChanged.connect(self.change_trancparancy)

# Set the Widget Layout
self.layout = QGridLayout()
self.layout.setAlignment(Qt.AlignTop)
self.layout.addWidget(self.sl_opacity, 0, 0)
self.layout.addWidget(self.opacity_indicator, 0, 1)

# Tab Manager
self.layout.addWidget(self.tabmanager, 3, 0, 1, 3)
self.setLayout(self.layout)

# Logger
log_box = QtLogBox('Logging displayer')
self.layout.addWidget(log_box, 4, 0, 1, 3)
# Get the StreamHandler attached to the logger
log_box_handler = QtLogger(log_box)
log_box_handler.setFormatter(logging.Formatter("%(levelname)s - %(module)s - %(message)s"))
logger.addHandler(log_box_handler)

logger.debug('aaaaaa')
logger.info('asfasdfasdfsdf')
logger.info('asfasdfasdfsdf')

# ===============================
# Transform matrix that aligns cursor with laser point
self.transform_list = [AffineTransform(), AffineTransform()]

@property
def parameters(self):
raise NotImplementedError("parameters not yet implemented")

def change_trancparancy(self):
self.opacity_indicator.setText(f'Opacity {self.sl_opacity.value()} %')
self.window1.opacity = self.sl_opacity.value() / 100
self.window1.setWindowOpacity(self.window1.opacity)
169 changes: 169 additions & 0 deletions copylot/gui/_qt/photom_control/UI_demo/movable_windows.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
import sys
# from mcculw import ul
# from mcculw.ul import ULError

# from props.ao import AnalogOutputProps

from PyQt5.QtWidgets import (
QApplication,
QMainWindow,
QGraphicsScene,
QGraphicsSimpleTextItem,
QGraphicsItem,
QGraphicsView,
QPushButton,
QTabWidget,
QWidget,
QVBoxLayout,
QDesktopWidget,
)

from PyQt5.QtCore import Qt, QPointF
from PyQt5.QtGui import QPainter, QPen, QMouseEvent, QCursor

#TODO; custom logger that has to be properly implemented in main package
from copylot.gui._qt.photom_control.logger import setup_logger
logger = setup_logger()

class CustomWindow(QMainWindow):
def __init__(self):
super().__init__()
self.windowGeo = (300, 300, 1000, 1000)
self.setMouseTracking(True)
self.mouseX = None
self.mouseY = None
self.board_num = 0
# self.ao_props = AnalogOutputProps(self.board_num)
self.setWindowOpacity(0.7)
self.scale = 0.025
self.offset = (-0.032000, -0.046200)

self.initMarker()
self.initUI()

def initUI(self):
self.setGeometry(
self.windowGeo[0],
self.windowGeo[1],
self.windowGeo[2],
self.windowGeo[3],
)
self.setWindowTitle('Mouse Tracker')
# self.setFixedSize(
# self.windowGeo[2],
# self.windowGeo[3],
# )
# self.signal_to_DAC()
self.show()

def initMarker(self):
scene = QGraphicsScene(self)
view = QGraphicsView(scene)
view.setMouseTracking(True)
self.setCentralWidget(view)
self.setMouseTracking(True)
self.marker = QGraphicsSimpleTextItem('X')
self.marker.setFlag(QGraphicsItem.ItemIsMovable, True)
scene.addItem(self.marker)

def recordinate(self, rawcord):
return -self.scale * (rawcord - (self.windowGeo[2] / 2)) / 50

def mouseMoveEvent(self, event: 'QGraphicsSceneMouseEvent'):
new_cursor_position = event.scenePos()

print(f'current x: {new_cursor_position}')


# self.mouseX = self.recordinate(event.x())
# self.mouseY = self.recordinate(event.y())
# print('Mouse coords: ( %f : %f )' % (self.mouseX, self.mouseY))
# print(f'{event.x()}, {event.y()}')
# ao_range = self.ao_props.available_ranges[0]
# raw_valueX = ul.from_eng_units(self.board_num, ao_range, self.mouseX)
# raw_valueY = ul.from_eng_units(self.board_num, ao_range, self.mouseY)
# print(f'{raw_valueX}, {raw_valueY}')

def mousePressEvent(self, event):
marker_x = self.marker.pos().x()
marker_y = self.marker.pos().y()
print(f'x position: {(marker_x, marker_y)}')
# print('Mouse press coords: ( %f : %f )' % (self.mouseX, self.mouseY))
# self.signal_to_DAC()

# def mouseReleaseEvent(self, event):
# print('Mouse release coords: ( %f : %f )' % (self.mouseX, self.mouseY))


# def signal_to_DAC(self):
# if self.mouseX is None:
# self.mouseX = 0
# if self.mouseY is None:
# self.mouseY = 0
#
# self.mouseX = self.mouseX + self.offset[0]
# self.mouseY = self.mouseY + self.offset[1]
# ao_range = self.ao_props.available_ranges[0]
# raw_valueX = ul.from_eng_units(self.board_num, ao_range, self.mouseX)
# raw_valueY = ul.from_eng_units(self.board_num, ao_range, self.mouseY)
#
# try:
# ul.a_out(self.board_num, 1, ao_range, raw_valueX)
# ul.a_out(self.board_num, 0, ao_range, raw_valueY)
# except ULError as e:
# self.show_ul_error(e)


class CtrlWindow(QMainWindow):
def __init__(self):
super().__init__()
#Get the number of screens and make the live view
num_screens = QDesktopWidget().screenCount()
logger.debug(f'num screens {num_screens}')

self.windowGeo = (1300, 300, 500, 1000)
self.buttonSize = (200, 100)
self.initUI()

def initUI(self):
self.layout = QVBoxLayout(self)
self.setGeometry(
self.windowGeo[0],
self.windowGeo[1],
self.windowGeo[2],
self.windowGeo[3],
)
self.setWindowTitle('Control panel')
self.setFixedSize(
self.windowGeo[2],
self.windowGeo[3],
)

self.button = QPushButton("OK", self)
self.button.setStyleSheet(
"QPushButton{"
"font-size: 24px;"
"font-family: Helvetica;"
"border-width: 2px;"
"border-radius: 15px;"
"border-color: black;"
"border-style: outset;"
"}\n"
"QPushButton:pressed {background-color: gray;}"
)
self.button.move(250, 250) # button location
self.button.setGeometry(
(self.windowGeo[2] - self.buttonSize[0]) // 2,
100,
self.buttonSize[0],
self.buttonSize[1],
)

self.show()


if __name__ == "__main__":
app = QApplication(sys.argv)
dac = CustomWindow()
ctrl = CtrlWindow()
sys.exit(app.exec_())
20 changes: 20 additions & 0 deletions copylot/gui/_qt/photom_control/UI_demo/qtscenes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from PyQt5.QtWidgets import QGraphicsView, QGraphicsScene, QGraphicsSimpleTextItem, QApplication
import sys

# Create the application
app = QApplication(sys.argv)

# Create the QGraphicsScene and add a QGraphicsSimpleTextItem to it
scene = QGraphicsScene()
text_item = QGraphicsSimpleTextItem('Hello, world!')
scene.addItem(text_item)

# Create the QGraphicsView and set the scene on it
view = QGraphicsView()
view.setScene(scene)

# Show the view
view.show()

# Run the event loop
sys.exit(app.exec_())
Empty file.
Loading