much rewrite, very kane :)
This commit is contained in:
parent
05745e3c52
commit
80d6aea666
|
@ -0,0 +1,9 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="icon" type="image/png" href="/icon.png" />
|
||||
</head>
|
||||
<body>
|
||||
<img src="/texter_%d.mjpeg" alt="Smiley face" />
|
||||
</body>
|
||||
</html>
|
|
@ -23,8 +23,9 @@ from __future__ import absolute_import
|
|||
import os
|
||||
import os.path
|
||||
import re
|
||||
import signal
|
||||
import sys
|
||||
|
||||
from collections import deque
|
||||
from datetime import datetime
|
||||
from chaosc.argparser_groups import *
|
||||
from chaosc.lib import logger, resolve_host
|
||||
|
@ -33,7 +34,8 @@ 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
|
||||
from psylib.mjpeg_streaming_server import *
|
||||
from psylib.psyqt_base import PsyQtChaoscClientBase
|
||||
|
||||
try:
|
||||
from chaosc.c_osc_lib import OSCMessage, decode_osc
|
||||
|
@ -42,62 +44,17 @@ except ImportError as e:
|
|||
|
||||
app = QtGui.QApplication([])
|
||||
|
||||
class TextStorage(object):
|
||||
def __init__(self, columns):
|
||||
super(TextStorage, self).__init__()
|
||||
class ExclusiveTextStorage(object):
|
||||
def __init__(self, columns, default_font, column_width, line_height, scene):
|
||||
self.column_count = columns
|
||||
self.colors = (QtCore.Qt.red, QtCore.Qt.green, QtGui.QColor(46, 100, 254))
|
||||
|
||||
def init_columns(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
def add_text(self, column, text):
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class ColumnTextStorage(TextStorage):
|
||||
def __init__(self, columns, default_font, column_width, line_height, scene):
|
||||
super(ColumnTextStorage, self).__init__(columns)
|
||||
self.columns = list()
|
||||
self.default_font = default_font
|
||||
self.column_width = column_width
|
||||
self.line_height = line_height
|
||||
self.graphics_scene = scene
|
||||
self.num_lines, self.offset = divmod(768, self.line_height)
|
||||
|
||||
def init_columns(self):
|
||||
for x in range(self.column_count):
|
||||
column = list()
|
||||
color = self.colors[x]
|
||||
for y in range(self.num_lines):
|
||||
text_item = self.graphics_scene.addSimpleText("%d:%d" % (x, y), self.default_font)
|
||||
text_item.setBrush(color)
|
||||
text_item.setPos(x * self.column_width, y * self.line_height)
|
||||
column.append(text_item)
|
||||
self.columns.append(column)
|
||||
|
||||
def add_text(self, column, text):
|
||||
text_item = self.graphics_scene.addSimpleText(text, self.default_font)
|
||||
color = self.colors[column]
|
||||
text_item.setBrush(color)
|
||||
|
||||
old_item = self.columns[column].pop(0)
|
||||
self.graphics_scene.removeItem(old_item)
|
||||
self.columns[column].append(text_item)
|
||||
for iy, text_item in enumerate(self.columns[column]):
|
||||
text_item.setPos(column * self.column_width, iy * self.line_height)
|
||||
|
||||
|
||||
class ExclusiveTextStorage(TextStorage):
|
||||
def __init__(self, columns, default_font, column_width, line_height, scene):
|
||||
super(ExclusiveTextStorage, self).__init__(columns)
|
||||
self.column_count = columns
|
||||
self.lines = list()
|
||||
self.lines = deque()
|
||||
self.default_font = default_font
|
||||
self.column_width = column_width
|
||||
self.line_height = line_height
|
||||
self.graphics_scene = scene
|
||||
self.num_lines, self.offset = divmod(576, self.line_height)
|
||||
self.data = deque()
|
||||
|
||||
def init_columns(self):
|
||||
color = self.colors[0]
|
||||
|
@ -107,26 +64,38 @@ class ExclusiveTextStorage(TextStorage):
|
|||
text_item.setPos(0, y * self.line_height)
|
||||
self.lines.append(text_item)
|
||||
|
||||
def add_text(self, column, text):
|
||||
def __add_text(self, column, text):
|
||||
text_item = self.graphics_scene.addSimpleText(text, self.default_font)
|
||||
text_item.setX(column * self.column_width)
|
||||
color = self.colors[column]
|
||||
text_item.setBrush(color)
|
||||
|
||||
old_item = self.lines.pop(0)
|
||||
old_item = self.lines.popleft()
|
||||
self.graphics_scene.removeItem(old_item)
|
||||
self.lines.append(text_item)
|
||||
|
||||
def finish(self):
|
||||
while 1:
|
||||
try:
|
||||
column, text = self.data.popleft()
|
||||
self.__add_text(column, text)
|
||||
except IndexError, msg:
|
||||
break
|
||||
|
||||
|
||||
for iy, text_item in enumerate(self.lines):
|
||||
text_item.setY(iy * self.line_height)
|
||||
|
||||
def add_text(self, column, text):
|
||||
self.data.append((column, text))
|
||||
|
||||
|
||||
|
||||
|
||||
class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
||||
def __init__(self, args, parent=None, columns=3, column_exclusive=False):
|
||||
super(MainWindow, self).__init__(parent)
|
||||
class MainWindow(QtGui.QMainWindow, Ui_MainWindow, MjpegStreamingConsumerInterface, PsyQtChaoscClientBase):
|
||||
def __init__(self, args, parent=None):
|
||||
self.args = args
|
||||
#PsyQtChaoscClientBase.__init__(self)
|
||||
super(MainWindow, self).__init__()
|
||||
self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
|
||||
|
||||
self.setupUi(self)
|
||||
self.http_server = MjpegStreamingServer((args.http_host, args.http_port), self)
|
||||
|
@ -140,20 +109,16 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
|||
self.graphics_view.setScene(self.graphics_scene)
|
||||
self.default_font = QtGui.QFont("Monospace", 14)
|
||||
self.default_font.setStyleHint(QtGui.QFont.Monospace)
|
||||
self.default_font.setBold(True)
|
||||
#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()
|
||||
columns = 3
|
||||
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.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")
|
||||
|
@ -165,6 +130,9 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
|||
|
||||
self.regex = re.compile("^/(uwe|merle|bjoern)/(.*?)$")
|
||||
|
||||
def pubdir(self):
|
||||
return os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
def closeEvent(self, event):
|
||||
msg = OSCMessage("/unsubscribe")
|
||||
msg.appendTypedArg("localhost", "s")
|
||||
|
@ -179,6 +147,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
|||
self.text_storage.add_text(column, text)
|
||||
|
||||
def render_image(self):
|
||||
self.text_storage.finish()
|
||||
image = QtGui.QImage(768, 576, QtGui.QImage.Format_ARGB32_Premultiplied)
|
||||
image.fill(QtCore.Qt.black)
|
||||
painter = QtGui.QPainter(image)
|
||||
|
@ -191,7 +160,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
|||
painter.end()
|
||||
buf = QBuffer()
|
||||
buf.open(QIODevice.WriteOnly)
|
||||
image.save(buf, "JPG", 80)
|
||||
image.save(buf, "JPG", 85)
|
||||
image_data = buf.data()
|
||||
return image_data
|
||||
|
||||
|
@ -207,6 +176,8 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
|||
except AttributeError:
|
||||
pass
|
||||
else:
|
||||
if text == "temperatur":
|
||||
text += "e"
|
||||
if actor == "merle":
|
||||
self.add_text(0, "%s = %s" % (text, ", ".join([str(i) for i in args])))
|
||||
elif actor == "uwe":
|
||||
|
@ -229,8 +200,11 @@ def main():
|
|||
args = arg_parser.finalize()
|
||||
|
||||
http_host, http_port = resolve_host(args.http_host, args.http_port, args.address_family)
|
||||
args.chaosc_host, args.chaosc_port = resolve_host(args.chaosc_host, args.chaosc_port, args.address_family)
|
||||
|
||||
window = MainWindow(args)
|
||||
#window.show()
|
||||
sys.excepthook = window.sigint_handler
|
||||
signal.signal(signal.SIGTERM, window.sigterm_handler)
|
||||
app.exec_()
|
||||
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<HTML>
|
||||
<BODY>
|
||||
|
||||
<img src="/camera.mjpeg" alt="Smiley face">
|
||||
<img src="/camera_%d.mjpeg" alt="Smiley face">
|
||||
|
||||
</BODY>
|
||||
</HTML>
|
||||
|
|
|
@ -24,79 +24,82 @@
|
|||
|
||||
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 atexit
|
||||
import random
|
||||
import os.path
|
||||
import re
|
||||
import signal
|
||||
import sys
|
||||
import exceptions
|
||||
|
||||
from PyQt4 import QtCore, QtGui
|
||||
from PyQt4.QtCore import QBuffer, QByteArray, QIODevice
|
||||
from PyQt4.QtNetwork import QUdpSocket, QHostAddress
|
||||
|
||||
import numpy as np
|
||||
|
||||
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
|
||||
|
||||
from chaosc.argparser_groups import ArgParser
|
||||
from chaosc.lib import logger, resolve_host
|
||||
from psylib.mjpeg_streaming_server import *
|
||||
from psylib.psyqt_base import PsyQtChaoscClientBase
|
||||
|
||||
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
|
||||
|
||||
qtapp = QtGui.QApplication([])
|
||||
|
||||
def get_steps(pulse_rate, rate):
|
||||
beat_length = 60. / pulse_rate
|
||||
steps_pre = int(beat_length / rate) + 1
|
||||
|
||||
def get_steps(pulse, delta_ms):
|
||||
beat_length = 60000. / pulse
|
||||
steps_pre = int(beat_length / delta_ms) + 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):
|
||||
def __init__(self, pulse=92, delta=80):
|
||||
self.count = 0
|
||||
self.pulse = 92
|
||||
self.pulse = random.randint(85, 105)
|
||||
self.delta = delta
|
||||
self.steps = get_steps(self.pulse, delta / 2)
|
||||
self.multiplier = 4
|
||||
self.steps, _ = get_steps(self.pulse, delta / self.multiplier)
|
||||
|
||||
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.):
|
||||
if self.count < int(self.steps / 100. * 30):
|
||||
value = random.randint(30, 35)
|
||||
elif self.count == int(self.steps / 100. * 30):
|
||||
value = random.randint(random.randint(50,60), random.randint(60, 70))
|
||||
elif self.count < int(self.steps / 100. * 45):
|
||||
value = random.randint(30, 35)
|
||||
elif self.count < int(self.steps / 2.):
|
||||
value = random.randint(0, 15)
|
||||
elif self.count == int(self.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:
|
||||
elif self.count < int(self.steps / 100. * 60):
|
||||
value = random.randint(random.randint(25,30), random.randint(30, 35))
|
||||
elif self.count < int(self.steps / 100. * 70):
|
||||
value = random.randint(random.randint(10,25), random.randint(25, 30))
|
||||
elif self.count < self.steps:
|
||||
value = random.randint(random.randint(15,25), random.randint(25, 30))
|
||||
else:
|
||||
self.count = 0
|
||||
value = 30
|
||||
|
||||
self.count += 1
|
||||
yield value
|
||||
|
||||
def set_pulse(self, pulse):
|
||||
self.pulse = pulse
|
||||
self.steps, _ = get_steps(pulse, self.delta / self.multiplier)
|
||||
|
||||
|
||||
def retrigger(self):
|
||||
self.count = self.steps / 2
|
||||
|
||||
|
@ -115,11 +118,14 @@ class Actor(object):
|
|||
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.plotItem = pg.PlotCurveItem(pen=pg.mkPen(color, width=3), width=4, name=name)
|
||||
#self.plotItem.setShadowPen(pg.mkPen("w", width=5))
|
||||
self.plotPoint = pg.ScatterPlotItem(pen=pg.mkPen("w", width=5), brush=pg.mkBrush(color), size=5)
|
||||
self.osci = None
|
||||
self.osci_obj = None
|
||||
|
||||
def __str__(self):
|
||||
return "<Actor name:%r, active=%r, position=%r>" % (self.name, self.active, self.head)
|
||||
return "<Actor name:%r, position=%r>" % (self.name, self.head)
|
||||
|
||||
__repr__ = __str__
|
||||
|
||||
|
@ -146,26 +152,17 @@ class Actor(object):
|
|||
self.plotPoint.setData(x=[self.pre_head], y=[self.data[self.pre_head]])
|
||||
|
||||
|
||||
class EkgPlotWidget(PlotWidget):
|
||||
class EkgPlotWidget(PlotWidget, MjpegStreamingConsumerInterface, PsyQtChaoscClientBase):
|
||||
def __init__(self, args, parent=None):
|
||||
super(EkgPlotWidget, self).__init__(parent)
|
||||
self.args = args
|
||||
PsyQtChaoscClientBase.__init__(self)
|
||||
super(EkgPlotWidget, self).__init__()
|
||||
self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
|
||||
|
||||
self.http_server = MjpegStreamingServer((args.http_host, args.http_port), self)
|
||||
self.fps = 12.5
|
||||
self.http_server = MjpegStreamingServer((args.http_host, args.http_port), self, self.fps)
|
||||
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()
|
||||
|
@ -173,8 +170,6 @@ class EkgPlotWidget(PlotWidget):
|
|||
self.setYRange(0, 255)
|
||||
self.setXRange(0, self.num_data)
|
||||
self.resize(768, 576)
|
||||
|
||||
|
||||
colors = ["r", "g", "b"]
|
||||
|
||||
ba = self.getAxis("bottom")
|
||||
|
@ -186,7 +181,6 @@ class EkgPlotWidget(PlotWidget):
|
|||
self.active_actors = list()
|
||||
|
||||
self.actors = dict()
|
||||
self.lengths1 = [0]
|
||||
|
||||
self.max_value = 255
|
||||
actor_names = ["merle", "uwe", "bjoern"]
|
||||
|
@ -197,11 +191,10 @@ class EkgPlotWidget(PlotWidget):
|
|||
self.add_actor(actor_name, self.num_data, color, ix, self.max_actors, self.actor_height)
|
||||
|
||||
self.set_positions()
|
||||
self.heartbeat_regex = re.compile("^/(.*?)/heartbeat$")
|
||||
|
||||
self.ekg_regex = re.compile("^/(.*?)/ekg$")
|
||||
self.ctl_regex = re.compile("^/plot/(.*?)$")
|
||||
self.updated_actors = set()
|
||||
self.new_round()
|
||||
def pubdir(self):
|
||||
return os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
|
||||
def add_actor(self, actor_name, num_data, color, ix, max_actors, actor_height):
|
||||
|
@ -210,7 +203,8 @@ class EkgPlotWidget(PlotWidget):
|
|||
self.addItem(actor_obj.plotItem)
|
||||
self.addItem(actor_obj.plotPoint)
|
||||
self.active_actors.append(actor_obj)
|
||||
|
||||
actor_obj.osci_obj = Generator(delta=self.http_server.timer_delta)
|
||||
actor_obj.osci = actor_obj.osci_obj()
|
||||
|
||||
def set_positions(self):
|
||||
for ix, actor_obj in enumerate(self.active_actors):
|
||||
|
@ -220,51 +214,24 @@ class EkgPlotWidget(PlotWidget):
|
|||
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(self, osc_address, args):
|
||||
|
||||
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)
|
||||
res = self.heartbeat_regex.match(osc_address)
|
||||
if res:
|
||||
actor_name = res.group(1)
|
||||
actor_obj = self.actors[actor_name]
|
||||
actor_obj.add_value(value)
|
||||
#logger.info("actor: %r, %r", actor_name, args)
|
||||
if args[0] == 1:
|
||||
actor_obj.osci_obj.retrigger()
|
||||
actor_obj.osci_obj.set_pulse(args[1])
|
||||
|
||||
|
||||
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()
|
||||
for actor_obj in self.active_actors:
|
||||
osc = actor_obj.osci
|
||||
for i in range(actor_obj.osci_obj.multiplier):
|
||||
actor_obj.add_value(osc.next())
|
||||
actor_obj.render()
|
||||
exporter = pg.exporters.ImageExporter.ImageExporter(self.plotItem)
|
||||
exporter.parameters()['width'] = 768
|
||||
img = exporter.export(toBytes=True)
|
||||
|
@ -272,7 +239,6 @@ class EkgPlotWidget(PlotWidget):
|
|||
buf.open(QIODevice.WriteOnly)
|
||||
img.save(buf, "JPG", 75)
|
||||
JpegData = buf.data()
|
||||
self.new_round()
|
||||
return JpegData
|
||||
|
||||
def got_message(self):
|
||||
|
@ -282,10 +248,8 @@ class EkgPlotWidget(PlotWidget):
|
|||
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
|
||||
self.update(osc_address, args)
|
||||
|
||||
|
||||
|
||||
|
@ -293,18 +257,20 @@ 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="::",
|
||||
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,
|
||||
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)
|
||||
args.chaosc_host, args.chaosc_port = resolve_host(args.chaosc_host, args.chaosc_port, args.address_family)
|
||||
|
||||
qtapp = QtGui.QApplication([])
|
||||
widget = EkgPlotWidget(args)
|
||||
window = EkgPlotWidget(args)
|
||||
sys.excepthook = window.sigint_handler
|
||||
signal.signal(signal.SIGTERM, window.sigterm_handler)
|
||||
qtapp.exec_()
|
||||
|
||||
|
||||
|
|
|
@ -1,20 +1,20 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# This file is part of chaosc and psychosis
|
||||
# This file is part of chaosc/psylib package
|
||||
#
|
||||
# chaosc/psychosis is free software: you can redistribute it and/or modify
|
||||
# chaosc/psylib 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.
|
||||
#
|
||||
# chaosc/psychosis is distributed in the hope that it will be useful,
|
||||
# chaosc/psylib 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 chaosc/psychosis. If not, see <http://www.gnu.org/licenses/>.
|
||||
# along with chaosc/psylib. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# Copyright (C) 2014 Stefan Kögl
|
||||
|
||||
|
@ -23,27 +23,47 @@ from __future__ import absolute_import
|
|||
import os
|
||||
import os.path
|
||||
import re
|
||||
import sys
|
||||
|
||||
from datetime import datetime
|
||||
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 chaosc.lib import logger
|
||||
from PyQt4 import QtCore
|
||||
from PyQt4.QtCore import QByteArray
|
||||
from PyQt4.QtNetwork import QTcpServer, QTcpSocket
|
||||
|
||||
__all__ = ["MjpegStreamingConsumerInterface", "MjpegStreamingServer"]
|
||||
|
||||
class MjpegStreamingConsumerInterface(object):
|
||||
def pubdir(self):
|
||||
""" returns the directory, from where your static files should be served
|
||||
|
||||
fast and dirty implementation e.g:
|
||||
return os.path.dirname(os.path.abspath(__file__))
|
||||
"""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
def render_image(self):
|
||||
"""returns a QByteArray with the binary date of a jpg image
|
||||
|
||||
this method should implement the actual window/widget grabbing"""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
class MjpegStreamingServer(QTcpServer):
|
||||
|
||||
def __init__(self, server_address, parent=None):
|
||||
def __init__(self, server_address, parent=None, fps=12.5):
|
||||
super(MjpegStreamingServer, self).__init__(parent)
|
||||
self.server_address = server_address
|
||||
self.newConnection.connect(self.new_connection)
|
||||
assert isinstance(parent, MjpegStreamingConsumerInterface)
|
||||
self.widget = parent
|
||||
|
||||
self.sockets = list()
|
||||
self.img_data = None
|
||||
self.fps = fps
|
||||
self.timer_delta = 1000 / fps
|
||||
self.timer = QtCore.QTimer()
|
||||
self.timer.timeout.connect(self.send_image)
|
||||
self.timer.start(80)
|
||||
self.timer.start(self.timer_delta)
|
||||
self.stream_clients = list()
|
||||
self.get_regex = re.compile("^GET /(\w+?)\.(\w+?) HTTP/(\d+\.\d+)$")
|
||||
self.host_regex = re.compile("^Host: (\w+?):(\d+)$")
|
||||
|
@ -51,8 +71,8 @@ class MjpegStreamingServer(QTcpServer):
|
|||
|
||||
def handle_request(self):
|
||||
sock = self.sender()
|
||||
logger.info("handle_request: %s %d", sock.peerAddress(), sock.peerPort())
|
||||
sock_id = id(sock)
|
||||
logger.info("handle_request: sock_id=%r", sock_id)
|
||||
if sock.state() in (QTcpSocket.UnconnectedState, QTcpSocket.ClosingState):
|
||||
logger.info("connection closed")
|
||||
self.sockets.remove(sock)
|
||||
|
@ -79,13 +99,25 @@ class MjpegStreamingServer(QTcpServer):
|
|||
return
|
||||
else:
|
||||
if ext == "ico":
|
||||
directory = os.path.dirname(os.path.abspath(__file__))
|
||||
directory = self.widget.pubdir()
|
||||
try:
|
||||
data = open(os.path.join(directory, "favicon.ico"), "rb").read()
|
||||
except IOError:
|
||||
logger.error("request not found/handled - sending 404 not found")
|
||||
sock.write("HTTP/1.1 404 Not Found\r\n")
|
||||
return
|
||||
else:
|
||||
sock.write(QByteArray('HTTP/1.1 200 Ok\r\nContent-Type: image/x-ico\r\n\r\n%s' % data))
|
||||
elif ext == "html":
|
||||
directory = os.path.dirname(os.path.abspath(__file__))
|
||||
directory = self.widget.pubdir()
|
||||
try:
|
||||
data = open(os.path.join(directory, "index.html"), "rb").read() % sock_id
|
||||
self.html_map[sock_id] = None
|
||||
except IOError:
|
||||
logger.error("request not found/handled - sending 404 not found")
|
||||
sock.write("HTTP/1.1 404 Not Found\r\n")
|
||||
return
|
||||
else:
|
||||
sock.write(QByteArray('HTTP/1.1 200 Ok\r\nContent-Type: text/html;encoding: utf-8\r\n\r\n%s' % data))
|
||||
elif ext == "mjpeg":
|
||||
try:
|
||||
|
@ -117,29 +149,38 @@ class MjpegStreamingServer(QTcpServer):
|
|||
sock.disconnected.disconnect(self.slot_remove_connection)
|
||||
sock.close()
|
||||
sock.deleteLater()
|
||||
try:
|
||||
self.sockets.remove(sock)
|
||||
logger.info("connection removed: sock=%r, sock_id=%r", sock, sock_id)
|
||||
logger.info("connection %r removed", sock_id)
|
||||
except ValueError, msg:
|
||||
logger.info("connection %r was not stored?", sock_id)
|
||||
|
||||
|
||||
try:
|
||||
self.stream_clients.remove(sock)
|
||||
except ValueError:
|
||||
pass
|
||||
logger.info("connection %r was not streaming", sock_id)
|
||||
|
||||
|
||||
try:
|
||||
stream_client = self.html_map.pop(sock_id)
|
||||
except KeyError:
|
||||
logger.info("socket has no child socket")
|
||||
logger.info("socket %r has no child socket", sock_id)
|
||||
else:
|
||||
try:
|
||||
stream_client.close()
|
||||
except AttributeError, msg:
|
||||
logger.info("no stream client")
|
||||
else:
|
||||
try:
|
||||
self.stream_clients.remove(stream_client)
|
||||
logger.info("removed stream_client=%r", id(stream_client))
|
||||
logger.info("child connection %r removed from streaming", id(stream_client))
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
try:
|
||||
self.sockets.remove(stream_client)
|
||||
logger.info("removed child sock_id=%r", id(stream_client))
|
||||
logger.info("child connection %r removed from storage", id(stream_client))
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
|
|
|
@ -0,0 +1,105 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# This file is part of chaosc/psylib package
|
||||
#
|
||||
# chaosc/psylib 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.
|
||||
#
|
||||
# chaosc/psylib 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 chaosc/psylib. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# Copyright (C) 2014 Stefan Kögl
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import sys
|
||||
|
||||
from PyQt4 import QtCore, QtGui
|
||||
from PyQt4.QtCore import QBuffer, QByteArray
|
||||
from PyQt4.QtNetwork import QUdpSocket, QHostAddress
|
||||
|
||||
|
||||
from chaosc.lib import logger
|
||||
|
||||
try:
|
||||
from chaosc.c_osc_lib import OSCMessage, decode_osc
|
||||
except ImportError as e:
|
||||
from chaosc.osc_lib import OSCMessage, decode_osc
|
||||
|
||||
|
||||
class PsyQtClientBase(QtCore.QObject):
|
||||
|
||||
def __init__(self):
|
||||
super(PsyQtClientBase, self).__init__()
|
||||
# periodically trap into python interpreter domain to catch signals etc
|
||||
timer = QtCore.QTimer()
|
||||
timer.start(2000)
|
||||
timer.timeout.connect(lambda: None)
|
||||
|
||||
def sigint_handler(self, ex_cls, ex, traceback):
|
||||
"""Handler for the SIGINT signal."""
|
||||
if ex_cls == KeyboardInterrupt:
|
||||
logger.info("found KeyboardInterrupt")
|
||||
QtGui.QApplication.exit()
|
||||
else:
|
||||
logger.critical(''.join(traceback.format_tb(tb)))
|
||||
logger.critical('{0}: {1}'.format(ex_cls, ex))
|
||||
|
||||
class PsyQtChaoscClientBase(PsyQtClientBase):
|
||||
|
||||
def __init__(self):
|
||||
super(PsyQtChaoscClientBase, self).__init__()
|
||||
self.osc_sock = QUdpSocket(self)
|
||||
logger.info("osc bind localhost %d", self.args.client_port)
|
||||
self.osc_sock.bind(QHostAddress(self.args.client_host), self.args.client_port)
|
||||
self.osc_sock.readyRead.connect(self.got_message)
|
||||
self.osc_sock.error.connect(self.handle_osc_error)
|
||||
self.subscribe()
|
||||
|
||||
def sigint_handler(self, ex_cls, ex, traceback):
|
||||
"""Handler for the SIGINT signal."""
|
||||
if ex_cls == KeyboardInterrupt:
|
||||
logger.info("found KeyboardInterrupt")
|
||||
self.unsubscribe()
|
||||
QtGui.QApplication.exit()
|
||||
else:
|
||||
logger.critical(''.join(traceback.format_tb(traceback)))
|
||||
logger.critical('{0}: {1}'.format(ex_cls, ex))
|
||||
|
||||
def sigterm_handler(self, *args):
|
||||
self.unsubscribe()
|
||||
QtGui.QApplication.exit()
|
||||
|
||||
def subscribe(self):
|
||||
logger.info("subscribe")
|
||||
msg = OSCMessage("/subscribe")
|
||||
msg.appendTypedArg("localhost", "s")
|
||||
msg.appendTypedArg(self.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(self.args.chaosc_host), self.args.chaosc_port)
|
||||
|
||||
def unsubscribe(self):
|
||||
logger.info("unsubscribe")
|
||||
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(self.args.chaosc_host), self.args.chaosc_port)
|
||||
|
||||
def handle_osc_error(self, error):
|
||||
logger.info("osc socket error %d", error)
|
||||
|
||||
def closeEvent(self, event):
|
||||
logger.info("closeEvent", event)
|
||||
self.unsubscribe()
|
||||
event.accept()
|
|
@ -24,6 +24,7 @@ from __future__ import absolute_import
|
|||
import cPickle
|
||||
import os.path
|
||||
import re
|
||||
import sys
|
||||
|
||||
from PyQt4 import QtCore, QtGui
|
||||
|
||||
|
@ -39,11 +40,14 @@ from PyQt4.QtNetwork import QTcpServer, QTcpSocket
|
|||
from chaosc.argparser_groups import ArgParser
|
||||
from chaosc.lib import resolve_host, logger
|
||||
|
||||
from psylib.mjpeg_streaming_server import *
|
||||
from psylib.psyqt_base import PsyQtClientBase
|
||||
|
||||
from texter.texter_ui import Ui_MainWindow, _fromUtf8
|
||||
from texter.edit_dialog_ui import Ui_EditDialog
|
||||
from texter.text_model import TextModel
|
||||
|
||||
app = QtGui.QApplication([])
|
||||
qtapp = QtGui.QApplication([])
|
||||
|
||||
|
||||
# NOTE: if the QIcon.fromTheme method does not find any icons, you can use
|
||||
|
@ -54,144 +58,6 @@ app = QtGui.QApplication([])
|
|||
def get_preview_text(text):
|
||||
return re.sub(" +", " ", text.replace("\n", " ")).strip()[:20]
|
||||
|
||||
class MjpegStreamingServer(QTcpServer):
|
||||
|
||||
def __init__(self, server_address, parent=None):
|
||||
super(MjpegStreamingServer, self).__init__(parent)
|
||||
self.server_address = server_address
|
||||
self.newConnection.connect(self.start_streaming)
|
||||
self.widget = parent.live_text
|
||||
self.win_id = self.widget.winId()
|
||||
self.sockets = list()
|
||||
self.img_data = None
|
||||
self.timer = QtCore.QTimer()
|
||||
self.timer.timeout.connect(self.render_image)
|
||||
self.timer.start(80)
|
||||
self.stream_clients = list()
|
||||
self.regex = re.compile("^GET /(\w+?)\.(\w+?) HTTP/(\d+\.\d+)$")
|
||||
self.html_map = dict()
|
||||
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)
|
||||
if sock.state() in (QTcpSocket.UnconnectedState, QTcpSocket.ClosingState):
|
||||
logger.info("connection closed")
|
||||
self.sockets.remove(sock)
|
||||
sock.deleteLater()
|
||||
return
|
||||
|
||||
client_data = str(sock.readAll())
|
||||
logger.info("request %r", client_data)
|
||||
line = client_data.split("\r\n")[0]
|
||||
logger.info("first line: %r", line)
|
||||
try:
|
||||
resource, ext, http_version = self.regex.match(line).groups()
|
||||
logger.info("resource=%r, ext=%r, http_version=%r", resource, ext, http_version)
|
||||
except AttributeError:
|
||||
logger.info("no matching request - sending 404 not found")
|
||||
sock.write("HTTP/1.1 404 Not Found\r\n")
|
||||
else:
|
||||
if ext == "ico":
|
||||
directory = os.path.dirname(os.path.abspath(__file__))
|
||||
data = open(os.path.join(directory, "favicon.ico"), "rb").read()
|
||||
sock.write(QByteArray('HTTP/1.1 200 Ok\r\nContent-Type: image/x-ico\r\n\r\n%s' % data))
|
||||
elif ext == "html":
|
||||
directory = os.path.dirname(os.path.abspath(__file__))
|
||||
data = open(os.path.join(directory, "index.html"), "rb").read() % sock_id
|
||||
self.html_map[sock_id] = None
|
||||
sock.write(QByteArray('HTTP/1.1 200 Ok\r\nContent-Type: text/html;encoding: utf-8\r\n\r\n%s' % data))
|
||||
#sock.close()
|
||||
elif ext == "mjpeg":
|
||||
try:
|
||||
_, html_sock_id = resource.split("_", 1)
|
||||
html_sock_id = int(html_sock_id)
|
||||
except ValueError:
|
||||
html_sock_id = None
|
||||
|
||||
if sock not in self.stream_clients:
|
||||
logger.info("starting streaming...")
|
||||
if html_sock_id is not None:
|
||||
self.html_map[html_sock_id] = sock
|
||||
self.stream_clients.append(sock)
|
||||
sock.write(QByteArray('HTTP/1.1 200 Ok\r\nContent-Type: multipart/x-mixed-replace; boundary=--2342\r\n\r\n'))
|
||||
else:
|
||||
logger.error("request not found/handled - sending 404 not found")
|
||||
sock.write("HTTP/1.1 404 Not Found\r\n")
|
||||
|
||||
|
||||
def remove_stream_client(self):
|
||||
try:
|
||||
sock = self.sender()
|
||||
except RuntimeError:
|
||||
return
|
||||
sock_id = id(sock)
|
||||
logger.info("remove_stream_client: sock=%r, sock_id=%r", sock, sock_id)
|
||||
if sock.state() == QTcpSocket.UnconnectedState:
|
||||
sock.disconnected.disconnect(self.remove_stream_client)
|
||||
self.sockets.remove(sock)
|
||||
logger.info("removed sock_id=%r", sock_id)
|
||||
sock.close()
|
||||
try:
|
||||
self.stream_clients.remove(sock)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
try:
|
||||
stream_client = self.html_map.pop(sock_id)
|
||||
except KeyError:
|
||||
logger.info("socket has no child socket")
|
||||
else:
|
||||
stream_client.close()
|
||||
try:
|
||||
self.stream_clients.remove(stream_client)
|
||||
logger.info("removed stream_client=%r", id(stream_client))
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
try:
|
||||
self.sockets.remove(stream_client)
|
||||
logger.info("removed child sock_id=%r", id(stream_client))
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
def render_image(self):
|
||||
if not self.stream_clients:
|
||||
return
|
||||
|
||||
#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", 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))
|
||||
for sock in self.stream_clients:
|
||||
sock.write(array)
|
||||
|
||||
def start_streaming(self):
|
||||
while self.hasPendingConnections():
|
||||
sock = self.nextPendingConnection()
|
||||
logger.info("new connection=%r", id(sock))
|
||||
sock.readyRead.connect(self.handle_request)
|
||||
sock.disconnected.connect(self.remove_stream_client)
|
||||
self.sockets.append(sock)
|
||||
|
||||
def stop(self):
|
||||
for sock in self.sockets:
|
||||
sock.close()
|
||||
sock.deleteLater()
|
||||
for sock in self.stream_clients:
|
||||
sock.close()
|
||||
sock.deleteLater()
|
||||
self.stream_clients = list()
|
||||
self.sockets = list()
|
||||
self.html_map = dict()
|
||||
self.close()
|
||||
|
||||
|
||||
class EditDialog(QtGui.QWidget, Ui_EditDialog):
|
||||
def __init__(self, parent=None):
|
||||
|
@ -366,10 +232,10 @@ class TextAnimation(QtCore.QObject):
|
|||
self.count += 1
|
||||
|
||||
|
||||
class MainWindow(KMainWindow, Ui_MainWindow):
|
||||
class MainWindow(KMainWindow, Ui_MainWindow, MjpegStreamingConsumerInterface, PsyQtClientBase):
|
||||
def __init__(self, args, parent=None):
|
||||
super(MainWindow, self).__init__(parent)
|
||||
self.args = args
|
||||
super(MainWindow, self).__init__()
|
||||
self.is_streaming = False
|
||||
|
||||
self.live_center_action = None
|
||||
|
@ -391,11 +257,15 @@ class MainWindow(KMainWindow, Ui_MainWindow):
|
|||
self.dialog = None
|
||||
self.current_object = None
|
||||
self.current_index = -1
|
||||
self.win_id = self.winId()
|
||||
|
||||
self.is_auto_publish = False
|
||||
|
||||
self.setupUi(self)
|
||||
self.http_server = MjpegStreamingServer((args.http_host, args.http_port), self)
|
||||
self.coords = self.live_text_rect()
|
||||
|
||||
self.fps = 12.5
|
||||
self.http_server = MjpegStreamingServer((args.http_host, args.http_port), self, self.fps)
|
||||
|
||||
self.live_text.setLineWrapMode(QtGui.QTextEdit.LineWrapMode(QtGui.QTextEdit.FixedPixelWidth))
|
||||
self.live_text.setLineWrapColumnOrWidth(768)
|
||||
|
@ -433,17 +303,29 @@ class MainWindow(KMainWindow, Ui_MainWindow):
|
|||
self.create_toolbar()
|
||||
self.slot_load()
|
||||
|
||||
app.focusChanged.connect(self.focusChanged)
|
||||
qtapp.focusChanged.connect(self.focusChanged)
|
||||
self.start_streaming()
|
||||
|
||||
self.show()
|
||||
|
||||
def pubdir(self):
|
||||
return os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
|
||||
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 render_image(self):
|
||||
pixmap = QPixmap.grabWindow(self.win_id, self.coords.x() + 10, self.coords.y() + 10, 768, 576)
|
||||
buf = QBuffer()
|
||||
buf.open(QIODevice.WriteOnly)
|
||||
pixmap.save(buf, "JPG", 75)
|
||||
return buf.data()
|
||||
|
||||
|
||||
def filter_editor_actions(self):
|
||||
disabled_action_names = [
|
||||
"action_to_plain_text",
|
||||
|
@ -631,9 +513,10 @@ class MainWindow(KMainWindow, Ui_MainWindow):
|
|||
self.dialog.setButtons(KDialog.ButtonCodes(KDialog.Ok | KDialog.Cancel))
|
||||
self.dialog.okClicked.connect(self.slot_save)
|
||||
self.dialog.exec_()
|
||||
super(self, MainWindow).closeEvent(event)
|
||||
|
||||
def live_text_rect(self):
|
||||
return 5, 5, 768, 576
|
||||
return self.live_text.geometry()
|
||||
|
||||
def stop_streaming(self):
|
||||
self.is_streaming = False
|
||||
|
@ -643,6 +526,24 @@ class MainWindow(KMainWindow, Ui_MainWindow):
|
|||
self.http_server.listen(port=self.args.http_port)
|
||||
self.is_streaming = True
|
||||
|
||||
def fill_combo_box(self):
|
||||
if self.dialog is not None:
|
||||
self.dialog.deleteLater()
|
||||
self.dialog = None
|
||||
|
||||
self.text_combo.clear()
|
||||
current_row = -1
|
||||
for index, list_obj in enumerate(self.model.text_db):
|
||||
preview, text = list_obj
|
||||
self.text_combo.addAction(preview)
|
||||
if list_obj == self.current_object:
|
||||
current_row = index
|
||||
|
||||
if current_row == -1:
|
||||
current_row = self.current_index
|
||||
self.slot_load_preview_text(current_row)
|
||||
self.text_combo.setCurrentItem(current_row)
|
||||
|
||||
def focusChanged(self, old, new):
|
||||
if new == self.preview_text:
|
||||
self.live_editor_collection.clearAssociatedWidgets()
|
||||
|
@ -651,13 +552,6 @@ class MainWindow(KMainWindow, Ui_MainWindow):
|
|||
self.preview_editor_collection.clearAssociatedWidgets()
|
||||
self.live_editor_collection.addAssociatedWidget(self.toolbar)
|
||||
|
||||
def custom_clear(self, cursor):
|
||||
cursor.beginEditBlock()
|
||||
cursor.movePosition(QtGui.QTextCursor.Start)
|
||||
cursor.movePosition(QtGui.QTextCursor.End, QtGui.QTextCursor.KeepAnchor)
|
||||
cursor.removeSelectedText()
|
||||
cursor.endEditBlock()
|
||||
|
||||
def slot_auto_publish(self, state):
|
||||
self.is_auto_publish = bool(state)
|
||||
|
||||
|
@ -737,23 +631,6 @@ class MainWindow(KMainWindow, Ui_MainWindow):
|
|||
if self.fade_animation.timer is None:
|
||||
self.fade_animation.start_animation()
|
||||
|
||||
def fill_combo_box(self):
|
||||
if self.dialog is not None:
|
||||
self.dialog.deleteLater()
|
||||
self.dialog = None
|
||||
|
||||
self.text_combo.clear()
|
||||
current_row = -1
|
||||
for index, list_obj in enumerate(self.model.text_db):
|
||||
preview, text = list_obj
|
||||
self.text_combo.addAction(preview)
|
||||
if list_obj == self.current_object:
|
||||
current_row = index
|
||||
|
||||
if current_row == -1:
|
||||
current_row = self.current_index
|
||||
self.slot_load_preview_text(current_row)
|
||||
self.text_combo.setCurrentItem(current_row)
|
||||
|
||||
def slot_load_preview_text(self, index):
|
||||
try:
|
||||
|
@ -864,9 +741,11 @@ def main():
|
|||
args = arg_parser.finalize()
|
||||
|
||||
args.http_host, args.http_port = resolve_host(args.http_host, args.http_port, args.address_family)
|
||||
args.chaosc_host, args.chaosc_port = resolve_host(args.chaosc_host, args.chaosc_port, args.address_family)
|
||||
|
||||
window = MainWindow(args)
|
||||
app.exec_()
|
||||
window = MainWindow(args, None)
|
||||
sys.excepthook = window.sigint_handler
|
||||
qtapp.exec_()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
Loading…
Reference in New Issue