2016-03-05 11:57:37 +00:00
|
|
|
#!/usr/bin/env python
|
2016-12-07 11:36:51 +00:00
|
|
|
#
|
|
|
|
# SSDV RX GUI
|
|
|
|
#
|
2019-07-27 05:47:12 +00:00
|
|
|
# Copyright (C) 2019 Mark Jessop <vk5qi@rfhead.net>
|
2018-01-25 03:04:43 +00:00
|
|
|
# Released under GNU GPL v3 or later
|
2016-12-07 11:36:51 +00:00
|
|
|
#
|
2019-07-27 05:47:12 +00:00
|
|
|
# TODO:
|
|
|
|
# [x] Make functional under Python 2 & Python 3
|
|
|
|
# [ ] Completely replace with a browser-based interface.
|
|
|
|
#
|
2019-07-27 06:39:08 +00:00
|
|
|
import argparse
|
2019-07-26 14:00:02 +00:00
|
|
|
import logging
|
|
|
|
import json
|
|
|
|
import socket
|
2019-07-27 06:39:08 +00:00
|
|
|
import sys
|
2019-07-26 14:00:02 +00:00
|
|
|
import time
|
2016-12-07 11:36:51 +00:00
|
|
|
from WenetPackets import *
|
2016-03-05 11:57:37 +00:00
|
|
|
from threading import Thread
|
2019-07-26 14:00:02 +00:00
|
|
|
from PyQt5 import QtGui, QtCore, QtWidgets
|
|
|
|
from PyQt5.QtCore import Qt
|
2016-03-05 11:57:37 +00:00
|
|
|
|
2019-07-26 14:00:02 +00:00
|
|
|
try:
|
|
|
|
# Python 2
|
|
|
|
from Queue import Queue
|
|
|
|
except ImportError:
|
|
|
|
# Python 3
|
|
|
|
from queue import Queue
|
2016-03-05 11:57:37 +00:00
|
|
|
|
2019-07-27 05:47:12 +00:00
|
|
|
|
2016-12-08 11:23:36 +00:00
|
|
|
# Auto-resizing Widget, to contain displayed image.
|
2019-07-26 14:00:02 +00:00
|
|
|
class Label(QtWidgets.QLabel):
|
2016-03-05 11:57:37 +00:00
|
|
|
def __init__(self, img):
|
|
|
|
super(Label, self).__init__()
|
2019-07-26 14:00:02 +00:00
|
|
|
self.setFrameStyle(QtWidgets.QFrame.StyledPanel)
|
2016-03-05 11:57:37 +00:00
|
|
|
self.pixmap = QtGui.QPixmap()
|
|
|
|
|
2019-07-26 14:00:02 +00:00
|
|
|
|
2016-03-05 11:57:37 +00:00
|
|
|
def paintEvent(self, event):
|
|
|
|
size = self.size()
|
|
|
|
painter = QtGui.QPainter(self)
|
|
|
|
point = QtCore.QPoint(0,0)
|
|
|
|
scaledPix = self.pixmap.scaled(size, Qt.KeepAspectRatio, transformMode = Qt.SmoothTransformation)
|
|
|
|
# start painting the label from left upper corner
|
|
|
|
point.setX((size.width() - scaledPix.width())/2)
|
|
|
|
point.setY((size.height() - scaledPix.height())/2)
|
|
|
|
painter.drawPixmap(point, scaledPix)
|
|
|
|
|
2019-07-26 14:00:02 +00:00
|
|
|
|
|
|
|
class MyWindow(QtWidgets.QWidget):
|
2016-03-05 11:57:37 +00:00
|
|
|
def __init__(self, parent=None):
|
|
|
|
super(MyWindow, self).__init__(parent)
|
|
|
|
self.label = Label(self)
|
2019-07-26 14:00:02 +00:00
|
|
|
self.statusLabel = QtWidgets.QLabel("SSDV: No data yet.")
|
|
|
|
self.statusLabel.setFixedHeight(20)
|
|
|
|
self.uploaderLabel = QtWidgets.QLabel("Uploader: No Data Yet.")
|
|
|
|
self.uploaderLabel.setFixedHeight(20)
|
|
|
|
self.layout = QtWidgets.QVBoxLayout(self)
|
2016-03-05 11:57:37 +00:00
|
|
|
self.layout.addWidget(self.label)
|
|
|
|
self.layout.addWidget(self.statusLabel)
|
2019-07-26 14:00:02 +00:00
|
|
|
self.layout.addWidget(self.uploaderLabel)
|
|
|
|
self.rxqueue = Queue(32)
|
2016-03-05 11:57:37 +00:00
|
|
|
|
|
|
|
|
|
|
|
@QtCore.pyqtSlot(str)
|
2016-12-08 11:23:36 +00:00
|
|
|
def changeImage(self, pathToImage, text_message):
|
2019-07-26 14:00:02 +00:00
|
|
|
""" Load and display the supplied image, and update the status text field. """
|
|
|
|
logging.debug("New image: %s" % pathToImage)
|
2016-03-05 11:57:37 +00:00
|
|
|
pixmap = QtGui.QPixmap(pathToImage)
|
|
|
|
self.label.pixmap = pixmap
|
2016-12-08 11:23:36 +00:00
|
|
|
self.statusLabel.setText(text_message)
|
2019-07-26 14:00:02 +00:00
|
|
|
self.label.repaint()
|
|
|
|
logging.debug("Re-painted GUI.")
|
|
|
|
|
|
|
|
|
|
|
|
@QtCore.pyqtSlot(str)
|
|
|
|
def update_upload_status(self, queued, uploaded, discarded):
|
|
|
|
self.uploaderLabel.setText("Uploader: %d queued, %d uploaded, %d discarded." % (queued, uploaded, discarded))
|
|
|
|
|
2016-03-05 11:57:37 +00:00
|
|
|
|
|
|
|
def read_queue(self):
|
2019-07-26 14:00:02 +00:00
|
|
|
""" This function is called every 100ms in the QtGui thread. """
|
2016-03-05 11:57:37 +00:00
|
|
|
try:
|
2016-12-08 11:23:36 +00:00
|
|
|
new_packet = self.rxqueue.get_nowait()
|
|
|
|
packet_data = json.loads(new_packet)
|
2019-07-26 14:00:02 +00:00
|
|
|
if 'filename' in packet_data:
|
|
|
|
self.changeImage(packet_data['filename'],packet_data['text'])
|
|
|
|
elif 'uploader_status' in packet_data:
|
|
|
|
self.update_upload_status(packet_data['queued'], packet_data['uploaded'], packet_data['discarded'])
|
2016-03-05 11:57:37 +00:00
|
|
|
except:
|
2019-07-26 14:00:02 +00:00
|
|
|
# If there is nothing in the queue we will get a queue.Empty error, so we just return.
|
2016-03-05 11:57:37 +00:00
|
|
|
pass
|
|
|
|
|
2019-07-26 14:00:02 +00:00
|
|
|
|
2016-03-05 11:57:37 +00:00
|
|
|
# UDP Listener
|
|
|
|
udp_listener_running = False
|
|
|
|
udp_callback = None
|
2019-07-26 14:00:02 +00:00
|
|
|
|
2016-03-05 11:57:37 +00:00
|
|
|
def udp_rx():
|
|
|
|
global udp_listener_running, udp_callback
|
|
|
|
s = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
|
|
|
|
s.settimeout(1)
|
|
|
|
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
2019-07-27 11:05:41 +00:00
|
|
|
try:
|
|
|
|
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
|
|
|
|
except:
|
|
|
|
pass
|
|
|
|
|
2016-12-07 11:36:51 +00:00
|
|
|
s.bind(('',WENET_IMAGE_UDP_PORT))
|
2016-03-05 11:57:37 +00:00
|
|
|
print("Started UDP Listener Thread.")
|
|
|
|
udp_listener_running = True
|
|
|
|
while udp_listener_running:
|
|
|
|
try:
|
|
|
|
m = s.recvfrom(512)
|
|
|
|
except socket.timeout:
|
|
|
|
m = None
|
|
|
|
|
|
|
|
if m != None:
|
2019-07-26 14:00:02 +00:00
|
|
|
try:
|
|
|
|
udp_callback(m[0])
|
|
|
|
except Exception as e:
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
2016-03-05 11:57:37 +00:00
|
|
|
|
|
|
|
print("Closing UDP Listener")
|
|
|
|
s.close()
|
|
|
|
|
2019-07-26 14:00:02 +00:00
|
|
|
|
2016-03-05 11:57:37 +00:00
|
|
|
if __name__ == "__main__":
|
|
|
|
|
2019-07-27 06:39:08 +00:00
|
|
|
parser = argparse.ArgumentParser()
|
|
|
|
parser.add_argument("-v", "--verbose", action='store_true', default=False, help="Verbose output")
|
2024-10-04 03:52:04 +00:00
|
|
|
parser.add_argument("--image_port", type=int, default=None, help="UDP port used for communication between Wenet decoder processes. Default: 7890")
|
2019-07-27 06:39:08 +00:00
|
|
|
args = parser.parse_args()
|
|
|
|
|
|
|
|
if args.verbose:
|
|
|
|
log_level = logging.DEBUG
|
|
|
|
else:
|
|
|
|
log_level = logging.INFO
|
|
|
|
|
|
|
|
logging.basicConfig(format='%(asctime)s %(levelname)s: %(message)s', level=log_level)
|
2019-07-26 14:00:02 +00:00
|
|
|
|
2024-10-04 03:52:04 +00:00
|
|
|
# Overwrite the image UDP port if it has been provided
|
|
|
|
if args.image_port:
|
|
|
|
WENET_IMAGE_UDP_PORT = args.image_port
|
|
|
|
|
2019-07-26 14:00:02 +00:00
|
|
|
app = QtWidgets.QApplication(sys.argv)
|
2016-03-05 11:57:37 +00:00
|
|
|
app.setApplicationName('SSDV Viewer')
|
|
|
|
|
|
|
|
main = MyWindow()
|
|
|
|
main.resize(800,600)
|
2016-11-28 12:37:42 +00:00
|
|
|
main.setWindowTitle("SSDV Viewer")
|
2016-03-05 11:57:37 +00:00
|
|
|
|
|
|
|
udp_callback = main.rxqueue.put_nowait
|
|
|
|
t = Thread(target=udp_rx)
|
|
|
|
t.start()
|
|
|
|
|
|
|
|
timer = QtCore.QTimer()
|
|
|
|
timer.timeout.connect(main.read_queue)
|
|
|
|
timer.start(100)
|
|
|
|
|
|
|
|
main.show()
|
|
|
|
|
|
|
|
app.exec_()
|
|
|
|
|
|
|
|
udp_listener_running = False
|
|
|
|
|
2016-11-28 12:37:42 +00:00
|
|
|
sys.exit()
|