diff --git a/config_files/tmux-sensor-testing.conf b/config_files/tmux-sensor-testing.conf index ef21a6b..fff64ea 100644 --- a/config_files/tmux-sensor-testing.conf +++ b/config_files/tmux-sensor-testing.conf @@ -15,17 +15,17 @@ set -g terminal-overrides 'xterm*:smcup@:rmcup@' new-session -s 'csession' attach-session -t 'csession' -new-window -n 'socat-ekg-merle' -t 'csession:3' 'socat -d -d PTY,raw,echo=0,link=/tmp/ekg2osc-merle-in,b115200,user=stefan PTY,raw,echo=0,link=/tmp/ekg2osc-merle-out,b115200,user=stefan' -new-window -n 'socat-ekg-uwe' -t 'csession:4' 'socat -d -d PTY,raw,echo=0,link=/tmp/ekg2osc-uwe-in,b115200,user=stefan PTY,raw,echo=0,link=/tmp/ekg2osc-uwe-out,b115200,user=stefan' -new-window -n 'socat-ekg-bjoern' -t 'csession:2' 'socat -d -d PTY,raw,echo=0,link=/tmp/ekg2osc-bjoern-in,b115200,user=stefan PTY,raw,echo=0,link=/tmp/ekg2osc-bjoern-out,b115200,user=stefan' +new-window -n 'socat-ekg-merle' -t 'csession:3' 'socat -d -d PTY,raw,echo=0,link=/tmp/ekg2osc-merle-in,b115200,user=sarah PTY,raw,echo=0,link=/tmp/ekg2osc-merle-out,b115200,user=sarah' +new-window -n 'socat-ekg-uwe' -t 'csession:4' 'socat -d -d PTY,raw,echo=0,link=/tmp/ekg2osc-uwe-in,b115200,user=sarah PTY,raw,echo=0,link=/tmp/ekg2osc-uwe-out,b115200,user=sarah' +new-window -n 'socat-ekg-bjoern' -t 'csession:2' 'socat -d -d PTY,raw,echo=0,link=/tmp/ekg2osc-bjoern-in,b115200,user=sarah PTY,raw,echo=0,link=/tmp/ekg2osc-bjoern-out,b115200,user=sarah' -new-window -n 'socat-pulse-merle' -t 'csession:6' 'socat -d -d PTY,raw,echo=0,link=/tmp/pulse2osc-merle-in,b115200,user=stefan PTY,raw,echo=0,link=/tmp/pulse2osc-merle-out,b115200,user=stefan' -new-window -n 'socat-pulse-uwe' -t 'csession:7' 'socat -d -d PTY,raw,echo=0,link=/tmp/pulse2osc-uwe-in,b115200,user=stefan PTY,raw,echo=0,link=/tmp/pulse2osc-uwe-out,b115200,user=stefan' -new-window -n 'socat-pulse-bjoern' -t 'csession:5' 'socat -d -d PTY,raw,echo=0,link=/tmp/pulse2osc-bjoern-in,b115200,user=stefan PTY,raw,echo=0,link=/tmp/pulse2osc-bjoern-out,b115200,user=stefan' +new-window -n 'socat-pulse-merle' -t 'csession:6' 'socat -d -d PTY,raw,echo=0,link=/tmp/pulse2osc-merle-in,b115200,user=sarah PTY,raw,echo=0,link=/tmp/pulse2osc-merle-out,b115200,user=sarah' +new-window -n 'socat-pulse-uwe' -t 'csession:7' 'socat -d -d PTY,raw,echo=0,link=/tmp/pulse2osc-uwe-in,b115200,user=sarah PTY,raw,echo=0,link=/tmp/pulse2osc-uwe-out,b115200,user=sarah' +new-window -n 'socat-pulse-bjoern' -t 'csession:5' 'socat -d -d PTY,raw,echo=0,link=/tmp/pulse2osc-bjoern-in,b115200,user=sarah PTY,raw,echo=0,link=/tmp/pulse2osc-bjoern-out,b115200,user=sarah' -new-window -n 'socat-ehealth-merle' -t 'csession:9' 'socat -d -d PTY,raw,echo=0,link=/tmp/ehealth2osc-merle-in,b115200,user=stefan PTY,raw,echo=0,link=/tmp/ehealth2osc-merle-out,b115200,user=stefan' -new-window -n 'socat-ehealth-uwe' -t 'csession:10' 'socat -d -d PTY,raw,echo=0,link=/tmp/ehealth2osc-uwe-in,b115200,user=stefan PTY,raw,echo=0,link=/tmp/ehealth2osc-uwe-out,b115200,user=stefan' -new-window -n 'socat-ehealth-bjoern' -t 'csession:8' 'socat -d -d PTY,raw,echo=0,link=/tmp/ehealth2osc-bjoern-in,b115200,user=stefan PTY,raw,echo=0,link=/tmp/ehealth2osc-bjoern-out,b115200,user=stefan' +new-window -n 'socat-ehealth-merle' -t 'csession:9' 'socat -d -d PTY,raw,echo=0,link=/tmp/ehealth2osc-merle-in,b115200,user=sarah PTY,raw,echo=0,link=/tmp/ehealth2osc-merle-out,b115200,user=sarah' +new-window -n 'socat-ehealth-uwe' -t 'csession:10' 'socat -d -d PTY,raw,echo=0,link=/tmp/ehealth2osc-uwe-in,b115200,user=sarah PTY,raw,echo=0,link=/tmp/ehealth2osc-uwe-out,b115200,user=sarah' +new-window -n 'socat-ehealth-bjoern' -t 'csession:8' 'socat -d -d PTY,raw,echo=0,link=/tmp/ehealth2osc-bjoern-in,b115200,user=sarah PTY,raw,echo=0,link=/tmp/ehealth2osc-bjoern-out,b115200,user=sarah' new-window -n 'ekg2osc-merle' -t 'csession:11' 'ekgmerle -D /tmp/ekg2osc-merle-out' new-window -n 'ekg2osc-uwe' -t 'csession:12' 'ekguwe -D /tmp/ekg2osc-uwe-out' @@ -39,17 +39,17 @@ new-window -n 'ehealth2osc-merle' -t 'csession:17' 'sleep 1 && ehealthmerle - new-window -n 'ehealth2osc-uwe' -t 'csession:18' 'sleep 1 && ehealthuwe -D /tmp/ehealth2osc-uwe-out' new-window -n 'ehealth2osc-bjoern' -t 'csession:19' 'sleep 1 && ehealthbjoern -D /tmp/ehealth2osc-bjoern-out' -new-window -n 'test-ekg-merle' -t 'csession:21' 'python /home/stefan/dev/psychose/sensors2osc/sensors2osc/socat_ekg_test.py /tmp/ekg2osc-merle-in' -new-window -n 'test-ekg-uwe' -t 'csession:22' 'python /home/stefan/dev/psychose/sensors2osc/sensors2osc/socat_ekg_test.py /tmp/ekg2osc-uwe-in' -new-window -n 'test-ekg-bjoern' -t 'csession:20' 'python /home/stefan/dev/psychose/sensors2osc/sensors2osc/socat_ekg_test.py /tmp/ekg2osc-bjoern-in' +new-window -n 'test-ekg-merle' -t 'csession:21' 'python /home/sarah/dev/psychose/sensors2osc/sensors2osc/socat_ekg_test.py /tmp/ekg2osc-merle-in' +new-window -n 'test-ekg-uwe' -t 'csession:22' 'python /home/sarah/dev/psychose/sensors2osc/sensors2osc/socat_ekg_test.py /tmp/ekg2osc-uwe-in' +new-window -n 'test-ekg-bjoern' -t 'csession:20' 'python /home/sarah/dev/psychose/sensors2osc/sensors2osc/socat_ekg_test.py /tmp/ekg2osc-bjoern-in' -new-window -n 'test-pulse-merle' -t 'csession:24' 'python /home/stefan/dev/psychose/sensors2osc/sensors2osc/socat_pulse_test.py /tmp/pulse2osc-merle-in' -new-window -n 'test-pulse-uwe' -t 'csession:25' 'python /home/stefan/dev/psychose/sensors2osc/sensors2osc/socat_pulse_test.py /tmp/pulse2osc-uwe-in' -new-window -n 'test-pulse-bjoern' -t 'csession:23' 'python /home/stefan/dev/psychose/sensors2osc/sensors2osc/socat_pulse_test.py /tmp/pulse2osc-bjoern-in' +new-window -n 'test-pulse-merle' -t 'csession:24' 'python /home/sarah/dev/psychose/sensors2osc/sensors2osc/socat_pulse_test.py /tmp/pulse2osc-merle-in' +new-window -n 'test-pulse-uwe' -t 'csession:25' 'python /home/sarah/dev/psychose/sensors2osc/sensors2osc/socat_pulse_test.py /tmp/pulse2osc-uwe-in' +new-window -n 'test-pulse-bjoern' -t 'csession:23' 'python /home/sarah/dev/psychose/sensors2osc/sensors2osc/socat_pulse_test.py /tmp/pulse2osc-bjoern-in' -new-window -n 'test-ehealth-merle' -t 'csession:27' 'python /home/stefan/dev/psychose/sensors2osc/sensors2osc/socat_ehealth_test.py /tmp/ehealth2osc-merle-in' -new-window -n 'test-ehealth-uwe' -t 'csession:28' 'python /home/stefan/dev/psychose/sensors2osc/sensors2osc/socat_ehealth_test.py /tmp/ehealth2osc-uwe-in' -new-window -n 'test-ehealth-bjoern' -t 'csession:26' 'python /home/stefan/dev/psychose/sensors2osc/sensors2osc/socat_ehealth_test.py /tmp/ehealth2osc-bjoern-in' +new-window -n 'test-ehealth-merle' -t 'csession:27' 'python /home/sarah/dev/psychose/sensors2osc/sensors2osc/socat_ehealth_test.py /tmp/ehealth2osc-merle-in' +new-window -n 'test-ehealth-uwe' -t 'csession:28' 'python /home/sarah/dev/psychose/sensors2osc/sensors2osc/socat_ehealth_test.py /tmp/ehealth2osc-uwe-in' +new-window -n 'test-ehealth-bjoern' -t 'csession:26' 'python /home/sarah/dev/psychose/sensors2osc/sensors2osc/socat_ehealth_test.py /tmp/ehealth2osc-bjoern-in' select-window -t 'csession:2' diff --git a/dump_grabber/dump_grabber/dump_grabber.ui b/dump_grabber/dump_grabber/dump_grabber.ui index 1da8b65..c70c83a 100644 --- a/dump_grabber/dump_grabber/dump_grabber.ui +++ b/dump_grabber/dump_grabber/dump_grabber.ui @@ -10,421 +10,6 @@ 606 - - - - - - - 255 - 255 - 255 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 255 - 255 - 220 - - - - - - - 0 - 0 - 0 - - - - - - - - - 255 - 255 - 255 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 255 - 255 - 220 - - - - - - - 0 - 0 - 0 - - - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 255 - 255 - 255 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 255 - 255 - 220 - - - - - - - 0 - 0 - 0 - - - - - - Monospace @@ -447,39 +32,428 @@ 580 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + - true + false - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Qt::Vertical - - - - 20 - 4 - - - - diff --git a/dump_grabber/dump_grabber/dump_grabber_ui.py b/dump_grabber/dump_grabber/dump_grabber_ui.py index 33517eb..de1e72a 100644 --- a/dump_grabber/dump_grabber/dump_grabber_ui.py +++ b/dump_grabber/dump_grabber/dump_grabber_ui.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'dump_grabber.ui' # -# Created: Wed Apr 16 22:18:59 2014 +# Created: Tue May 13 06:55:09 2014 # by: PyQt4 UI code generator 4.10.3 # # WARNING! All changes made in this file will be lost! @@ -27,6 +27,19 @@ class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.setObjectName(_fromUtf8("MainWindow")) MainWindow.resize(811, 606) + font = QtGui.QFont() + font.setFamily(_fromUtf8("Monospace")) + font.setPointSize(14) + font.setItalic(True) + MainWindow.setFont(font) + self.centralwidget = QtGui.QWidget(MainWindow) + self.centralwidget.setObjectName(_fromUtf8("centralwidget")) + self.verticalLayout = QtGui.QVBoxLayout(self.centralwidget) + self.verticalLayout.setObjectName(_fromUtf8("verticalLayout")) + self.horizontalLayout = QtGui.QHBoxLayout() + self.horizontalLayout.setObjectName(_fromUtf8("horizontalLayout")) + self.graphics_view = QtGui.QGraphicsView(self.centralwidget) + self.graphics_view.setMinimumSize(QtCore.QSize(785, 580)) palette = QtGui.QPalette() brush = QtGui.QBrush(QtGui.QColor(255, 255, 255)) brush.setStyle(QtCore.Qt.SolidPattern) @@ -163,23 +176,11 @@ class Ui_MainWindow(object): brush = QtGui.QBrush(QtGui.QColor(0, 0, 0)) brush.setStyle(QtCore.Qt.SolidPattern) palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.ToolTipText, brush) - MainWindow.setPalette(palette) - self.centralwidget = QtGui.QWidget(MainWindow) - self.centralwidget.setObjectName(_fromUtf8("centralwidget")) - self.verticalLayout = QtGui.QVBoxLayout(self.centralwidget) - self.verticalLayout.setObjectName(_fromUtf8("verticalLayout")) - self.horizontalLayout = QtGui.QHBoxLayout() - self.horizontalLayout.setObjectName(_fromUtf8("horizontalLayout")) - self.graphics_view = QtGui.QGraphicsView(self.centralwidget) - self.graphics_view.setMinimumSize(QtCore.QSize(785, 580)) - self.graphics_view.setAutoFillBackground(True) + self.graphics_view.setPalette(palette) + self.graphics_view.setAutoFillBackground(False) self.graphics_view.setObjectName(_fromUtf8("graphics_view")) self.horizontalLayout.addWidget(self.graphics_view) - spacerItem = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) - self.horizontalLayout.addItem(spacerItem) self.verticalLayout.addLayout(self.horizontalLayout) - spacerItem1 = QtGui.QSpacerItem(20, 4, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) - self.verticalLayout.addItem(spacerItem1) MainWindow.setCentralWidget(self.centralwidget) self.retranslateUi(MainWindow) diff --git a/dump_grabber/dump_grabber/main.py b/dump_grabber/dump_grabber/main.py index 5c78bcb..06d0701 100644 --- a/dump_grabber/dump_grabber/main.py +++ b/dump_grabber/dump_grabber/main.py @@ -1,7 +1,6 @@ #!/usr/bin/python # -*- coding: utf-8 -*- - # This file is part of chaosc and psychosis # # chaosc is free software: you can redistribute it and/or modify @@ -21,24 +20,20 @@ from __future__ import absolute_import -import logging import os import os.path -import Queue import re -import select -import socket import sys -import threading -import time from datetime import datetime -from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer from chaosc.argparser_groups import * from chaosc.lib import logger, resolve_host from PyQt4 import QtCore, QtGui from PyQt4.QtCore import QBuffer, QByteArray, QIODevice +from PyQt4.QtNetwork import QTcpServer, QTcpSocket, QUdpSocket, QHostAddress + from dump_grabber.dump_grabber_ui import Ui_MainWindow +from psylib.mjpeg_streaming_server import MjpegStreamingServer try: from chaosc.c_osc_lib import OSCMessage, decode_osc @@ -68,7 +63,7 @@ class ColumnTextStorage(TextStorage): self.column_width = column_width self.line_height = line_height self.graphics_scene = scene - self.num_lines, self.offset = divmod(775, self.line_height) + self.num_lines, self.offset = divmod(768, self.line_height) def init_columns(self): for x in range(self.column_count): @@ -102,7 +97,7 @@ class ExclusiveTextStorage(TextStorage): self.column_width = column_width self.line_height = line_height self.graphics_scene = scene - self.num_lines, self.offset = divmod(775, self.line_height) + self.num_lines, self.offset = divmod(576, self.line_height) def init_columns(self): color = self.colors[0] @@ -125,10 +120,17 @@ class ExclusiveTextStorage(TextStorage): text_item.setY(iy * self.line_height) + + + class MainWindow(QtGui.QMainWindow, Ui_MainWindow): - def __init__(self, parent=None, columns=3, column_exclusive=False): + def __init__(self, args, parent=None, columns=3, column_exclusive=False): super(MainWindow, self).__init__(parent) + self.args = args + self.setupUi(self) + self.http_server = MjpegStreamingServer((args.http_host, args.http_port), self) + self.http_server.listen(port=args.http_port) self.graphics_view.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) self.graphics_view.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) self.graphics_view.setRenderHint(QtGui.QPainter.Antialiasing, True) @@ -140,178 +142,78 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): self.default_font.setStyleHint(QtGui.QFont.Monospace) self.default_font.setBold(True) self.graphics_scene.setFont(self.default_font) - self.font_metrics = QtGui.QFontMetrics(self.default_font) self.line_height = self.font_metrics.height() self.column_width = 775 / columns self.text_storage = ExclusiveTextStorage(columns, self.default_font, self.column_width, self.line_height, self.graphics_scene) - #self.text_storage = ColumnTextStorage(columns, self.default_font, self.column_width, self.line_height, self.graphics_scene) self.text_storage.init_columns() + self.osc_sock = QUdpSocket(self) + logger.info("osc bind localhost %d", args.client_port) + self.osc_sock.bind(QHostAddress("127.0.0.1"), args.client_port) + self.osc_sock.readyRead.connect(self.got_message) + self.osc_sock.error.connect(self.handle_osc_error) + msg = OSCMessage("/subscribe") + msg.appendTypedArg("localhost", "s") + msg.appendTypedArg(args.client_port, "i") + msg.appendTypedArg(self.args.authenticate, "s") + if self.args.subscriber_label is not None: + msg.appendTypedArg(self.args.subscriber_label, "s") + self.osc_sock.writeDatagram(QByteArray(msg.encode_osc()), QHostAddress("127.0.0.1"), 7110) + #self.add_text(0, "foo bar") + + self.regex = re.compile("^/(uwe|merle|bjoern)/(.*?)$") + + def closeEvent(self, event): + msg = OSCMessage("/unsubscribe") + msg.appendTypedArg("localhost", "s") + msg.appendTypedArg(self.args.client_port, "i") + msg.appendTypedArg(self.args.authenticate, "s") + self.osc_sock.writeDatagram(QByteArray(msg.encode_osc()), QHostAddress("127.0.0.1"), 7110) + + def handle_osc_error(self, error): + logger.info("osc socket error %d", error) + def add_text(self, column, text): self.text_storage.add_text(column, text) - def render(self): + def render_image(self): image = QtGui.QImage(768, 576, QtGui.QImage.Format_ARGB32_Premultiplied) image.fill(QtCore.Qt.black) painter = QtGui.QPainter(image) - painter.setRenderHints(QtGui.QPainter.RenderHint(QtGui.QPainter.Antialiasing | QtGui.QPainter.TextAntialiasing), True) + painter.setRenderHints(QtGui.QPainter.RenderHint( + QtGui.QPainter.Antialiasing | QtGui.QPainter.TextAntialiasing), + True) painter.setFont(self.default_font) - self.graphics_view.render(painter, target=QtCore.QRectF(0,0,768,576),source=QtCore.QRect(0,0,768,576)) + self.graphics_view.render(painter, target=QtCore.QRectF(0, 0, 768, 576), + source=QtCore.QRect(0, 0, 768, 576)) painter.end() - return image + buf = QBuffer() + buf.open(QIODevice.WriteOnly) + image.save(buf, "JPG", 80) + image_data = buf.data() + return image_data - -class OSCThread(threading.Thread): - def __init__(self, args): - super(OSCThread, self).__init__() - self.args = args - self.running = True - - self.client_address = resolve_host(args.client_host, args.client_port, args.address_family) - - self.chaosc_address = chaosc_host, chaosc_port = resolve_host(args.chaosc_host, args.chaosc_port, args.address_family) - - self.osc_sock = socket.socket(args.address_family, 2, 17) - self.osc_sock.bind(self.client_address) - self.osc_sock.setblocking(0) - - logger.info("starting up osc receiver on '%s:%d'", self.client_address[0], self.client_address[1]) - - self.subscribe_me() - - def subscribe_me(self): - logger.info("%s: subscribing to '%s:%d' with label %r", datetime.now().strftime("%x %X"), self.chaosc_address[0], self.chaosc_address[1], self.args.subscriber_label) - msg = OSCMessage("/subscribe") - msg.appendTypedArg(self.client_address[0], "s") - msg.appendTypedArg(self.client_address[1], "i") - msg.appendTypedArg(self.args.authenticate, "s") - if self.args.subscriber_label is not None: - msg.appendTypedArg(self.args.subscriber_label, "s") - self.osc_sock.sendto(msg.encode_osc(), self.chaosc_address) - - - def unsubscribe_me(self): - if self.args.keep_subscribed: - return - - logger.info("unsubscribing from '%s:%d'", self.chaosc_address[0], self.chaosc_address[1]) - msg = OSCMessage("/unsubscribe") - msg.appendTypedArg(self.client_address[0], "s") - msg.appendTypedArg(self.client_address[1], "i") - msg.appendTypedArg(self.args.authenticate, "s") - self.osc_sock.sendto(msg.encode_osc(), self.chaosc_address) - - def run(self): - - while self.running: + def got_message(self): + while self.osc_sock.hasPendingDatagrams(): + data, address, port = self.osc_sock.readDatagram(self.osc_sock.pendingDatagramSize()) try: - reads, writes, errs = select.select([self.osc_sock], [], [], 0.01) - except Exception, e: + osc_address, typetags, args = decode_osc(data, 0, len(data)) + except Exception: + return + try: + actor, text = self.regex.match(osc_address).groups() + except AttributeError: pass else: - if reads: - try: - osc_input, address = self.osc_sock.recvfrom(8192) - osc_address, typetags, messages = decode_osc(osc_input, 0, len(osc_input)) - queue.put_nowait((osc_address, messages)) - except Exception, e: - pass - else: - pass - - self.unsubscribe_me() - logger.info("OSCThread is going down") - - -queue = Queue.Queue() - -class MyHandler(BaseHTTPRequestHandler): - - def do_GET(self): - - try: - self.path=re.sub('[^.a-zA-Z0-9]', "",str(self.path)) - if self.path=="" or self.path==None or self.path[:1]==".": - self.send_error(403,'Forbidden') - - if self.path.endswith(".html"): - directory = os.path.dirname(os.path.abspath(__file__)) - data = open(os.path.join(directory, self.path), "rb").read() - self.send_response(200) - self.send_header('Content-type', 'text/html') - self.end_headers() - self.wfile.write(data) - elif self.path.endswith(".mjpeg"): - self.thread = thread = OSCThread(self.server.args) - thread.daemon = True - thread.start() - window = MainWindow() - window.hide() - - self.send_response(200) - self.send_header("Content-Type", "multipart/x-mixed-replace; boundary=--aaboundary") - self.end_headers() - - event_loop = QtCore.QEventLoop() - last_frame = time.time() - 1. - frame_rate = 16.0 - frame_length = 1. / frame_rate - regex = re.compile("^/(uwe|merle|bjoern)/(.*?)$") - while 1: - event_loop.processEvents() - app.sendPostedEvents(None, 0) - while 1: - try: - osc_address, args = queue.get_nowait() - print osc_address - except Queue.Empty: - break - else: - try: - actor, text = regex.match(osc_address).groups() - if actor == "merle": - window.add_text(0, "%s = %s" % (text, ", ".join([str(i) for i in args]))) - if actor == "uwe": - window.add_text(1, "%s = %s" % (text, ", ".join([str(i) for i in args]))) - if actor == "bjoern": - window.add_text(2, "%s = %s" % (text, ", ".join([str(i) for i in args]))) - except AttributeError: - pass - - now = time.time() - delta = now - last_frame - if delta > frame_length: - last_frame = now - img = window.render() - buffer = QBuffer() - buffer.open(QIODevice.WriteOnly) - img.save(buffer, "JPG") - JpegData = buffer.data() - self.wfile.write("--aaboundary\r\nContent-Type: image/jpeg\r\nContent-length: %d\r\n\r\n%s\r\n\r\n\r\n" % (len(JpegData), JpegData)) - JpegData = None - buffer = None - img = None - time.sleep(0.01) - return - except (KeyboardInterrupt, SystemError): - if hasattr(self, "thread") and self.thread is not None: - self.thread.running = False - self.thread.join() - self.thread = None - except IOError, e: - if e[0] in (32, 104): - if hasattr(self, "thread") and self.thread is not None: - self.thread.running = False - self.thread.join() - self.thread = None - else: - pass - - -class JustAHTTPServer(HTTPServer): - pass + if actor == "merle": + self.add_text(0, "%s = %s" % (text, ", ".join([str(i) for i in args]))) + elif actor == "uwe": + self.add_text(1, "%s = %s" % (text, ", ".join([str(i) for i in args]))) + elif actor == "bjoern": + self.add_text(2, "%s = %s" % (text, ", ".join([str(i) for i in args]))) + return True def main(): @@ -327,13 +229,10 @@ def main(): args = arg_parser.finalize() http_host, http_port = resolve_host(args.http_host, args.http_port, args.address_family) + window = MainWindow(args) + #window.show() + app.exec_() - server = JustAHTTPServer((http_host, http_port), MyHandler) - server.address_family = args.address_family - server.args = args - logger.info("starting up http server on '%s:%d'", http_host, http_port) - - server.serve_forever() if ( __name__ == '__main__' ): main() diff --git a/ekgplotter/ekgplotter/main_qt.py b/ekgplotter/ekgplotter/main_qt.py new file mode 100644 index 0000000..bec7536 --- /dev/null +++ b/ekgplotter/ekgplotter/main_qt.py @@ -0,0 +1,312 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# This file is part of sensors2osc package +# +# sensors2osc is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# sensors2osc is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with sensors2osc. If not, see . +# +# found the mjpeg part here, thanks for the nice code :) +# http://hardsoftlucid.wordpress.com/2013/04/11/mjpeg-server-for-webcam-in-python-with-opencv/ +# the osc integration stuff is implemented by me +# +# Copyright (C) 2014 Stefan KУЖgl + +from __future__ import absolute_import + +from chaosc.argparser_groups import * +from chaosc.lib import logger, resolve_host +from datetime import datetime +from operator import attrgetter +from PyQt4 import QtGui, QtCore +from PyQt4.QtCore import QBuffer, QByteArray, QIODevice +from PyQt4.QtNetwork import QTcpServer, QTcpSocket, QUdpSocket, QHostAddress +from PyQt4.QtGui import QPixmap + +import logging +import numpy as np +import os.path +import pyqtgraph as pg +from pyqtgraph.widgets.PlotWidget import PlotWidget +import Queue +import re +import select +import socket +import threading +import time + +from psylib.mjpeg_streaming_server import MjpegStreamingServer + + +try: + from chaosc.c_osc_lib import OSCMessage, decode_osc +except ImportError as e: + logging.exception(e) + from chaosc.osc_lib import OSCMessage, decode_osc + + +def get_steps(pulse_rate, rate): + beat_length = 60. / pulse_rate + steps_pre = int(beat_length / rate) + 1 + used_sleep_time = beat_length / steps_pre + steps = int(beat_length / used_sleep_time) + return steps, used_sleep_time + + +class Generator(object): + def __init__(self, pulse=92, delta=0.08): + self.count = 0 + self.pulse = 92 + self.delta = delta + self.steps = get_steps(self.pulse, delta / 2) + + def __call__(self): + while 1: + value = random.randint(0, steps) + if self.count < int(steps / 100. * 20): + value = random.randint(0,20) + elif self.count < int(steps / 100. * 30): + value = random.randint(20, 30) + elif self.count < int(steps / 100. * 40): + value = random.randint(30,100) + elif self.count < int(steps / 2.): + value = random.randint(100,200) + elif self.count == int(steps / 2.): + value = 255 + elif self.count < int(steps / 100. * 60): + value = random.randint(100, 200) + elif self.count < int(steps / 100. * 70): + value = random.randint(50, 100) + elif self.count < int(steps / 100. * 80): + value = random.randint(20, 50) + elif self.count <= steps: + value = random.randint(0,20) + elif self.count >= steps: + self.count = 0 + + self.count += 1 + yield value + + def retrigger(self): + self.count = self.steps / 2 + + +class Actor(object): + def __init__(self, name, num_data, color, ix, max_actors, actor_height): + self.name = name + self.num_data = num_data + self.color = color + self.ix = ix + self.max_actors = max_actors + self.actor_height = actor_height + self.updated = 0 + + self.offset = ix * actor_height + self.data = np.array([self.offset] * num_data) + self.head = 0 + self.pre_head = 0 + self.plotItem = pg.PlotCurveItem(pen=pg.mkPen(color, width=3), name=name) + self.plotPoint = pg.ScatterPlotItem(pen=pg.mkPen("w", width=5), brush=pg.mkBrush(color), size=5) + + def __str__(self): + return "" % (self.name, self.active, self.head) + + __repr__ = __str__ + + + def add_value(self, value): + dp = self.head + self.data[dp] = value / self.max_actors + self.offset + self.pre_head = dp + self.head = (dp + 1) % self.num_data + self.updated += 1 + + def fill_missing(self, count): + dp = self.head + for i in range(count): + self.data[dp] = self.offset + dp = (dp + 1) % self.num_data + self.updated += 1 + + self.pre_head = (dp - 1) % self.num_data + self.head = dp + + def render(self): + self.plotItem.setData(y=self.data, clear=True) + self.plotPoint.setData(x=[self.pre_head], y = [self.data[self.pre_head]]) + + +class EkgPlotWidget(PlotWidget): + def __init__(self, args, parent=None): + super(EkgPlotWidget, self).__init__(parent) + self.args = args + + self.http_server = MjpegStreamingServer((args.http_host, args.http_port), self) + self.http_server.listen(port=args.http_port) + + self.osc_sock = QUdpSocket(self) + logger.info("osc bind localhost %d", args.client_port) + self.osc_sock.bind(QHostAddress("127.0.0.1"), args.client_port) + self.osc_sock.readyRead.connect(self.got_message) + self.osc_sock.error.connect(self.handle_osc_error) + msg = OSCMessage("/subscribe") + msg.appendTypedArg("localhost", "s") + msg.appendTypedArg(args.client_port, "i") + msg.appendTypedArg(self.args.authenticate, "s") + if self.args.subscriber_label is not None: + msg.appendTypedArg(self.args.subscriber_label, "s") + self.osc_sock.writeDatagram(QByteArray(msg.encode_osc()), QHostAddress("127.0.0.1"), 7110) + self.num_data = 100 + + self.hide() + self.showGrid(False, False) + self.setYRange(0, 255) + self.setXRange(0, self.num_data) + self.resize(768, 576) + + + colors = ["r", "g", "b"] + + ba = self.getAxis("bottom") + bl = self.getAxis("left") + ba.setTicks([]) + bl.setTicks([]) + ba.hide() + bl.hide() + self.active_actors = list() + + self.actors = dict() + self.lengths1 = [0] + + self.max_value = 255 + actor_names = ["merle", "uwe", "bjoern" ] + self.max_actors = len(actor_names) + self.actor_height = self.max_value / self.max_actors + + for ix, (actor_name, color) in enumerate(zip(actor_names, colors)): + self.add_actor(actor_name, self.num_data, color, ix, self.max_actors, self.actor_height) + + self.set_positions() + + self.ekg_regex = re.compile("^/(.*?)/ekg$") + self.ctl_regex = re.compile("^/plot/(.*?)$") + self.updated_actors = set() + self.new_round() + + + def add_actor(self, actor_name, num_data, color, ix, max_actors, actor_height): + actor_obj = Actor(actor_name, num_data, color, ix, max_actors, actor_height) + self.actors[actor_name] = actor_obj + self.addItem(actor_obj.plotItem) + self.addItem(actor_obj.plotPoint) + self.active_actors.append(actor_obj) + + + def set_positions(self): + for ix, actor_obj in enumerate(self.active_actors): + actor_obj.plotItem.setPos(0, ix * 2) + actor_obj.plotPoint.setPos(0, ix * 2) + + def active_actor_count(self): + return self.max_actors + + def new_round(self): + for ix, actor in enumerate(self.active_actors): + actor.updated = 0 + + def update_missing_actors(self): + liste = sorted(self.active_actors, key=attrgetter("updated")) + max_values = liste[-1].updated + if max_values == 0: + # handling no signal + for actor in self.active_actors: + actor.add_value(0) + return + for ix, actor in enumerate(self.active_actors): + diff = max_values - actor.updated + if diff > 0: + for i in range(diff): + actor.add_value(0) + + + def update(self, osc_address, value): + + res = self.ekg_regex.match(osc_address) + if res: + actor_name = res.group(1) + actor_obj = self.actors[actor_name] + actor_obj.add_value(value) + + + def render(self): + for ix, actor in enumerate(self.active_actors): + actor.render() + + def closeEvent(self, event): + msg = OSCMessage("/unsubscribe") + msg.appendTypedArg("localhost", "s") + msg.appendTypedArg(self.args.client_port, "i") + msg.appendTypedArg(self.args.authenticate, "s") + self.osc_sock.writeDatagram(QByteArray(msg.encode_osc()), QHostAddress("127.0.0.1"), 7110) + + def handle_osc_error(self, error): + logger.info("osc socket error %d", error) + + def render_image(self): + self.update_missing_actors() + self.render() + exporter = pg.exporters.ImageExporter.ImageExporter(self.plotItem) + exporter.parameters()['width'] = 768 + img = exporter.export(toBytes=True) + buf = QBuffer() + buf.open(QIODevice.WriteOnly) + img.save(buf, "JPG", 75) + JpegData = buf.data() + self.new_round() + return JpegData + + def got_message(self): + while self.osc_sock.hasPendingDatagrams(): + data, address, port = self.osc_sock.readDatagram(self.osc_sock.pendingDatagramSize()) + try: + osc_address, typetags, args = decode_osc(data, 0, len(data)) + except Exception, e: + logger.exception(e) + return + else: + self.update(osc_address, args[0]) + return True + + + +def main(): + arg_parser = ArgParser("ekgplotter") + arg_parser.add_global_group() + client_group = arg_parser.add_client_group() + arg_parser.add_argument(client_group, '-x', "--http_host", default="::", + help='my host, defaults to "::"') + arg_parser.add_argument(client_group, '-X', "--http_port", default=9000, + type=int, help='my port, defaults to 9000') + arg_parser.add_chaosc_group() + arg_parser.add_subscriber_group() + args = arg_parser.finalize() + + args.http_host, args.http_port = resolve_host(args.http_host, args.http_port, args.address_family) + + qtapp = QtGui.QApplication([]) + widget = EkgPlotWidget(args) + qtapp.exec_() + + +if __name__ == '__main__': + main() diff --git a/ekgplotter/setup.py b/ekgplotter/setup.py index 3cb5b9a..85ac4de 100644 --- a/ekgplotter/setup.py +++ b/ekgplotter/setup.py @@ -30,7 +30,7 @@ setup( # predefined extension points, e.g. for plugins entry_points = """ [console_scripts] - ekgplotter = ekgplotter.main:main + ekgplotter = ekgplotter.main_qt:main """, # pypi metadata author = "Stefan KУЖgl", diff --git a/sensors2osc/sensors2osc/common.py b/sensors2osc/sensors2osc/common.py index d63531f..6779cab 100644 --- a/sensors2osc/sensors2osc/common.py +++ b/sensors2osc/sensors2osc/common.py @@ -28,6 +28,7 @@ import time import sys from chaosc.argparser_groups import ArgParser +from chaosc.lib import logger try: @@ -47,8 +48,8 @@ class Platform(object): def connect(self): - print "connect serial" - print "waiting for the device %r to come up" % self.args.device + logger.info("connect serial") + logger.info("waiting for the device %r to come up", self.args.device) self.serial_sock = serial.Serial() self.serial_sock.port = self.args.device self.serial_sock.baudrate = 115200 @@ -57,7 +58,7 @@ class Platform(object): try: self.serial_sock.open() except (serial.serialutil.SerialException, os.error), e: - print "serial error", e + logger.exception(e) time.sleep(0.5) pass else: @@ -66,12 +67,12 @@ class Platform(object): def close(self): if self.serial_sock is not None: - print "close serial" + logger.info("close serial") self.serial_sock.close() def reconnect(self): - print "reconnect serial" + logger.info("reconnect serial") self.close() self.connect() diff --git a/sensors2osc/sensors2osc/ehealth2osc.py b/sensors2osc/sensors2osc/ehealth2osc.py index df7819b..e65fc54 100644 --- a/sensors2osc/sensors2osc/ehealth2osc.py +++ b/sensors2osc/sensors2osc/ehealth2osc.py @@ -39,20 +39,19 @@ def main(): #print repr(data) except (socket.error, serial.serialutil.SerialException), msg: # got disconnected? - print "serial socket error!!!", msg + logger.exception(msg) platform.reconnect() - print "data", repr(data) try: airFlow, emg, temp = data.split(";") - except ValueError, e: - print e + except ValueError, msg: + logger.exception(msg) continue try: airFlow = int(airFlow) - except ValueError, e: - print e + except ValueError, msg: + logger.exception(msg) continue try: @@ -60,14 +59,14 @@ def main(): osc_message.appendTypedArg(airFlow, "i") platform.osc_sock.sendto(osc_message.encode_osc(), platform.remote) except socket.error, msg: - print "cannot connect to chaosc", msg + logger.exception(msg) continue try: emg = int(emg) - except ValueError, e: - print e + except ValueError, msg: + logger.exception(msg) continue try: @@ -75,14 +74,14 @@ def main(): osc_message.appendTypedArg(emg, "i") platform.osc_sock.sendto(osc_message.encode_osc(), platform.remote) except socket.error, msg: - print "cannot connect to chaosc", msg + logger.exception(msg) continue try: temp = int(temp) - except ValueError, e: - print e + except ValueError, msg: + logger.exception(msg) continue try: @@ -90,7 +89,7 @@ def main(): osc_message.appendTypedArg(temp, "i") platform.osc_sock.sendto(osc_message.encode_osc(), platform.remote) except socket.error, msg: - print "cannot connect to chaosc", msg + logger.exception(msg) continue diff --git a/sensors2osc/sensors2osc/ekg2osc.py b/sensors2osc/sensors2osc/ekg2osc.py index 2517af5..fcee877 100644 --- a/sensors2osc/sensors2osc/ekg2osc.py +++ b/sensors2osc/sensors2osc/ekg2osc.py @@ -51,11 +51,11 @@ def main(): except TypeError, e: continue - if msg_count >= 20: - logger.info("value = %d", t) - msg_count = 0 - else: - msg_count += 1 + #if msg_count >= 20: + # logger.info("value = %d", t) + # msg_count = 0 + #else: + # msg_count += 1 try: osc_message = OSCMessage("/%s/ekg" % actor) diff --git a/sensors2osc/sensors2osc/main.py b/sensors2osc/sensors2osc/main.py index fa5545d..01a0606 100644 --- a/sensors2osc/sensors2osc/main.py +++ b/sensors2osc/sensors2osc/main.py @@ -29,7 +29,6 @@ import datetime try: from chaosc.c_osc_lib import OSCMessage except ImportError as e: - print(e) from chaosc.osc_lib import OSCMessage @@ -48,7 +47,7 @@ class Forwarder(object): def close(self): """Close all resources and unpublish service""" - print "%s: closing..." % (self.device, ) + logger.info("%s: closing...", self.device) self.serial.close() @@ -58,7 +57,6 @@ class EHealth2OSC(Forwarder): def handle_read(self, osc_sock): data = self.serial.readline()[:-2] - print repr(data) try: airFlow, emg, temp = data.split(";") except ValueError: @@ -106,7 +104,7 @@ class RingBuffer(object): self.head = (self.head + 1) % self.length def getData(self): - print "getData", self.ring_buf, self.head + #print "getData", self.ring_buf, self.head data = list() for i in range(7, 1, -1): value = self.ring_buf[(self.head - i) % self.length] @@ -146,7 +144,7 @@ class Pulse2OSC(Forwarder): osc_message.appendTypedArg(heart_rate, "i") osc_message.appendTypedArg(o2, "i") osc_sock.sendall(osc_message.encode_osc()) - print "heartbeat", datetime.datetime.now(), heart_signal + #print "heartbeat", datetime.datetime.now(), heart_signal self.heartbeat_on = True elif pulse == 1 and self.heartbeat_on: #print "off heartbeat", datetime.datetime.now(), heart_signal diff --git a/sensors2osc/sensors2osc/pulse2osc.py b/sensors2osc/sensors2osc/pulse2osc.py index c0b8ad6..0746acc 100644 --- a/sensors2osc/sensors2osc/pulse2osc.py +++ b/sensors2osc/sensors2osc/pulse2osc.py @@ -41,7 +41,7 @@ class RingBuffer(object): self.head = (self.head + 1) % self.length def getData(self): - print "getData", self.ring_buf, self.head + #print "getData", self.ring_buf, self.head data = list() for i in range(self.length + 1, 1, -1): value = self.ring_buf[(self.head - i) % self.length] @@ -52,7 +52,7 @@ class RingBuffer(object): raise ValueError("not complete - reset ringbuffer") data.append(value) if data[0] != 0x0 or data[1] != 0xff: - print "issue", data + #print "issue", data self.reset() self.ring_buf[0] = 0 self.head = 1 @@ -77,7 +77,7 @@ def main(): continue except (socket.error, serial.serialutil.SerialException), msg: # got disconnected? - print "serial socket error!!!", msg + logger.exception(msg) platform.reconnect() try: @@ -91,8 +91,8 @@ def main(): if t == 0: try: heart_signal, heart_rate, o2, pulse = buf.getData() - except ValueError, e: - print e + except ValueError, msg: + logger.exception(msg) continue if pulse == 245 and not heartbeat_on: @@ -103,12 +103,12 @@ def main(): osc_message.appendTypedArg(heart_rate, "i") osc_message.appendTypedArg(o2, "i") platform.osc_sock.sendto(osc_message.encode_osc(), platform.remote) - print "on heartbeat", datetime.now(), heart_signal, heart_rate, o2, pulse + #print "on heartbeat", datetime.now(), heart_signal, heart_rate, o2, pulse except socket.error, msg: - print "cannot connect to chaosc" + logger.exception(msg) continue elif pulse == 1 and heartbeat_on: - print "off heartbeat", datetime.now(), heart_signal, heart_rate, o2, pulse + #print "off heartbeat", datetime.now(), heart_signal, heart_rate, o2, pulse heartbeat_on = False try: osc_message = OSCMessage("/%s/heartbeat" % actor) @@ -117,7 +117,7 @@ def main(): osc_message.appendTypedArg(o2, "i") platform.osc_sock.sendto(osc_message.encode_osc(), platform.remote) except socket.error, msg: - print "cannot connect to chaosc" + logger.exception(msg) continue diff --git a/texter/texter/448_texter.db b/texter/texter/448_texter.db deleted file mode 100644 index 2604dd1..0000000 Binary files a/texter/texter/448_texter.db and /dev/null differ diff --git a/texter/texter/edit_dialog.ui b/texter/texter/edit_dialog.ui index 7c587ed..64b71c4 100644 --- a/texter/texter/edit_dialog.ui +++ b/texter/texter/edit_dialog.ui @@ -15,481 +15,445 @@ - - - - 768 - 576 - - - - - 16777215 - 576 - - - - Qt::Horizontal - - - - - 1 - 0 - - - - - 200 - 576 - - - - - 16777215 - 576 - - - - - - - 100 - 0 - - - - - 0 - 576 - - - - - 768 - 576 - - - - - - - - - 255 - 255 - 255 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 255 - 255 - 220 - - - - - - - 0 - 0 - 0 - - - - - - - - - 255 - 255 - 255 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 255 - 255 - 220 - - - - - - - 0 - 0 - 0 - - - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 255 - 255 - 255 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - 255 - 255 - 220 - - - - - - - 0 - 0 - 0 - - - - - - - - true - - - + + + + + + + + + 100 + 0 + + + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + + false + + + true + + + Qt::NoTextInteraction + + + + @@ -526,11 +490,6 @@ QPushButton
karrowbutton.h
- - KRichTextEdit - KTextEdit -
krichtextedit.h
-
KButtonGroup QGroupBox @@ -542,16 +501,6 @@ QPushButton
kpushbutton.h
- - KTextEdit - QTextEdit -
ktextedit.h
-
- - KRichTextWidget - KRichTextEdit -
krichtextwidget.h
-
diff --git a/texter/texter/text_sorter_ui.py b/texter/texter/edit_dialog_ui.py similarity index 85% rename from texter/texter/text_sorter_ui.py rename to texter/texter/edit_dialog_ui.py index 1a0f767..9dfbd4f 100644 --- a/texter/texter/text_sorter_ui.py +++ b/texter/texter/edit_dialog_ui.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- -# Form implementation generated from reading ui file 'texter4.ui' +# Form implementation generated from reading ui file 'edit_dialog.ui' # -# Created: Mon Apr 28 21:58:51 2014 +# Created: Sat May 17 16:15:38 2014 # by: PyQt4 UI code generator 4.10.3 # # WARNING! All changes made in this file will be lost! @@ -23,32 +23,23 @@ except AttributeError: def _translate(context, text, disambig): return QtGui.QApplication.translate(context, text, disambig) -class Ui_TextSorterDialog(object): - def setupUi(self, TextSorterDialog): - TextSorterDialog.setObjectName(_fromUtf8("TextSorterDialog")) - TextSorterDialog.resize(1084, 633) - self.verticalLayout = QtGui.QVBoxLayout(TextSorterDialog) +class Ui_EditDialog(object): + def setupUi(self, EditDialog): + EditDialog.setObjectName(_fromUtf8("EditDialog")) + EditDialog.resize(1084, 633) + self.verticalLayout = QtGui.QVBoxLayout(EditDialog) self.verticalLayout.setObjectName(_fromUtf8("verticalLayout")) - self.splitter = QtGui.QSplitter(TextSorterDialog) - self.splitter.setOrientation(QtCore.Qt.Horizontal) - self.splitter.setObjectName(_fromUtf8("splitter")) - self.text_list = QtGui.QListView(self.splitter) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding) - sizePolicy.setHorizontalStretch(1) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.text_list.sizePolicy().hasHeightForWidth()) - self.text_list.setSizePolicy(sizePolicy) - self.text_list.setMinimumSize(QtCore.QSize(200, 576)) - self.text_list.setMaximumSize(QtCore.QSize(16777215, 576)) + self.horizontalLayout_2 = QtGui.QHBoxLayout() + self.horizontalLayout_2.setObjectName(_fromUtf8("horizontalLayout_2")) + self.text_list = QtGui.QTableView(EditDialog) self.text_list.setObjectName(_fromUtf8("text_list")) - self.text_preview = KRichTextWidget(self.splitter) + self.horizontalLayout_2.addWidget(self.text_list) + self.text_preview = QtGui.QTextEdit(EditDialog) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding) sizePolicy.setHorizontalStretch(100) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.text_preview.sizePolicy().hasHeightForWidth()) self.text_preview.setSizePolicy(sizePolicy) - self.text_preview.setMinimumSize(QtCore.QSize(0, 576)) - self.text_preview.setMaximumSize(QtCore.QSize(768, 576)) palette = QtGui.QPalette() brush = QtGui.QBrush(QtGui.QColor(255, 255, 255)) brush.setStyle(QtCore.Qt.SolidPattern) @@ -186,10 +177,13 @@ class Ui_TextSorterDialog(object): brush.setStyle(QtCore.Qt.SolidPattern) palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.ToolTipText, brush) self.text_preview.setPalette(palette) + self.text_preview.setUndoRedoEnabled(False) self.text_preview.setReadOnly(True) + self.text_preview.setTextInteractionFlags(QtCore.Qt.NoTextInteraction) self.text_preview.setObjectName(_fromUtf8("text_preview")) - self.verticalLayout.addWidget(self.splitter) - self.kbuttongroup = KButtonGroup(TextSorterDialog) + self.horizontalLayout_2.addWidget(self.text_preview) + self.verticalLayout.addLayout(self.horizontalLayout_2) + self.kbuttongroup = KButtonGroup(EditDialog) self.kbuttongroup.setObjectName(_fromUtf8("kbuttongroup")) self.horizontalLayout = QtGui.QHBoxLayout(self.kbuttongroup) self.horizontalLayout.setObjectName(_fromUtf8("horizontalLayout")) @@ -206,14 +200,12 @@ class Ui_TextSorterDialog(object): self.remove_button.setObjectName(_fromUtf8("remove_button")) self.horizontalLayout.addWidget(self.remove_button) self.verticalLayout.addWidget(self.kbuttongroup) - spacerItem = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) - self.verticalLayout.addItem(spacerItem) - self.retranslateUi(TextSorterDialog) - QtCore.QMetaObject.connectSlotsByName(TextSorterDialog) + self.retranslateUi(EditDialog) + QtCore.QMetaObject.connectSlotsByName(EditDialog) - def retranslateUi(self, TextSorterDialog): - TextSorterDialog.setWindowTitle(_translate("TextSorterDialog", "Form", None)) - self.remove_button.setText(_translate("TextSorterDialog", "Remove", None)) + def retranslateUi(self, EditDialog): + EditDialog.setWindowTitle(_translate("EditDialog", "Form", None)) + self.remove_button.setText(_translate("EditDialog", "Remove", None)) -from PyKDE4.kdeui import KButtonGroup, KArrowButton, KPushButton, KRichTextWidget +from PyKDE4.kdeui import KButtonGroup, KArrowButton, KPushButton diff --git a/texter/texter/favicon.ico b/texter/texter/favicon.ico new file mode 100644 index 0000000..791d34c Binary files /dev/null and b/texter/texter/favicon.ico differ diff --git a/texter/texter/favicon.pnm b/texter/texter/favicon.pnm new file mode 100644 index 0000000..1faed8a --- /dev/null +++ b/texter/texter/favicon.pnm @@ -0,0 +1,5 @@ +P6 +# CREATOR: GIMP PNM Filter Version 1.1 +16 16 +255 +џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџФФФђђђџџџџџџџџџџџџџџџџџџЏЏЏџџџџџџЈЈЈОООџџџџџџщщщжжжџџџџџџџџџџџџџџџОООџџџ•••ГГГyyyЮЮЮџџџeeeжжжџџџџџџџџџџџџџџџtttQQQџџџжжжlllbbbѓѓѓЈЈЈ………^^^ЂЂЂџџџџџџџџџџџџpppААА666ззз———ЎЎЎ~~~ЦЦЦчччРРРbbbЉЉЉџџџааа­­­џџџлллУУУ666рррhhhЪЪЪ”””   џџџџџџггг№№№џџџфффаааџџџџџџџџџОООџџџџџџЂЂЂџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ \ No newline at end of file diff --git a/texter/texter/main.py b/texter/texter/main.py index 38e4107..de4d46a 100644 --- a/texter/texter/main.py +++ b/texter/texter/main.py @@ -60,7 +60,8 @@ class MjpegStreamingServer(QTcpServer): super(MjpegStreamingServer, self).__init__(parent) self.server_address = server_address self.newConnection.connect(self.start_streaming) - self.widget = parent + self.widget = parent.live_text + self.win_id = self.widget.winId() self.sockets = list() self.img_data = None self.timer = QtCore.QTimer() @@ -72,6 +73,7 @@ class MjpegStreamingServer(QTcpServer): self.coords = parent.live_text_rect() def handle_request(self): + print "foo" sock = self.sender() logger.info("handle_request: %s %d", sock.peerAddress(), sock.peerPort()) sock_id = id(sock) @@ -89,7 +91,7 @@ class MjpegStreamingServer(QTcpServer): resource, ext, http_version = self.regex.match(line).groups() logger.info("resource=%r, ext=%r, http_version=%r", resource, ext, http_version) except AttributeError: - loggging.info("no matching request - sending 404 not found") + logger.info("no matching request - sending 404 not found") sock.write("HTTP/1.1 404 Not Found\r\n") else: if ext == "ico": @@ -159,10 +161,11 @@ class MjpegStreamingServer(QTcpServer): if not self.stream_clients: return - pixmap = QPixmap.grabWidget(self.widget.live_text, *self.coords) + #pixmap = QPixmap.grabWidget(self.widget, QtCore.QRect(10, 10, 768, 576)) + pixmap = QPixmap.grabWindow(self.win_id, 5, 5, 768, 576) buf = QBuffer() buf.open(QIODevice.WriteOnly) - pixmap.save(buf, "JPG", 25) + pixmap.save(buf, "JPG", 30) self.img_data = buf.data() len_data = len(self.img_data) array = QByteArray("--2342\r\nContent-Type: image/jpeg\r\nContent-length: %d\r\n\r\n%s\r\n\r\n\r\n" % (len_data, self.img_data)) @@ -368,7 +371,7 @@ class MainWindow(KMainWindow, Ui_MainWindow): super(MainWindow, self).__init__(parent) self.args = args self.is_streaming = False - self.http_server = MjpegStreamingServer((args.http_host, args.http_port), self) + self.live_center_action = None self.preview_center_action = None self.live_size_action = None @@ -392,6 +395,10 @@ class MainWindow(KMainWindow, Ui_MainWindow): self.is_auto_publish = False self.setupUi(self) + self.http_server = MjpegStreamingServer((args.http_host, args.http_port), self) + + self.live_text.setLineWrapMode(QtGui.QTextEdit.LineWrapMode(QtGui.QTextEdit.FixedPixelWidth)) + self.live_text.setLineWrapColumnOrWidth(768) self.font = QtGui.QFont("monospace", self.default_size) self.font.setStyleHint(QtGui.QFont.TypeWriter) @@ -431,6 +438,12 @@ class MainWindow(KMainWindow, Ui_MainWindow): self.show() + def getPreviewCoords(self): + public_rect = self.preview_text.geometry() + global_rect = QtCore.QRect(self.mapToGlobal(public_rect.topLeft()), self.mapToGlobal(public_rect.bottomRight())) + return global_rect.x(), global_rect.y() + + def filter_editor_actions(self): disabled_action_names = [ "action_to_plain_text", @@ -620,14 +633,14 @@ class MainWindow(KMainWindow, Ui_MainWindow): self.dialog.exec_() def live_text_rect(self): - return 3, 3, 768, 576 + return 5, 5, 768, 576 def stop_streaming(self): self.is_streaming = False self.http_server.stop() def start_streaming(self): - self.http_server.listen(port=9009) + self.http_server.listen(port=self.args.http_port) self.is_streaming = True def focusChanged(self, old, new): @@ -814,6 +827,8 @@ class MainWindow(KMainWindow, Ui_MainWindow): self.dialog.setButtons(KDialog.Close) self.dialog_widget = EditDialog(self.dialog) self.dialog.setMainWidget(self.dialog_widget) + pos_x, pos_y = self.getPreviewCoords() + self.dialog.move(pos_x, self.pos().y()) self.dialog.exec_() self.fill_combo_box() diff --git a/texter/texter/text_model.py b/texter/texter/text_model.py index 558ef45..e99e2dc 100644 --- a/texter/texter/text_model.py +++ b/texter/texter/text_model.py @@ -16,7 +16,7 @@ class TextModel(QtCore.QAbstractTableModel): return len(self.text_db) def columnCount(self, parent=QtCore.QModelIndex()): - return 2 + return 1 def data(self, index, role): if not index.isValid() or \ @@ -26,12 +26,9 @@ class TextModel(QtCore.QAbstractTableModel): row = index.row() column = index.column() if role == QtCore.Qt.DisplayRole: - return self.text_db[row][column] + print "data", row, column, row + return QtCore.QVariant(self.text_db[row][column]) #return "foo bar" - elif role == QtCore.Qt.ForegroundRole: - return QtGui.QBrush(QtCore.Qt.black) - elif role == QtCore.Qt.BackgroundRole: - return QtGui.QBrush(QtCore.Qt.white) return QtCore.QVariant() @@ -45,6 +42,12 @@ class TextModel(QtCore.QAbstractTableModel): return QtCore.QVariant() def setData(self, index, value, role): + if (not index.isValid() or + not 0 <= index.row() < self.rowCount()): + print "setData index not valid" + return False + + print "setData", index.row(), index.column(), value, role if role == QtCore.Qt.EditRole: text = value.toString() @@ -52,14 +55,16 @@ class TextModel(QtCore.QAbstractTableModel): return False else: self.text_db[index.row()][index.column()] = text - - return True + return True + else: + return False def flags(self, index): - return QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsDragEnabled | QtCore.Qt.ItemIsDropEnabled + if index.column() == 0: + return QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsEnabled + else: + return QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled - def supportedDropActions(self): - return QtCore.Qt.MoveAction def insertRows(self, row, count, parent=QtCore.QModelIndex()): self.beginInsertRows(parent, row, row+count+1) diff --git a/texter/texter/texter.html b/texter/texter/texter.html new file mode 120000 index 0000000..64233a9 --- /dev/null +++ b/texter/texter/texter.html @@ -0,0 +1 @@ +index.html \ No newline at end of file diff --git a/texter/texter/texter_ui.py b/texter/texter/texter_ui.py index 66a337d..cdc27fc 100644 --- a/texter/texter/texter_ui.py +++ b/texter/texter/texter_ui.py @@ -62,8 +62,8 @@ class Ui_MainWindow(object): self.horizontalLayout = QtGui.QHBoxLayout() self.horizontalLayout.setObjectName(_fromUtf8("horizontalLayout")) self.live_text = KRichTextWidget(self.centralwidget) - self.live_text.setMinimumSize(QtCore.QSize(775, 582)) - self.live_text.setMaximumSize(QtCore.QSize(775, 582)) + self.live_text.setMinimumSize(QtCore.QSize(778, 586)) + self.live_text.setMaximumSize(QtCore.QSize(778, 586)) palette = QtGui.QPalette() brush = QtGui.QBrush(QtGui.QColor(0, 0, 0)) brush.setStyle(QtCore.Qt.SolidPattern) @@ -116,8 +116,8 @@ class Ui_MainWindow(object): sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.preview_text.sizePolicy().hasHeightForWidth()) self.preview_text.setSizePolicy(sizePolicy) - self.preview_text.setMinimumSize(QtCore.QSize(300, 582)) - self.preview_text.setMaximumSize(QtCore.QSize(775, 582)) + self.preview_text.setMinimumSize(QtCore.QSize(300, 586)) + self.preview_text.setMaximumSize(QtCore.QSize(778, 586)) palette = QtGui.QPalette() brush = QtGui.QBrush(QtGui.QColor(0, 0, 0)) brush.setStyle(QtCore.Qt.SolidPattern)