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/index.html b/dump_grabber/dump_grabber/index.html
new file mode 100644
index 0000000..df3af71
--- /dev/null
+++ b/dump_grabber/dump_grabber/index.html
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/dump_grabber/dump_grabber/main.py b/dump_grabber/dump_grabber/main.py
index 5c78bcb..5dd5951 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,297 +20,172 @@
from __future__ import absolute_import
-import logging
import os
import os.path
-import Queue
import re
-import select
-import socket
+import signal
import sys
-import threading
-import time
+from collections import deque
-from datetime import datetime
-from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
-from chaosc.argparser_groups import *
+from chaosc.argparser_groups import ArgParser
from chaosc.lib import logger, resolve_host
from PyQt4 import QtCore, QtGui
from PyQt4.QtCore import QBuffer, QByteArray, QIODevice
+from PyQt4.QtGui import QPixmap, QPainter
+from PyQt4.QtNetwork import QHostAddress
+
from dump_grabber.dump_grabber_ui import Ui_MainWindow
+from psylib.mjpeg_streaming_server import (MjpegStreamingServer,
+ MjpegStreamingConsumerInterface)
+from psylib.psyqt_base import PsyQtChaoscClientBase
try:
from chaosc.c_osc_lib import OSCMessage, decode_osc
-except ImportError as e:
+except ImportError:
from chaosc.osc_lib import OSCMessage, decode_osc
-app = QtGui.QApplication([])
+QTAPP = QtGui.QApplication([])
-class TextStorage(object):
- def __init__(self, columns):
- super(TextStorage, self).__init__()
- self.column_count = columns
- self.colors = (QtCore.Qt.red, QtCore.Qt.green, QtGui.QColor(46, 100, 254))
+class ExclusiveTextStorage(object):
+ """Stores the text representation of per actor osc messages"""
- 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.column_count = columns
+ self.colors = (
+ QtCore.Qt.red, QtCore.Qt.green, QtGui.QColor(46, 100, 254))
+ 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(775, 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.default_font = default_font
- 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)
+ self.data = deque()
def init_columns(self):
color = self.colors[0]
- for y in range(self.num_lines):
+ for line_index in range(self.num_lines):
text_item = self.graphics_scene.addSimpleText("", self.default_font)
text_item.setBrush(color)
- text_item.setPos(0, y * self.line_height)
+ text_item.setPos(0, line_index * 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)
+ text_item.setBrush(self.colors[column])
+ old_item = self.lines.popleft()
self.graphics_scene.removeItem(old_item)
self.lines.append(text_item)
- for iy, text_item in enumerate(self.lines):
- text_item.setY(iy * self.line_height)
+
+ def finish(self):
+ for column, text in self.data:
+ self.__add_text(column, text)
+ self.data.clear()
+
+ for text_index, text_item in enumerate(self.lines):
+ text_item.setY(text_index * self.line_height)
+
+ def add_text(self, column, text):
+ self.data.append((column, text))
-class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
- def __init__(self, parent=None, columns=3, column_exclusive=False):
- super(MainWindow, self).__init__(parent)
+class MainWindow(QtGui.QMainWindow, Ui_MainWindow,
+ MjpegStreamingConsumerInterface, PsyQtChaoscClientBase):
+
+ """This app receives per actor osc messages and provides an mjpeg stream
+ with colored text representation arranged in columns"""
+
+ def __init__(self, args, parent=None):
+ self.args = args
+ super(MainWindow, self).__init__()
self.setupUi(self)
- self.graphics_view.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
- self.graphics_view.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
+ 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)
self.graphics_view.setFrameStyle(QtGui.QFrame.NoFrame)
self.graphics_scene = QtGui.QGraphicsScene(self)
- self.graphics_scene.setSceneRect(0,0, 775, 580)
+ self.graphics_scene.setSceneRect(0, 0, 775, 580)
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.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 = ColumnTextStorage(columns, self.default_font, self.column_width, self.line_height, self.graphics_scene)
+ self.text_storage = ExclusiveTextStorage(columns, self.default_font,
+ self.column_width,
+ self.line_height,
+ self.graphics_scene)
self.text_storage.init_columns()
+ 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")
+ 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):
- image = QtGui.QImage(768, 576, QtGui.QImage.Format_ARGB32_Premultiplied)
+ def render_image(self):
+ self.text_storage.finish()
+ image = QPixmap(768, 576)
image.fill(QtCore.Qt.black)
- painter = QtGui.QPainter(image)
- painter.setRenderHints(QtGui.QPainter.RenderHint(QtGui.QPainter.Antialiasing | QtGui.QPainter.TextAntialiasing), True)
+ painter = QPainter(image)
+ painter.setRenderHints(QPainter.RenderHint(
+ QPainter.Antialiasing | 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", 100)
+ 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 ValueError:
+ 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 text == "temperatur":
+ text += "e"
+ 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():
@@ -319,21 +193,23 @@ def main():
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 "::"')
+ help='my host, defaults to "::"')
arg_parser.add_argument(client_group, '-X', "--http_port", default=9001,
- type=int, help='my port, defaults to 9001')
+ type=int, help='my port, defaults to 9001')
arg_parser.add_chaosc_group()
arg_parser.add_subscriber_group()
args = arg_parser.finalize()
- http_host, http_port = resolve_host(args.http_host, args.http_port, args.address_family)
+ 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)
- 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)
+ window = MainWindow(args)
+ sys.excepthook = window.sigint_handler
+ signal.signal(signal.SIGTERM, window.sigterm_handler)
+ QTAPP.exec_()
- server.serve_forever()
-if ( __name__ == '__main__' ):
+if __name__ == '__main__':
main()
diff --git a/dump_grabber/setup.py b/dump_grabber/setup.py
index 48cf6f7..39c5f87 100644
--- a/dump_grabber/setup.py
+++ b/dump_grabber/setup.py
@@ -1,9 +1,6 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
-from distribute_setup import use_setuptools
-use_setuptools()
-
import sys
from setuptools import find_packages, setup
@@ -12,13 +9,15 @@ if sys.version_info >= (3,):
setup(
name='dump_grabber',
- version="0.1",
+ version="0.2",
packages=find_packages(exclude=["scripts",]),
include_package_data = True,
exclude_package_data = {'': ['.gitignore']},
+ install_requires = ["psylib"],
+
# installing unzipped
zip_safe = False,
diff --git a/ekgplotter/distribute_setup.py b/ekgplotter/distribute_setup.py
new file mode 100644
index 0000000..3553b21
--- /dev/null
+++ b/ekgplotter/distribute_setup.py
@@ -0,0 +1,556 @@
+#!python
+"""Bootstrap distribute installation
+
+If you want to use setuptools in your package's setup.py, just include this
+file in the same directory with it, and add this to the top of your setup.py::
+
+ from distribute_setup import use_setuptools
+ use_setuptools()
+
+If you want to require a specific version of setuptools, set a download
+mirror, or use an alternate download directory, you can do so by supplying
+the appropriate options to ``use_setuptools()``.
+
+This file can also be run as a script to install or upgrade setuptools.
+"""
+import os
+import shutil
+import sys
+import time
+import fnmatch
+import tempfile
+import tarfile
+import optparse
+
+from distutils import log
+
+try:
+ from site import USER_SITE
+except ImportError:
+ USER_SITE = None
+
+try:
+ import subprocess
+
+ def _python_cmd(*args):
+ args = (sys.executable,) + args
+ return subprocess.call(args) == 0
+
+except ImportError:
+ # will be used for python 2.3
+ def _python_cmd(*args):
+ args = (sys.executable,) + args
+ # quoting arguments if windows
+ if sys.platform == 'win32':
+ def quote(arg):
+ if ' ' in arg:
+ return '"%s"' % arg
+ return arg
+ args = [quote(arg) for arg in args]
+ return os.spawnl(os.P_WAIT, sys.executable, *args) == 0
+
+DEFAULT_VERSION = "0.6.49"
+DEFAULT_URL = "http://pypi.python.org/packages/source/d/distribute/"
+SETUPTOOLS_FAKED_VERSION = "0.6c11"
+
+SETUPTOOLS_PKG_INFO = """\
+Metadata-Version: 1.0
+Name: setuptools
+Version: %s
+Summary: xxxx
+Home-page: xxx
+Author: xxx
+Author-email: xxx
+License: xxx
+Description: xxx
+""" % SETUPTOOLS_FAKED_VERSION
+
+
+def _install(tarball, install_args=()):
+ # extracting the tarball
+ tmpdir = tempfile.mkdtemp()
+ log.warn('Extracting in %s', tmpdir)
+ old_wd = os.getcwd()
+ try:
+ os.chdir(tmpdir)
+ tar = tarfile.open(tarball)
+ _extractall(tar)
+ tar.close()
+
+ # going in the directory
+ subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0])
+ os.chdir(subdir)
+ log.warn('Now working in %s', subdir)
+
+ # installing
+ log.warn('Installing Distribute')
+ if not _python_cmd('setup.py', 'install', *install_args):
+ log.warn('Something went wrong during the installation.')
+ log.warn('See the error message above.')
+ # exitcode will be 2
+ return 2
+ finally:
+ os.chdir(old_wd)
+ shutil.rmtree(tmpdir)
+
+
+def _build_egg(egg, tarball, to_dir):
+ # extracting the tarball
+ tmpdir = tempfile.mkdtemp()
+ log.warn('Extracting in %s', tmpdir)
+ old_wd = os.getcwd()
+ try:
+ os.chdir(tmpdir)
+ tar = tarfile.open(tarball)
+ _extractall(tar)
+ tar.close()
+
+ # going in the directory
+ subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0])
+ os.chdir(subdir)
+ log.warn('Now working in %s', subdir)
+
+ # building an egg
+ log.warn('Building a Distribute egg in %s', to_dir)
+ _python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir)
+
+ finally:
+ os.chdir(old_wd)
+ shutil.rmtree(tmpdir)
+ # returning the result
+ log.warn(egg)
+ if not os.path.exists(egg):
+ raise IOError('Could not build the egg.')
+
+
+def _do_download(version, download_base, to_dir, download_delay):
+ egg = os.path.join(to_dir, 'distribute-%s-py%d.%d.egg'
+ % (version, sys.version_info[0], sys.version_info[1]))
+ if not os.path.exists(egg):
+ tarball = download_setuptools(version, download_base,
+ to_dir, download_delay)
+ _build_egg(egg, tarball, to_dir)
+ sys.path.insert(0, egg)
+ import setuptools
+ setuptools.bootstrap_install_from = egg
+
+
+def use_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
+ to_dir=os.curdir, download_delay=15, no_fake=True):
+ # making sure we use the absolute path
+ to_dir = os.path.abspath(to_dir)
+ was_imported = 'pkg_resources' in sys.modules or \
+ 'setuptools' in sys.modules
+ try:
+ try:
+ import pkg_resources
+
+ # Setuptools 0.7b and later is a suitable (and preferable)
+ # substitute for any Distribute version.
+ try:
+ pkg_resources.require("setuptools>=0.7b")
+ return
+ except (pkg_resources.DistributionNotFound,
+ pkg_resources.VersionConflict):
+ pass
+
+ if not hasattr(pkg_resources, '_distribute'):
+ if not no_fake:
+ _fake_setuptools()
+ raise ImportError
+ except ImportError:
+ return _do_download(version, download_base, to_dir, download_delay)
+ try:
+ pkg_resources.require("distribute>=" + version)
+ return
+ except pkg_resources.VersionConflict:
+ e = sys.exc_info()[1]
+ if was_imported:
+ sys.stderr.write(
+ "The required version of distribute (>=%s) is not available,\n"
+ "and can't be installed while this script is running. Please\n"
+ "install a more recent version first, using\n"
+ "'easy_install -U distribute'."
+ "\n\n(Currently using %r)\n" % (version, e.args[0]))
+ sys.exit(2)
+ else:
+ del pkg_resources, sys.modules['pkg_resources'] # reload ok
+ return _do_download(version, download_base, to_dir,
+ download_delay)
+ except pkg_resources.DistributionNotFound:
+ return _do_download(version, download_base, to_dir,
+ download_delay)
+ finally:
+ if not no_fake:
+ _create_fake_setuptools_pkg_info(to_dir)
+
+
+def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
+ to_dir=os.curdir, delay=15):
+ """Download distribute from a specified location and return its filename
+
+ `version` should be a valid distribute version number that is available
+ as an egg for download under the `download_base` URL (which should end
+ with a '/'). `to_dir` is the directory where the egg will be downloaded.
+ `delay` is the number of seconds to pause before an actual download
+ attempt.
+ """
+ # making sure we use the absolute path
+ to_dir = os.path.abspath(to_dir)
+ try:
+ from urllib.request import urlopen
+ except ImportError:
+ from urllib2 import urlopen
+ tgz_name = "distribute-%s.tar.gz" % version
+ url = download_base + tgz_name
+ saveto = os.path.join(to_dir, tgz_name)
+ src = dst = None
+ if not os.path.exists(saveto): # Avoid repeated downloads
+ try:
+ log.warn("Downloading %s", url)
+ src = urlopen(url)
+ # Read/write all in one block, so we don't create a corrupt file
+ # if the download is interrupted.
+ data = src.read()
+ dst = open(saveto, "wb")
+ dst.write(data)
+ finally:
+ if src:
+ src.close()
+ if dst:
+ dst.close()
+ return os.path.realpath(saveto)
+
+
+def _no_sandbox(function):
+ def __no_sandbox(*args, **kw):
+ try:
+ from setuptools.sandbox import DirectorySandbox
+ if not hasattr(DirectorySandbox, '_old'):
+ def violation(*args):
+ pass
+ DirectorySandbox._old = DirectorySandbox._violation
+ DirectorySandbox._violation = violation
+ patched = True
+ else:
+ patched = False
+ except ImportError:
+ patched = False
+
+ try:
+ return function(*args, **kw)
+ finally:
+ if patched:
+ DirectorySandbox._violation = DirectorySandbox._old
+ del DirectorySandbox._old
+
+ return __no_sandbox
+
+
+def _patch_file(path, content):
+ """Will backup the file then patch it"""
+ f = open(path)
+ existing_content = f.read()
+ f.close()
+ if existing_content == content:
+ # already patched
+ log.warn('Already patched.')
+ return False
+ log.warn('Patching...')
+ _rename_path(path)
+ f = open(path, 'w')
+ try:
+ f.write(content)
+ finally:
+ f.close()
+ return True
+
+_patch_file = _no_sandbox(_patch_file)
+
+
+def _same_content(path, content):
+ f = open(path)
+ existing_content = f.read()
+ f.close()
+ return existing_content == content
+
+
+def _rename_path(path):
+ new_name = path + '.OLD.%s' % time.time()
+ log.warn('Renaming %s to %s', path, new_name)
+ os.rename(path, new_name)
+ return new_name
+
+
+def _remove_flat_installation(placeholder):
+ if not os.path.isdir(placeholder):
+ log.warn('Unkown installation at %s', placeholder)
+ return False
+ found = False
+ for file in os.listdir(placeholder):
+ if fnmatch.fnmatch(file, 'setuptools*.egg-info'):
+ found = True
+ break
+ if not found:
+ log.warn('Could not locate setuptools*.egg-info')
+ return
+
+ log.warn('Moving elements out of the way...')
+ pkg_info = os.path.join(placeholder, file)
+ if os.path.isdir(pkg_info):
+ patched = _patch_egg_dir(pkg_info)
+ else:
+ patched = _patch_file(pkg_info, SETUPTOOLS_PKG_INFO)
+
+ if not patched:
+ log.warn('%s already patched.', pkg_info)
+ return False
+ # now let's move the files out of the way
+ for element in ('setuptools', 'pkg_resources.py', 'site.py'):
+ element = os.path.join(placeholder, element)
+ if os.path.exists(element):
+ _rename_path(element)
+ else:
+ log.warn('Could not find the %s element of the '
+ 'Setuptools distribution', element)
+ return True
+
+_remove_flat_installation = _no_sandbox(_remove_flat_installation)
+
+
+def _after_install(dist):
+ log.warn('After install bootstrap.')
+ placeholder = dist.get_command_obj('install').install_purelib
+ _create_fake_setuptools_pkg_info(placeholder)
+
+
+def _create_fake_setuptools_pkg_info(placeholder):
+ if not placeholder or not os.path.exists(placeholder):
+ log.warn('Could not find the install location')
+ return
+ pyver = '%s.%s' % (sys.version_info[0], sys.version_info[1])
+ setuptools_file = 'setuptools-%s-py%s.egg-info' % \
+ (SETUPTOOLS_FAKED_VERSION, pyver)
+ pkg_info = os.path.join(placeholder, setuptools_file)
+ if os.path.exists(pkg_info):
+ log.warn('%s already exists', pkg_info)
+ return
+
+ log.warn('Creating %s', pkg_info)
+ try:
+ f = open(pkg_info, 'w')
+ except EnvironmentError:
+ log.warn("Don't have permissions to write %s, skipping", pkg_info)
+ return
+ try:
+ f.write(SETUPTOOLS_PKG_INFO)
+ finally:
+ f.close()
+
+ pth_file = os.path.join(placeholder, 'setuptools.pth')
+ log.warn('Creating %s', pth_file)
+ f = open(pth_file, 'w')
+ try:
+ f.write(os.path.join(os.curdir, setuptools_file))
+ finally:
+ f.close()
+
+_create_fake_setuptools_pkg_info = _no_sandbox(
+ _create_fake_setuptools_pkg_info
+)
+
+
+def _patch_egg_dir(path):
+ # let's check if it's already patched
+ pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO')
+ if os.path.exists(pkg_info):
+ if _same_content(pkg_info, SETUPTOOLS_PKG_INFO):
+ log.warn('%s already patched.', pkg_info)
+ return False
+ _rename_path(path)
+ os.mkdir(path)
+ os.mkdir(os.path.join(path, 'EGG-INFO'))
+ pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO')
+ f = open(pkg_info, 'w')
+ try:
+ f.write(SETUPTOOLS_PKG_INFO)
+ finally:
+ f.close()
+ return True
+
+_patch_egg_dir = _no_sandbox(_patch_egg_dir)
+
+
+def _before_install():
+ log.warn('Before install bootstrap.')
+ _fake_setuptools()
+
+
+def _under_prefix(location):
+ if 'install' not in sys.argv:
+ return True
+ args = sys.argv[sys.argv.index('install') + 1:]
+ for index, arg in enumerate(args):
+ for option in ('--root', '--prefix'):
+ if arg.startswith('%s=' % option):
+ top_dir = arg.split('root=')[-1]
+ return location.startswith(top_dir)
+ elif arg == option:
+ if len(args) > index:
+ top_dir = args[index + 1]
+ return location.startswith(top_dir)
+ if arg == '--user' and USER_SITE is not None:
+ return location.startswith(USER_SITE)
+ return True
+
+
+def _fake_setuptools():
+ log.warn('Scanning installed packages')
+ try:
+ import pkg_resources
+ except ImportError:
+ # we're cool
+ log.warn('Setuptools or Distribute does not seem to be installed.')
+ return
+ ws = pkg_resources.working_set
+ try:
+ setuptools_dist = ws.find(
+ pkg_resources.Requirement.parse('setuptools', replacement=False)
+ )
+ except TypeError:
+ # old distribute API
+ setuptools_dist = ws.find(
+ pkg_resources.Requirement.parse('setuptools')
+ )
+
+ if setuptools_dist is None:
+ log.warn('No setuptools distribution found')
+ return
+ # detecting if it was already faked
+ setuptools_location = setuptools_dist.location
+ log.warn('Setuptools installation detected at %s', setuptools_location)
+
+ # if --root or --preix was provided, and if
+ # setuptools is not located in them, we don't patch it
+ if not _under_prefix(setuptools_location):
+ log.warn('Not patching, --root or --prefix is installing Distribute'
+ ' in another location')
+ return
+
+ # let's see if its an egg
+ if not setuptools_location.endswith('.egg'):
+ log.warn('Non-egg installation')
+ res = _remove_flat_installation(setuptools_location)
+ if not res:
+ return
+ else:
+ log.warn('Egg installation')
+ pkg_info = os.path.join(setuptools_location, 'EGG-INFO', 'PKG-INFO')
+ if (os.path.exists(pkg_info) and
+ _same_content(pkg_info, SETUPTOOLS_PKG_INFO)):
+ log.warn('Already patched.')
+ return
+ log.warn('Patching...')
+ # let's create a fake egg replacing setuptools one
+ res = _patch_egg_dir(setuptools_location)
+ if not res:
+ return
+ log.warn('Patching complete.')
+ _relaunch()
+
+
+def _relaunch():
+ log.warn('Relaunching...')
+ # we have to relaunch the process
+ # pip marker to avoid a relaunch bug
+ _cmd1 = ['-c', 'install', '--single-version-externally-managed']
+ _cmd2 = ['-c', 'install', '--record']
+ if sys.argv[:3] == _cmd1 or sys.argv[:3] == _cmd2:
+ sys.argv[0] = 'setup.py'
+ args = [sys.executable] + sys.argv
+ sys.exit(subprocess.call(args))
+
+
+def _extractall(self, path=".", members=None):
+ """Extract all members from the archive to the current working
+ directory and set owner, modification time and permissions on
+ directories afterwards. `path' specifies a different directory
+ to extract to. `members' is optional and must be a subset of the
+ list returned by getmembers().
+ """
+ import copy
+ import operator
+ from tarfile import ExtractError
+ directories = []
+
+ if members is None:
+ members = self
+
+ for tarinfo in members:
+ if tarinfo.isdir():
+ # Extract directories with a safe mode.
+ directories.append(tarinfo)
+ tarinfo = copy.copy(tarinfo)
+ tarinfo.mode = 448 # decimal for oct 0700
+ self.extract(tarinfo, path)
+
+ # Reverse sort directories.
+ if sys.version_info < (2, 4):
+ def sorter(dir1, dir2):
+ return cmp(dir1.name, dir2.name)
+ directories.sort(sorter)
+ directories.reverse()
+ else:
+ directories.sort(key=operator.attrgetter('name'), reverse=True)
+
+ # Set correct owner, mtime and filemode on directories.
+ for tarinfo in directories:
+ dirpath = os.path.join(path, tarinfo.name)
+ try:
+ self.chown(tarinfo, dirpath)
+ self.utime(tarinfo, dirpath)
+ self.chmod(tarinfo, dirpath)
+ except ExtractError:
+ e = sys.exc_info()[1]
+ if self.errorlevel > 1:
+ raise
+ else:
+ self._dbg(1, "tarfile: %s" % e)
+
+
+def _build_install_args(options):
+ """
+ Build the arguments to 'python setup.py install' on the distribute package
+ """
+ install_args = []
+ if options.user_install:
+ if sys.version_info < (2, 6):
+ log.warn("--user requires Python 2.6 or later")
+ raise SystemExit(1)
+ install_args.append('--user')
+ return install_args
+
+def _parse_args():
+ """
+ Parse the command line for options
+ """
+ parser = optparse.OptionParser()
+ parser.add_option(
+ '--user', dest='user_install', action='store_true', default=False,
+ help='install in user site package (requires Python 2.6 or later)')
+ parser.add_option(
+ '--download-base', dest='download_base', metavar="URL",
+ default=DEFAULT_URL,
+ help='alternative URL from where to download the distribute package')
+ options, args = parser.parse_args()
+ # positional arguments are ignored
+ return options
+
+def main(version=DEFAULT_VERSION):
+ """Install or upgrade setuptools and EasyInstall"""
+ options = _parse_args()
+ tarball = download_setuptools(download_base=options.download_base)
+ return _install(tarball, _build_install_args(options))
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/ekgplotter/ekgplotter/index.html b/ekgplotter/ekgplotter/index.html
index 3f46bbb..81a9f75 100644
--- a/ekgplotter/ekgplotter/index.html
+++ b/ekgplotter/ekgplotter/index.html
@@ -1,7 +1,7 @@
-
+
diff --git a/ekgplotter/ekgplotter/main_qt.py b/ekgplotter/ekgplotter/main_qt.py
new file mode 100644
index 0000000..7b40170
--- /dev/null
+++ b/ekgplotter/ekgplotter/main_qt.py
@@ -0,0 +1,282 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# This file is part of psychose/ekgplotter package
+#
+# psychose/ekgplotter 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.
+#
+# psychose/ekgplotter 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 psychose/ekgplotter. If not, see .
+#
+# Copyright (C) 2014 Stefan Kögl
+
+from __future__ import absolute_import
+
+import random
+import os.path
+import re
+import signal
+import sys
+import exceptions
+
+from PyQt4 import QtCore, QtGui
+from PyQt4.QtGui import QImage, QPixmap, QMainWindow
+from PyQt4.QtCore import QBuffer, QIODevice
+
+import numpy as np
+
+import pyqtgraph as pg
+from pyqtgraph.widgets.PlotWidget import PlotWidget
+from pyqtgraph.graphicsItems.PlotCurveItem import PlotCurveItem
+from pyqtgraph.graphicsItems.ScatterPlotItem import ScatterPlotItem
+
+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 decode_osc
+except ImportError as e:
+ from chaosc.osc_lib import decode_osc
+
+qtapp = QtGui.QApplication([])
+
+
+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=80):
+ self.count = 0
+ self.pulse = random.randint(85, 105)
+ self.delta = delta
+ self.finished = False
+ self.steps, _ = get_steps(self.pulse, delta / 4)
+
+ def __call__(self):
+ while 1:
+ if self.count < int(self.steps / 100. * 30):
+ value = random.randint(30, 35)
+ elif self.count == int(self.steps / 100. * 30):
+ value = random.randint(55, 66)
+ 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(self.steps / 100. * 60):
+ value = random.randint(25, 35)
+ elif self.count < int(self.steps / 100. * 70):
+ value = random.randint(10, 30)
+ elif self.count < self.steps:
+ value = random.randint(15, 30)
+ else:
+ self.finished = True
+ 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)
+
+ 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.offset = ix * actor_height
+ self.data = np.array([self.offset + 30] * num_data)
+ self.head = 0
+ self.pre_head = 0
+ self.plotItem = PlotCurveItem(pen=pg.mkPen(color, width=3), width=4, name=name)
+ self.plotPoint = ScatterPlotItem(pen=pg.mkPen("w", width=5), brush=pg.mkBrush(color), size=5)
+ self.osci = None
+ self.osci_obj = None
+ self.render()
+
+ def __str__(self):
+ return "" % (self.name, self.head)
+
+ def __repr__(self):
+ return "Actor(%r, %r, %r, %r, %r, %r)" % (self.name, self.num_data,
+ self.color, self.ix, self.max_actors, self.actor_height)
+
+ def add_value(self, value):
+ self.pre_head = self.head
+ self.data[self.pre_head] = value / self.max_actors + self.offset
+ self.head = (self.pre_head + 1) % self.num_data
+
+ 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.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 PlotWindow(PlotWidget):
+ def __init__(self, title=None, **kargs):
+ self.win = QtGui.QMainWindow()
+ self.win.resize(768, 576)
+ PlotWidget.__init__(self, **kargs)
+ self.win.setCentralWidget(self)
+ for m in ['resize']:
+ setattr(self, m, getattr(self.win, m))
+ if title is not None:
+ self.win.setWindowTitle(title)
+ self.win.show()
+
+
+class EkgPlotWidget(QMainWindow, PsyQtChaoscClientBase, MjpegStreamingConsumerInterface):
+ def __init__(self, args, parent=None):
+ self.args = args
+ super(EkgPlotWidget, self).__init__()
+ PsyQtChaoscClientBase.__init__(self)
+ self.plot_widget = PlotWidget(title="Psychose - EkgPlotter")
+ colors = ["r", "g", "b"]
+ self.active_actors = list()
+ self.actors = dict()
+ self.max_value = 255
+ actor_names = ["merle", "uwe", "bjoern"]
+ self.max_actors = len(actor_names)
+ self.actor_height = self.max_value / self.max_actors
+ 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.num_data = 100
+ self.plot_widget.showGrid(False, False)
+ self.plot_widget.setYRange(0, 255)
+ self.plot_widget.setXRange(0, self.num_data)
+ self.plot_widget.resize(768, 576)
+
+ bottom_axis = self.plot_widget.getAxis("bottom")
+ left_axis = self.plot_widget.getAxis("left")
+ bottom_axis.setTicks([])
+ left_axis.setTicks([])
+ bottom_axis.hide()
+ left_axis.hide()
+
+ 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.heartbeat_regex = re.compile("^/(.*?)/heartbeat$")
+
+ 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):
+ actor_obj = Actor(actor_name, num_data, color, ix, max_actors, actor_height)
+ self.actors[actor_name] = actor_obj
+ self.plot_widget.addItem(actor_obj.plotItem)
+ self.plot_widget.addItem(actor_obj.plotPoint)
+ self.active_actors.append(actor_obj)
+ actor_obj.osci_obj = Generator(pulse=random.randint(88, 104), 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):
+ actor_obj.plotItem.setPos(0, ix * 2)
+ actor_obj.plotPoint.setPos(0, ix * 2)
+
+ def active_actor_count(self):
+ return self.max_actors
+
+ def update(self, osc_address, args):
+ res = self.heartbeat_regex.match(osc_address)
+ if res:
+ actor_name = res.group(1)
+ actor_obj = self.actors[actor_name]
+ #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_image(self):
+ for actor_obj in self.active_actors:
+ actor_obj.add_value(actor_obj.osci.next())
+ actor_obj.add_value(actor_obj.osci.next())
+ actor_obj.add_value(actor_obj.osci.next())
+ actor_obj.add_value(actor_obj.osci.next())
+ actor_obj.render()
+ image = QPixmap(768, 576)
+ image.fill(QtCore.Qt.white)
+ painter = QtGui.QPainter(image)
+ painter.setRenderHints(QtGui.QPainter.RenderHint(
+ QtGui.QPainter.Antialiasing | QtGui.QPainter.TextAntialiasing),
+ True)
+ scene = self.plot_widget.plotItem.scene()
+ scene.render(painter, QtCore.QRectF(0, 0, 768, 576), QtCore.QRectF(0, 0, 768, 576))
+ painter.end()
+ buf = QBuffer()
+ buf.open(QIODevice.WriteOnly)
+ image.save(buf, "JPG", 80)
+ JpegData = buf.data()
+ 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 ValueError, error:
+ logger.exception(error)
+ else:
+ self.update(osc_address, args)
+
+
+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)
+ args.chaosc_host, args.chaosc_port = resolve_host(args.chaosc_host, args.chaosc_port, args.address_family)
+
+ window = EkgPlotWidget(args)
+ logger.info("foooooooo")
+ window.hide()
+ #sys.excepthook = window.sigint_handler
+ #signal.signal(signal.SIGTERM, window.sigterm_handler)
+ qtapp.exec_()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ekgplotter/setup.py b/ekgplotter/setup.py
index 3cb5b9a..180b61e 100644
--- a/ekgplotter/setup.py
+++ b/ekgplotter/setup.py
@@ -1,9 +1,6 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
-from distribute_setup import use_setuptools
-use_setuptools()
-
import sys
from setuptools import find_packages, setup
@@ -12,7 +9,7 @@ if sys.version_info >= (3,):
setup(
name='ekgplotter',
- version="0.1",
+ version="0.2",
packages=find_packages(exclude=["scripts",]),
include_package_data = True,
@@ -22,7 +19,7 @@ setup(
exclude_package_data = {'': ['.gitignore']},
- install_requires=["pyqtgraph"],
+ install_requires=["psylib", "pyqtgraph"],
# installing unzipped
zip_safe = False,
@@ -30,7 +27,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/psylib/distribute_setup.py b/psylib/distribute_setup.py
new file mode 100644
index 0000000..3553b21
--- /dev/null
+++ b/psylib/distribute_setup.py
@@ -0,0 +1,556 @@
+#!python
+"""Bootstrap distribute installation
+
+If you want to use setuptools in your package's setup.py, just include this
+file in the same directory with it, and add this to the top of your setup.py::
+
+ from distribute_setup import use_setuptools
+ use_setuptools()
+
+If you want to require a specific version of setuptools, set a download
+mirror, or use an alternate download directory, you can do so by supplying
+the appropriate options to ``use_setuptools()``.
+
+This file can also be run as a script to install or upgrade setuptools.
+"""
+import os
+import shutil
+import sys
+import time
+import fnmatch
+import tempfile
+import tarfile
+import optparse
+
+from distutils import log
+
+try:
+ from site import USER_SITE
+except ImportError:
+ USER_SITE = None
+
+try:
+ import subprocess
+
+ def _python_cmd(*args):
+ args = (sys.executable,) + args
+ return subprocess.call(args) == 0
+
+except ImportError:
+ # will be used for python 2.3
+ def _python_cmd(*args):
+ args = (sys.executable,) + args
+ # quoting arguments if windows
+ if sys.platform == 'win32':
+ def quote(arg):
+ if ' ' in arg:
+ return '"%s"' % arg
+ return arg
+ args = [quote(arg) for arg in args]
+ return os.spawnl(os.P_WAIT, sys.executable, *args) == 0
+
+DEFAULT_VERSION = "0.6.49"
+DEFAULT_URL = "http://pypi.python.org/packages/source/d/distribute/"
+SETUPTOOLS_FAKED_VERSION = "0.6c11"
+
+SETUPTOOLS_PKG_INFO = """\
+Metadata-Version: 1.0
+Name: setuptools
+Version: %s
+Summary: xxxx
+Home-page: xxx
+Author: xxx
+Author-email: xxx
+License: xxx
+Description: xxx
+""" % SETUPTOOLS_FAKED_VERSION
+
+
+def _install(tarball, install_args=()):
+ # extracting the tarball
+ tmpdir = tempfile.mkdtemp()
+ log.warn('Extracting in %s', tmpdir)
+ old_wd = os.getcwd()
+ try:
+ os.chdir(tmpdir)
+ tar = tarfile.open(tarball)
+ _extractall(tar)
+ tar.close()
+
+ # going in the directory
+ subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0])
+ os.chdir(subdir)
+ log.warn('Now working in %s', subdir)
+
+ # installing
+ log.warn('Installing Distribute')
+ if not _python_cmd('setup.py', 'install', *install_args):
+ log.warn('Something went wrong during the installation.')
+ log.warn('See the error message above.')
+ # exitcode will be 2
+ return 2
+ finally:
+ os.chdir(old_wd)
+ shutil.rmtree(tmpdir)
+
+
+def _build_egg(egg, tarball, to_dir):
+ # extracting the tarball
+ tmpdir = tempfile.mkdtemp()
+ log.warn('Extracting in %s', tmpdir)
+ old_wd = os.getcwd()
+ try:
+ os.chdir(tmpdir)
+ tar = tarfile.open(tarball)
+ _extractall(tar)
+ tar.close()
+
+ # going in the directory
+ subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0])
+ os.chdir(subdir)
+ log.warn('Now working in %s', subdir)
+
+ # building an egg
+ log.warn('Building a Distribute egg in %s', to_dir)
+ _python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir)
+
+ finally:
+ os.chdir(old_wd)
+ shutil.rmtree(tmpdir)
+ # returning the result
+ log.warn(egg)
+ if not os.path.exists(egg):
+ raise IOError('Could not build the egg.')
+
+
+def _do_download(version, download_base, to_dir, download_delay):
+ egg = os.path.join(to_dir, 'distribute-%s-py%d.%d.egg'
+ % (version, sys.version_info[0], sys.version_info[1]))
+ if not os.path.exists(egg):
+ tarball = download_setuptools(version, download_base,
+ to_dir, download_delay)
+ _build_egg(egg, tarball, to_dir)
+ sys.path.insert(0, egg)
+ import setuptools
+ setuptools.bootstrap_install_from = egg
+
+
+def use_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
+ to_dir=os.curdir, download_delay=15, no_fake=True):
+ # making sure we use the absolute path
+ to_dir = os.path.abspath(to_dir)
+ was_imported = 'pkg_resources' in sys.modules or \
+ 'setuptools' in sys.modules
+ try:
+ try:
+ import pkg_resources
+
+ # Setuptools 0.7b and later is a suitable (and preferable)
+ # substitute for any Distribute version.
+ try:
+ pkg_resources.require("setuptools>=0.7b")
+ return
+ except (pkg_resources.DistributionNotFound,
+ pkg_resources.VersionConflict):
+ pass
+
+ if not hasattr(pkg_resources, '_distribute'):
+ if not no_fake:
+ _fake_setuptools()
+ raise ImportError
+ except ImportError:
+ return _do_download(version, download_base, to_dir, download_delay)
+ try:
+ pkg_resources.require("distribute>=" + version)
+ return
+ except pkg_resources.VersionConflict:
+ e = sys.exc_info()[1]
+ if was_imported:
+ sys.stderr.write(
+ "The required version of distribute (>=%s) is not available,\n"
+ "and can't be installed while this script is running. Please\n"
+ "install a more recent version first, using\n"
+ "'easy_install -U distribute'."
+ "\n\n(Currently using %r)\n" % (version, e.args[0]))
+ sys.exit(2)
+ else:
+ del pkg_resources, sys.modules['pkg_resources'] # reload ok
+ return _do_download(version, download_base, to_dir,
+ download_delay)
+ except pkg_resources.DistributionNotFound:
+ return _do_download(version, download_base, to_dir,
+ download_delay)
+ finally:
+ if not no_fake:
+ _create_fake_setuptools_pkg_info(to_dir)
+
+
+def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
+ to_dir=os.curdir, delay=15):
+ """Download distribute from a specified location and return its filename
+
+ `version` should be a valid distribute version number that is available
+ as an egg for download under the `download_base` URL (which should end
+ with a '/'). `to_dir` is the directory where the egg will be downloaded.
+ `delay` is the number of seconds to pause before an actual download
+ attempt.
+ """
+ # making sure we use the absolute path
+ to_dir = os.path.abspath(to_dir)
+ try:
+ from urllib.request import urlopen
+ except ImportError:
+ from urllib2 import urlopen
+ tgz_name = "distribute-%s.tar.gz" % version
+ url = download_base + tgz_name
+ saveto = os.path.join(to_dir, tgz_name)
+ src = dst = None
+ if not os.path.exists(saveto): # Avoid repeated downloads
+ try:
+ log.warn("Downloading %s", url)
+ src = urlopen(url)
+ # Read/write all in one block, so we don't create a corrupt file
+ # if the download is interrupted.
+ data = src.read()
+ dst = open(saveto, "wb")
+ dst.write(data)
+ finally:
+ if src:
+ src.close()
+ if dst:
+ dst.close()
+ return os.path.realpath(saveto)
+
+
+def _no_sandbox(function):
+ def __no_sandbox(*args, **kw):
+ try:
+ from setuptools.sandbox import DirectorySandbox
+ if not hasattr(DirectorySandbox, '_old'):
+ def violation(*args):
+ pass
+ DirectorySandbox._old = DirectorySandbox._violation
+ DirectorySandbox._violation = violation
+ patched = True
+ else:
+ patched = False
+ except ImportError:
+ patched = False
+
+ try:
+ return function(*args, **kw)
+ finally:
+ if patched:
+ DirectorySandbox._violation = DirectorySandbox._old
+ del DirectorySandbox._old
+
+ return __no_sandbox
+
+
+def _patch_file(path, content):
+ """Will backup the file then patch it"""
+ f = open(path)
+ existing_content = f.read()
+ f.close()
+ if existing_content == content:
+ # already patched
+ log.warn('Already patched.')
+ return False
+ log.warn('Patching...')
+ _rename_path(path)
+ f = open(path, 'w')
+ try:
+ f.write(content)
+ finally:
+ f.close()
+ return True
+
+_patch_file = _no_sandbox(_patch_file)
+
+
+def _same_content(path, content):
+ f = open(path)
+ existing_content = f.read()
+ f.close()
+ return existing_content == content
+
+
+def _rename_path(path):
+ new_name = path + '.OLD.%s' % time.time()
+ log.warn('Renaming %s to %s', path, new_name)
+ os.rename(path, new_name)
+ return new_name
+
+
+def _remove_flat_installation(placeholder):
+ if not os.path.isdir(placeholder):
+ log.warn('Unkown installation at %s', placeholder)
+ return False
+ found = False
+ for file in os.listdir(placeholder):
+ if fnmatch.fnmatch(file, 'setuptools*.egg-info'):
+ found = True
+ break
+ if not found:
+ log.warn('Could not locate setuptools*.egg-info')
+ return
+
+ log.warn('Moving elements out of the way...')
+ pkg_info = os.path.join(placeholder, file)
+ if os.path.isdir(pkg_info):
+ patched = _patch_egg_dir(pkg_info)
+ else:
+ patched = _patch_file(pkg_info, SETUPTOOLS_PKG_INFO)
+
+ if not patched:
+ log.warn('%s already patched.', pkg_info)
+ return False
+ # now let's move the files out of the way
+ for element in ('setuptools', 'pkg_resources.py', 'site.py'):
+ element = os.path.join(placeholder, element)
+ if os.path.exists(element):
+ _rename_path(element)
+ else:
+ log.warn('Could not find the %s element of the '
+ 'Setuptools distribution', element)
+ return True
+
+_remove_flat_installation = _no_sandbox(_remove_flat_installation)
+
+
+def _after_install(dist):
+ log.warn('After install bootstrap.')
+ placeholder = dist.get_command_obj('install').install_purelib
+ _create_fake_setuptools_pkg_info(placeholder)
+
+
+def _create_fake_setuptools_pkg_info(placeholder):
+ if not placeholder or not os.path.exists(placeholder):
+ log.warn('Could not find the install location')
+ return
+ pyver = '%s.%s' % (sys.version_info[0], sys.version_info[1])
+ setuptools_file = 'setuptools-%s-py%s.egg-info' % \
+ (SETUPTOOLS_FAKED_VERSION, pyver)
+ pkg_info = os.path.join(placeholder, setuptools_file)
+ if os.path.exists(pkg_info):
+ log.warn('%s already exists', pkg_info)
+ return
+
+ log.warn('Creating %s', pkg_info)
+ try:
+ f = open(pkg_info, 'w')
+ except EnvironmentError:
+ log.warn("Don't have permissions to write %s, skipping", pkg_info)
+ return
+ try:
+ f.write(SETUPTOOLS_PKG_INFO)
+ finally:
+ f.close()
+
+ pth_file = os.path.join(placeholder, 'setuptools.pth')
+ log.warn('Creating %s', pth_file)
+ f = open(pth_file, 'w')
+ try:
+ f.write(os.path.join(os.curdir, setuptools_file))
+ finally:
+ f.close()
+
+_create_fake_setuptools_pkg_info = _no_sandbox(
+ _create_fake_setuptools_pkg_info
+)
+
+
+def _patch_egg_dir(path):
+ # let's check if it's already patched
+ pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO')
+ if os.path.exists(pkg_info):
+ if _same_content(pkg_info, SETUPTOOLS_PKG_INFO):
+ log.warn('%s already patched.', pkg_info)
+ return False
+ _rename_path(path)
+ os.mkdir(path)
+ os.mkdir(os.path.join(path, 'EGG-INFO'))
+ pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO')
+ f = open(pkg_info, 'w')
+ try:
+ f.write(SETUPTOOLS_PKG_INFO)
+ finally:
+ f.close()
+ return True
+
+_patch_egg_dir = _no_sandbox(_patch_egg_dir)
+
+
+def _before_install():
+ log.warn('Before install bootstrap.')
+ _fake_setuptools()
+
+
+def _under_prefix(location):
+ if 'install' not in sys.argv:
+ return True
+ args = sys.argv[sys.argv.index('install') + 1:]
+ for index, arg in enumerate(args):
+ for option in ('--root', '--prefix'):
+ if arg.startswith('%s=' % option):
+ top_dir = arg.split('root=')[-1]
+ return location.startswith(top_dir)
+ elif arg == option:
+ if len(args) > index:
+ top_dir = args[index + 1]
+ return location.startswith(top_dir)
+ if arg == '--user' and USER_SITE is not None:
+ return location.startswith(USER_SITE)
+ return True
+
+
+def _fake_setuptools():
+ log.warn('Scanning installed packages')
+ try:
+ import pkg_resources
+ except ImportError:
+ # we're cool
+ log.warn('Setuptools or Distribute does not seem to be installed.')
+ return
+ ws = pkg_resources.working_set
+ try:
+ setuptools_dist = ws.find(
+ pkg_resources.Requirement.parse('setuptools', replacement=False)
+ )
+ except TypeError:
+ # old distribute API
+ setuptools_dist = ws.find(
+ pkg_resources.Requirement.parse('setuptools')
+ )
+
+ if setuptools_dist is None:
+ log.warn('No setuptools distribution found')
+ return
+ # detecting if it was already faked
+ setuptools_location = setuptools_dist.location
+ log.warn('Setuptools installation detected at %s', setuptools_location)
+
+ # if --root or --preix was provided, and if
+ # setuptools is not located in them, we don't patch it
+ if not _under_prefix(setuptools_location):
+ log.warn('Not patching, --root or --prefix is installing Distribute'
+ ' in another location')
+ return
+
+ # let's see if its an egg
+ if not setuptools_location.endswith('.egg'):
+ log.warn('Non-egg installation')
+ res = _remove_flat_installation(setuptools_location)
+ if not res:
+ return
+ else:
+ log.warn('Egg installation')
+ pkg_info = os.path.join(setuptools_location, 'EGG-INFO', 'PKG-INFO')
+ if (os.path.exists(pkg_info) and
+ _same_content(pkg_info, SETUPTOOLS_PKG_INFO)):
+ log.warn('Already patched.')
+ return
+ log.warn('Patching...')
+ # let's create a fake egg replacing setuptools one
+ res = _patch_egg_dir(setuptools_location)
+ if not res:
+ return
+ log.warn('Patching complete.')
+ _relaunch()
+
+
+def _relaunch():
+ log.warn('Relaunching...')
+ # we have to relaunch the process
+ # pip marker to avoid a relaunch bug
+ _cmd1 = ['-c', 'install', '--single-version-externally-managed']
+ _cmd2 = ['-c', 'install', '--record']
+ if sys.argv[:3] == _cmd1 or sys.argv[:3] == _cmd2:
+ sys.argv[0] = 'setup.py'
+ args = [sys.executable] + sys.argv
+ sys.exit(subprocess.call(args))
+
+
+def _extractall(self, path=".", members=None):
+ """Extract all members from the archive to the current working
+ directory and set owner, modification time and permissions on
+ directories afterwards. `path' specifies a different directory
+ to extract to. `members' is optional and must be a subset of the
+ list returned by getmembers().
+ """
+ import copy
+ import operator
+ from tarfile import ExtractError
+ directories = []
+
+ if members is None:
+ members = self
+
+ for tarinfo in members:
+ if tarinfo.isdir():
+ # Extract directories with a safe mode.
+ directories.append(tarinfo)
+ tarinfo = copy.copy(tarinfo)
+ tarinfo.mode = 448 # decimal for oct 0700
+ self.extract(tarinfo, path)
+
+ # Reverse sort directories.
+ if sys.version_info < (2, 4):
+ def sorter(dir1, dir2):
+ return cmp(dir1.name, dir2.name)
+ directories.sort(sorter)
+ directories.reverse()
+ else:
+ directories.sort(key=operator.attrgetter('name'), reverse=True)
+
+ # Set correct owner, mtime and filemode on directories.
+ for tarinfo in directories:
+ dirpath = os.path.join(path, tarinfo.name)
+ try:
+ self.chown(tarinfo, dirpath)
+ self.utime(tarinfo, dirpath)
+ self.chmod(tarinfo, dirpath)
+ except ExtractError:
+ e = sys.exc_info()[1]
+ if self.errorlevel > 1:
+ raise
+ else:
+ self._dbg(1, "tarfile: %s" % e)
+
+
+def _build_install_args(options):
+ """
+ Build the arguments to 'python setup.py install' on the distribute package
+ """
+ install_args = []
+ if options.user_install:
+ if sys.version_info < (2, 6):
+ log.warn("--user requires Python 2.6 or later")
+ raise SystemExit(1)
+ install_args.append('--user')
+ return install_args
+
+def _parse_args():
+ """
+ Parse the command line for options
+ """
+ parser = optparse.OptionParser()
+ parser.add_option(
+ '--user', dest='user_install', action='store_true', default=False,
+ help='install in user site package (requires Python 2.6 or later)')
+ parser.add_option(
+ '--download-base', dest='download_base', metavar="URL",
+ default=DEFAULT_URL,
+ help='alternative URL from where to download the distribute package')
+ options, args = parser.parse_args()
+ # positional arguments are ignored
+ return options
+
+def main(version=DEFAULT_VERSION):
+ """Install or upgrade setuptools and EasyInstall"""
+ options = _parse_args()
+ tarball = download_setuptools(download_base=options.download_base)
+ return _install(tarball, _build_install_args(options))
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/psylib/psylib/__init__.py b/psylib/psylib/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/psylib/psylib/mjpeg_streaming_server.py b/psylib/psylib/mjpeg_streaming_server.py
new file mode 100644
index 0000000..e5af4ee
--- /dev/null
+++ b/psylib/psylib/mjpeg_streaming_server.py
@@ -0,0 +1,232 @@
+#!/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 .
+#
+# Copyright (C) 2014 Stefan Kögl
+
+from __future__ import absolute_import
+
+import os
+import os.path
+import re
+
+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):
+ """A simple async http class which provides a mjpeg stream and if found,
+ an index.html file containing the mjpeg stream.
+
+ Parent should implement the interface 'MjpegStreamingConsumerInterface'
+ """
+
+ 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(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+)$")
+ self.html_map = dict()
+
+ def handle_request(self):
+ sock = self.sender()
+ 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)
+ 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.get_regex.match(line).groups()
+ logger.info(
+ "resource=%r, ext=%r, http_version=%r",
+ resource, ext, http_version)
+ except AttributeError:
+ try:
+ host, port = self.host_regex.match(line).groups()
+ logger.info("found host header %r %r", host, port)
+ #return
+ #sock.write("HTTP/1.1 501 Not Implemented\r\n")
+ return
+ except AttributeError:
+ logger.info("no matching request - sending 404 not found")
+ sock.write("HTTP/1.1 404 Not Found\r\n")
+ return
+ else:
+ if ext == "ico":
+ 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 = 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:
+ _, 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\n" \
+ "Content-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 slot_remove_connection(self):
+ try:
+ sock = self.sender()
+ except RuntimeError:
+ return
+ if sock.state() == QTcpSocket.UnconnectedState:
+ self.__remove_connection(sock)
+
+ def __remove_connection(self, sock):
+ sock_id = id(sock)
+ sock.disconnected.disconnect(self.slot_remove_connection)
+ sock.close()
+ sock.deleteLater()
+ try:
+ self.sockets.remove(sock)
+ 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:
+ logger.info("connection %r was not streaming", sock_id)
+
+ # cleaning up streaming connections if that sock is serving index.html
+ try:
+ stream_client = self.html_map.pop(sock_id)
+ except KeyError:
+ logger.info("connection %r has no child connections", sock_id)
+ else:
+ try:
+ stream_client.close()
+ stream_client.deleteLater()
+ except AttributeError, msg:
+ logger.info("no stream client")
+ else:
+ try:
+ self.stream_clients.remove(stream_client)
+ logger.info("child connection %r removed from streaming",
+ id(stream_client))
+ except ValueError:
+ pass
+
+ try:
+ self.sockets.remove(stream_client)
+ logger.info("child connection %r removed from storage",
+ id(stream_client))
+ except ValueError:
+ pass
+
+ def send_image(self):
+ if not self.stream_clients:
+ return
+
+ img_data = self.widget.render_image()
+ len_data = len(img_data)
+ array = QByteArray("--2342\r\nContent-Type: image/jpeg\r\n" \
+ "Content-length: %d\r\n\r\n%s\r\n\r\n\r\n" % (len_data, img_data))
+ for sock in self.stream_clients:
+ sock.write(array)
+
+ def new_connection(self):
+ while self.hasPendingConnections():
+ sock = self.nextPendingConnection()
+ logger.info("new connection=%r", id(sock))
+ sock.readyRead.connect(self.handle_request)
+ sock.disconnected.connect(self.slot_remove_connection)
+ self.sockets.append(sock)
+
+ def stop(self):
+ self.stream_clients = list()
+ for sock in self.sockets:
+ sock.close()
+ sock.deleteLater()
+ self.sockets = list()
+ self.html_map = dict()
+ self.close()
diff --git a/psylib/psylib/other.py b/psylib/psylib/other.py
new file mode 100644
index 0000000..3140be1
--- /dev/null
+++ b/psylib/psylib/other.py
@@ -0,0 +1,164 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# This file is part of chaosc and psychosis
+#
+# chaosc/psychosis 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,
+# 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 .
+#
+# Copyright (C) 2014 Stefan Kögl
+
+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 PyQt4.QtNetwork import QTcpServer
+
+class MjpegStreamingServer(QTcpServer):
+
+ def __init__(self, server_address, parent=None):
+ super(MjpegStreamingServer, self).__init__(parent)
+ self.server_address = server_address
+ self.newConnection.connect(self.new_connection)
+ self.widget = parent
+ 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.get_regex = re.compile("^GET /(\w+?)\.(\w+?) HTTP/(\d+\.\d+)$")
+ self.host_regex = re.compile("^Host: (\w+?):(\d+)$")
+ self.html_map = dict()
+
+ def handle_request(self):
+ 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.get_regex.match(line).groups()
+ logger.info("resource=%r, ext=%r, http_version=%r", resource, ext, http_version)
+ except AttributeError:
+ try:
+ host, port = self.host_regex.match(line).groups()
+ print "found host header", host, port
+ return
+ #sock.write("HTTP/1.1 501 Not Implemented\r\n")
+ except AttributeError:
+ logger.info("no matching request - sending 404 not found")
+ sock.write("HTTP/1.1 404 Not Found\r\n")
+ return
+ 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))
+ 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_connection(self):
+ try:
+ sock = self.sender()
+ except RuntimeError:
+ return
+ sock_id = id(sock)
+ logger.info("remove_connection: sock=%r, sock_id=%r", sock, sock_id)
+ if sock.state() == QTcpSocket.UnconnectedState:
+ sock.disconnected.disconnect(self.remove_connection)
+ 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
+
+ img_data = self.widget.render_image()
+ len_data = len(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, img_data))
+ for sock in self.stream_clients:
+ sock.write(array)
+
+ def new_connection(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_connection)
+ self.sockets.append(sock)
+
+ def stop(self):
+ self.stream_clients = list()
+ self.sockets = list()
+ self.html_map = dict()
+ self.close()
diff --git a/psylib/psylib/psyqt_base.py b/psylib/psylib/psyqt_base.py
new file mode 100644
index 0000000..e429024
--- /dev/null
+++ b/psylib/psylib/psyqt_base.py
@@ -0,0 +1,108 @@
+#!/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 .
+#
+# Copyright (C) 2014 Stefan Kögl
+
+from __future__ import absolute_import
+
+import sys
+import traceback
+
+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, tb):
+ """Handler for the SIGINT signal."""
+ logger.info("sigint_handler")
+ if ex_cls == KeyboardInterrupt:
+ logger.info("found KeyboardInterrupt")
+ self.unsubscribe()
+ QtGui.QApplication.exit()
+ else:
+ logger.critical(''.join(traceback.format_tb(tb)))
+ logger.critical('{0}: {1}'.format(ex_cls, ex))
+
+ def sigterm_handler(self, *args):
+ logger.info("sigterm_handler")
+ 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()
diff --git a/psylib/setup.py b/psylib/setup.py
new file mode 100644
index 0000000..26f4db6
--- /dev/null
+++ b/psylib/setup.py
@@ -0,0 +1,40 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+import sys
+from setuptools import find_packages, setup
+
+if sys.version_info >= (3,):
+ extras['use_2to3'] = True
+
+setup(
+ name='psylib',
+ version="0.2",
+ packages=find_packages(exclude=["scripts",]),
+
+ include_package_data = True,
+
+ exclude_package_data = {'': ['.gitignore']},
+
+ zip_safe = False,
+
+ # pypi metadata
+ author = "Stefan Kögl",
+
+ # FIXME: add author email
+ author_email = "hotte@ctdo.de",
+ description = "library for psychosis",
+
+ # FIXME: add long_description
+ long_description = """
+ """,
+
+ # FIXME: add license
+ license = "GPL",
+
+ # FIXME: add keywords
+ keywords = "",
+
+ # FIXME: add download url
+ url = "",
+)
diff --git a/sensors2osc/distribute_setup.py b/sensors2osc/distribute_setup.py
new file mode 100644
index 0000000..3553b21
--- /dev/null
+++ b/sensors2osc/distribute_setup.py
@@ -0,0 +1,556 @@
+#!python
+"""Bootstrap distribute installation
+
+If you want to use setuptools in your package's setup.py, just include this
+file in the same directory with it, and add this to the top of your setup.py::
+
+ from distribute_setup import use_setuptools
+ use_setuptools()
+
+If you want to require a specific version of setuptools, set a download
+mirror, or use an alternate download directory, you can do so by supplying
+the appropriate options to ``use_setuptools()``.
+
+This file can also be run as a script to install or upgrade setuptools.
+"""
+import os
+import shutil
+import sys
+import time
+import fnmatch
+import tempfile
+import tarfile
+import optparse
+
+from distutils import log
+
+try:
+ from site import USER_SITE
+except ImportError:
+ USER_SITE = None
+
+try:
+ import subprocess
+
+ def _python_cmd(*args):
+ args = (sys.executable,) + args
+ return subprocess.call(args) == 0
+
+except ImportError:
+ # will be used for python 2.3
+ def _python_cmd(*args):
+ args = (sys.executable,) + args
+ # quoting arguments if windows
+ if sys.platform == 'win32':
+ def quote(arg):
+ if ' ' in arg:
+ return '"%s"' % arg
+ return arg
+ args = [quote(arg) for arg in args]
+ return os.spawnl(os.P_WAIT, sys.executable, *args) == 0
+
+DEFAULT_VERSION = "0.6.49"
+DEFAULT_URL = "http://pypi.python.org/packages/source/d/distribute/"
+SETUPTOOLS_FAKED_VERSION = "0.6c11"
+
+SETUPTOOLS_PKG_INFO = """\
+Metadata-Version: 1.0
+Name: setuptools
+Version: %s
+Summary: xxxx
+Home-page: xxx
+Author: xxx
+Author-email: xxx
+License: xxx
+Description: xxx
+""" % SETUPTOOLS_FAKED_VERSION
+
+
+def _install(tarball, install_args=()):
+ # extracting the tarball
+ tmpdir = tempfile.mkdtemp()
+ log.warn('Extracting in %s', tmpdir)
+ old_wd = os.getcwd()
+ try:
+ os.chdir(tmpdir)
+ tar = tarfile.open(tarball)
+ _extractall(tar)
+ tar.close()
+
+ # going in the directory
+ subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0])
+ os.chdir(subdir)
+ log.warn('Now working in %s', subdir)
+
+ # installing
+ log.warn('Installing Distribute')
+ if not _python_cmd('setup.py', 'install', *install_args):
+ log.warn('Something went wrong during the installation.')
+ log.warn('See the error message above.')
+ # exitcode will be 2
+ return 2
+ finally:
+ os.chdir(old_wd)
+ shutil.rmtree(tmpdir)
+
+
+def _build_egg(egg, tarball, to_dir):
+ # extracting the tarball
+ tmpdir = tempfile.mkdtemp()
+ log.warn('Extracting in %s', tmpdir)
+ old_wd = os.getcwd()
+ try:
+ os.chdir(tmpdir)
+ tar = tarfile.open(tarball)
+ _extractall(tar)
+ tar.close()
+
+ # going in the directory
+ subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0])
+ os.chdir(subdir)
+ log.warn('Now working in %s', subdir)
+
+ # building an egg
+ log.warn('Building a Distribute egg in %s', to_dir)
+ _python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir)
+
+ finally:
+ os.chdir(old_wd)
+ shutil.rmtree(tmpdir)
+ # returning the result
+ log.warn(egg)
+ if not os.path.exists(egg):
+ raise IOError('Could not build the egg.')
+
+
+def _do_download(version, download_base, to_dir, download_delay):
+ egg = os.path.join(to_dir, 'distribute-%s-py%d.%d.egg'
+ % (version, sys.version_info[0], sys.version_info[1]))
+ if not os.path.exists(egg):
+ tarball = download_setuptools(version, download_base,
+ to_dir, download_delay)
+ _build_egg(egg, tarball, to_dir)
+ sys.path.insert(0, egg)
+ import setuptools
+ setuptools.bootstrap_install_from = egg
+
+
+def use_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
+ to_dir=os.curdir, download_delay=15, no_fake=True):
+ # making sure we use the absolute path
+ to_dir = os.path.abspath(to_dir)
+ was_imported = 'pkg_resources' in sys.modules or \
+ 'setuptools' in sys.modules
+ try:
+ try:
+ import pkg_resources
+
+ # Setuptools 0.7b and later is a suitable (and preferable)
+ # substitute for any Distribute version.
+ try:
+ pkg_resources.require("setuptools>=0.7b")
+ return
+ except (pkg_resources.DistributionNotFound,
+ pkg_resources.VersionConflict):
+ pass
+
+ if not hasattr(pkg_resources, '_distribute'):
+ if not no_fake:
+ _fake_setuptools()
+ raise ImportError
+ except ImportError:
+ return _do_download(version, download_base, to_dir, download_delay)
+ try:
+ pkg_resources.require("distribute>=" + version)
+ return
+ except pkg_resources.VersionConflict:
+ e = sys.exc_info()[1]
+ if was_imported:
+ sys.stderr.write(
+ "The required version of distribute (>=%s) is not available,\n"
+ "and can't be installed while this script is running. Please\n"
+ "install a more recent version first, using\n"
+ "'easy_install -U distribute'."
+ "\n\n(Currently using %r)\n" % (version, e.args[0]))
+ sys.exit(2)
+ else:
+ del pkg_resources, sys.modules['pkg_resources'] # reload ok
+ return _do_download(version, download_base, to_dir,
+ download_delay)
+ except pkg_resources.DistributionNotFound:
+ return _do_download(version, download_base, to_dir,
+ download_delay)
+ finally:
+ if not no_fake:
+ _create_fake_setuptools_pkg_info(to_dir)
+
+
+def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
+ to_dir=os.curdir, delay=15):
+ """Download distribute from a specified location and return its filename
+
+ `version` should be a valid distribute version number that is available
+ as an egg for download under the `download_base` URL (which should end
+ with a '/'). `to_dir` is the directory where the egg will be downloaded.
+ `delay` is the number of seconds to pause before an actual download
+ attempt.
+ """
+ # making sure we use the absolute path
+ to_dir = os.path.abspath(to_dir)
+ try:
+ from urllib.request import urlopen
+ except ImportError:
+ from urllib2 import urlopen
+ tgz_name = "distribute-%s.tar.gz" % version
+ url = download_base + tgz_name
+ saveto = os.path.join(to_dir, tgz_name)
+ src = dst = None
+ if not os.path.exists(saveto): # Avoid repeated downloads
+ try:
+ log.warn("Downloading %s", url)
+ src = urlopen(url)
+ # Read/write all in one block, so we don't create a corrupt file
+ # if the download is interrupted.
+ data = src.read()
+ dst = open(saveto, "wb")
+ dst.write(data)
+ finally:
+ if src:
+ src.close()
+ if dst:
+ dst.close()
+ return os.path.realpath(saveto)
+
+
+def _no_sandbox(function):
+ def __no_sandbox(*args, **kw):
+ try:
+ from setuptools.sandbox import DirectorySandbox
+ if not hasattr(DirectorySandbox, '_old'):
+ def violation(*args):
+ pass
+ DirectorySandbox._old = DirectorySandbox._violation
+ DirectorySandbox._violation = violation
+ patched = True
+ else:
+ patched = False
+ except ImportError:
+ patched = False
+
+ try:
+ return function(*args, **kw)
+ finally:
+ if patched:
+ DirectorySandbox._violation = DirectorySandbox._old
+ del DirectorySandbox._old
+
+ return __no_sandbox
+
+
+def _patch_file(path, content):
+ """Will backup the file then patch it"""
+ f = open(path)
+ existing_content = f.read()
+ f.close()
+ if existing_content == content:
+ # already patched
+ log.warn('Already patched.')
+ return False
+ log.warn('Patching...')
+ _rename_path(path)
+ f = open(path, 'w')
+ try:
+ f.write(content)
+ finally:
+ f.close()
+ return True
+
+_patch_file = _no_sandbox(_patch_file)
+
+
+def _same_content(path, content):
+ f = open(path)
+ existing_content = f.read()
+ f.close()
+ return existing_content == content
+
+
+def _rename_path(path):
+ new_name = path + '.OLD.%s' % time.time()
+ log.warn('Renaming %s to %s', path, new_name)
+ os.rename(path, new_name)
+ return new_name
+
+
+def _remove_flat_installation(placeholder):
+ if not os.path.isdir(placeholder):
+ log.warn('Unkown installation at %s', placeholder)
+ return False
+ found = False
+ for file in os.listdir(placeholder):
+ if fnmatch.fnmatch(file, 'setuptools*.egg-info'):
+ found = True
+ break
+ if not found:
+ log.warn('Could not locate setuptools*.egg-info')
+ return
+
+ log.warn('Moving elements out of the way...')
+ pkg_info = os.path.join(placeholder, file)
+ if os.path.isdir(pkg_info):
+ patched = _patch_egg_dir(pkg_info)
+ else:
+ patched = _patch_file(pkg_info, SETUPTOOLS_PKG_INFO)
+
+ if not patched:
+ log.warn('%s already patched.', pkg_info)
+ return False
+ # now let's move the files out of the way
+ for element in ('setuptools', 'pkg_resources.py', 'site.py'):
+ element = os.path.join(placeholder, element)
+ if os.path.exists(element):
+ _rename_path(element)
+ else:
+ log.warn('Could not find the %s element of the '
+ 'Setuptools distribution', element)
+ return True
+
+_remove_flat_installation = _no_sandbox(_remove_flat_installation)
+
+
+def _after_install(dist):
+ log.warn('After install bootstrap.')
+ placeholder = dist.get_command_obj('install').install_purelib
+ _create_fake_setuptools_pkg_info(placeholder)
+
+
+def _create_fake_setuptools_pkg_info(placeholder):
+ if not placeholder or not os.path.exists(placeholder):
+ log.warn('Could not find the install location')
+ return
+ pyver = '%s.%s' % (sys.version_info[0], sys.version_info[1])
+ setuptools_file = 'setuptools-%s-py%s.egg-info' % \
+ (SETUPTOOLS_FAKED_VERSION, pyver)
+ pkg_info = os.path.join(placeholder, setuptools_file)
+ if os.path.exists(pkg_info):
+ log.warn('%s already exists', pkg_info)
+ return
+
+ log.warn('Creating %s', pkg_info)
+ try:
+ f = open(pkg_info, 'w')
+ except EnvironmentError:
+ log.warn("Don't have permissions to write %s, skipping", pkg_info)
+ return
+ try:
+ f.write(SETUPTOOLS_PKG_INFO)
+ finally:
+ f.close()
+
+ pth_file = os.path.join(placeholder, 'setuptools.pth')
+ log.warn('Creating %s', pth_file)
+ f = open(pth_file, 'w')
+ try:
+ f.write(os.path.join(os.curdir, setuptools_file))
+ finally:
+ f.close()
+
+_create_fake_setuptools_pkg_info = _no_sandbox(
+ _create_fake_setuptools_pkg_info
+)
+
+
+def _patch_egg_dir(path):
+ # let's check if it's already patched
+ pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO')
+ if os.path.exists(pkg_info):
+ if _same_content(pkg_info, SETUPTOOLS_PKG_INFO):
+ log.warn('%s already patched.', pkg_info)
+ return False
+ _rename_path(path)
+ os.mkdir(path)
+ os.mkdir(os.path.join(path, 'EGG-INFO'))
+ pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO')
+ f = open(pkg_info, 'w')
+ try:
+ f.write(SETUPTOOLS_PKG_INFO)
+ finally:
+ f.close()
+ return True
+
+_patch_egg_dir = _no_sandbox(_patch_egg_dir)
+
+
+def _before_install():
+ log.warn('Before install bootstrap.')
+ _fake_setuptools()
+
+
+def _under_prefix(location):
+ if 'install' not in sys.argv:
+ return True
+ args = sys.argv[sys.argv.index('install') + 1:]
+ for index, arg in enumerate(args):
+ for option in ('--root', '--prefix'):
+ if arg.startswith('%s=' % option):
+ top_dir = arg.split('root=')[-1]
+ return location.startswith(top_dir)
+ elif arg == option:
+ if len(args) > index:
+ top_dir = args[index + 1]
+ return location.startswith(top_dir)
+ if arg == '--user' and USER_SITE is not None:
+ return location.startswith(USER_SITE)
+ return True
+
+
+def _fake_setuptools():
+ log.warn('Scanning installed packages')
+ try:
+ import pkg_resources
+ except ImportError:
+ # we're cool
+ log.warn('Setuptools or Distribute does not seem to be installed.')
+ return
+ ws = pkg_resources.working_set
+ try:
+ setuptools_dist = ws.find(
+ pkg_resources.Requirement.parse('setuptools', replacement=False)
+ )
+ except TypeError:
+ # old distribute API
+ setuptools_dist = ws.find(
+ pkg_resources.Requirement.parse('setuptools')
+ )
+
+ if setuptools_dist is None:
+ log.warn('No setuptools distribution found')
+ return
+ # detecting if it was already faked
+ setuptools_location = setuptools_dist.location
+ log.warn('Setuptools installation detected at %s', setuptools_location)
+
+ # if --root or --preix was provided, and if
+ # setuptools is not located in them, we don't patch it
+ if not _under_prefix(setuptools_location):
+ log.warn('Not patching, --root or --prefix is installing Distribute'
+ ' in another location')
+ return
+
+ # let's see if its an egg
+ if not setuptools_location.endswith('.egg'):
+ log.warn('Non-egg installation')
+ res = _remove_flat_installation(setuptools_location)
+ if not res:
+ return
+ else:
+ log.warn('Egg installation')
+ pkg_info = os.path.join(setuptools_location, 'EGG-INFO', 'PKG-INFO')
+ if (os.path.exists(pkg_info) and
+ _same_content(pkg_info, SETUPTOOLS_PKG_INFO)):
+ log.warn('Already patched.')
+ return
+ log.warn('Patching...')
+ # let's create a fake egg replacing setuptools one
+ res = _patch_egg_dir(setuptools_location)
+ if not res:
+ return
+ log.warn('Patching complete.')
+ _relaunch()
+
+
+def _relaunch():
+ log.warn('Relaunching...')
+ # we have to relaunch the process
+ # pip marker to avoid a relaunch bug
+ _cmd1 = ['-c', 'install', '--single-version-externally-managed']
+ _cmd2 = ['-c', 'install', '--record']
+ if sys.argv[:3] == _cmd1 or sys.argv[:3] == _cmd2:
+ sys.argv[0] = 'setup.py'
+ args = [sys.executable] + sys.argv
+ sys.exit(subprocess.call(args))
+
+
+def _extractall(self, path=".", members=None):
+ """Extract all members from the archive to the current working
+ directory and set owner, modification time and permissions on
+ directories afterwards. `path' specifies a different directory
+ to extract to. `members' is optional and must be a subset of the
+ list returned by getmembers().
+ """
+ import copy
+ import operator
+ from tarfile import ExtractError
+ directories = []
+
+ if members is None:
+ members = self
+
+ for tarinfo in members:
+ if tarinfo.isdir():
+ # Extract directories with a safe mode.
+ directories.append(tarinfo)
+ tarinfo = copy.copy(tarinfo)
+ tarinfo.mode = 448 # decimal for oct 0700
+ self.extract(tarinfo, path)
+
+ # Reverse sort directories.
+ if sys.version_info < (2, 4):
+ def sorter(dir1, dir2):
+ return cmp(dir1.name, dir2.name)
+ directories.sort(sorter)
+ directories.reverse()
+ else:
+ directories.sort(key=operator.attrgetter('name'), reverse=True)
+
+ # Set correct owner, mtime and filemode on directories.
+ for tarinfo in directories:
+ dirpath = os.path.join(path, tarinfo.name)
+ try:
+ self.chown(tarinfo, dirpath)
+ self.utime(tarinfo, dirpath)
+ self.chmod(tarinfo, dirpath)
+ except ExtractError:
+ e = sys.exc_info()[1]
+ if self.errorlevel > 1:
+ raise
+ else:
+ self._dbg(1, "tarfile: %s" % e)
+
+
+def _build_install_args(options):
+ """
+ Build the arguments to 'python setup.py install' on the distribute package
+ """
+ install_args = []
+ if options.user_install:
+ if sys.version_info < (2, 6):
+ log.warn("--user requires Python 2.6 or later")
+ raise SystemExit(1)
+ install_args.append('--user')
+ return install_args
+
+def _parse_args():
+ """
+ Parse the command line for options
+ """
+ parser = optparse.OptionParser()
+ parser.add_option(
+ '--user', dest='user_install', action='store_true', default=False,
+ help='install in user site package (requires Python 2.6 or later)')
+ parser.add_option(
+ '--download-base', dest='download_base', metavar="URL",
+ default=DEFAULT_URL,
+ help='alternative URL from where to download the distribute package')
+ options, args = parser.parse_args()
+ # positional arguments are ignored
+ return options
+
+def main(version=DEFAULT_VERSION):
+ """Install or upgrade setuptools and EasyInstall"""
+ options = _parse_args()
+ tarball = download_setuptools(download_base=options.download_base)
+ return _install(tarball, _build_install_args(options))
+
+if __name__ == '__main__':
+ sys.exit(main())
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/distribute_setup.py b/texter/distribute_setup.py
new file mode 100644
index 0000000..3553b21
--- /dev/null
+++ b/texter/distribute_setup.py
@@ -0,0 +1,556 @@
+#!python
+"""Bootstrap distribute installation
+
+If you want to use setuptools in your package's setup.py, just include this
+file in the same directory with it, and add this to the top of your setup.py::
+
+ from distribute_setup import use_setuptools
+ use_setuptools()
+
+If you want to require a specific version of setuptools, set a download
+mirror, or use an alternate download directory, you can do so by supplying
+the appropriate options to ``use_setuptools()``.
+
+This file can also be run as a script to install or upgrade setuptools.
+"""
+import os
+import shutil
+import sys
+import time
+import fnmatch
+import tempfile
+import tarfile
+import optparse
+
+from distutils import log
+
+try:
+ from site import USER_SITE
+except ImportError:
+ USER_SITE = None
+
+try:
+ import subprocess
+
+ def _python_cmd(*args):
+ args = (sys.executable,) + args
+ return subprocess.call(args) == 0
+
+except ImportError:
+ # will be used for python 2.3
+ def _python_cmd(*args):
+ args = (sys.executable,) + args
+ # quoting arguments if windows
+ if sys.platform == 'win32':
+ def quote(arg):
+ if ' ' in arg:
+ return '"%s"' % arg
+ return arg
+ args = [quote(arg) for arg in args]
+ return os.spawnl(os.P_WAIT, sys.executable, *args) == 0
+
+DEFAULT_VERSION = "0.6.49"
+DEFAULT_URL = "http://pypi.python.org/packages/source/d/distribute/"
+SETUPTOOLS_FAKED_VERSION = "0.6c11"
+
+SETUPTOOLS_PKG_INFO = """\
+Metadata-Version: 1.0
+Name: setuptools
+Version: %s
+Summary: xxxx
+Home-page: xxx
+Author: xxx
+Author-email: xxx
+License: xxx
+Description: xxx
+""" % SETUPTOOLS_FAKED_VERSION
+
+
+def _install(tarball, install_args=()):
+ # extracting the tarball
+ tmpdir = tempfile.mkdtemp()
+ log.warn('Extracting in %s', tmpdir)
+ old_wd = os.getcwd()
+ try:
+ os.chdir(tmpdir)
+ tar = tarfile.open(tarball)
+ _extractall(tar)
+ tar.close()
+
+ # going in the directory
+ subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0])
+ os.chdir(subdir)
+ log.warn('Now working in %s', subdir)
+
+ # installing
+ log.warn('Installing Distribute')
+ if not _python_cmd('setup.py', 'install', *install_args):
+ log.warn('Something went wrong during the installation.')
+ log.warn('See the error message above.')
+ # exitcode will be 2
+ return 2
+ finally:
+ os.chdir(old_wd)
+ shutil.rmtree(tmpdir)
+
+
+def _build_egg(egg, tarball, to_dir):
+ # extracting the tarball
+ tmpdir = tempfile.mkdtemp()
+ log.warn('Extracting in %s', tmpdir)
+ old_wd = os.getcwd()
+ try:
+ os.chdir(tmpdir)
+ tar = tarfile.open(tarball)
+ _extractall(tar)
+ tar.close()
+
+ # going in the directory
+ subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0])
+ os.chdir(subdir)
+ log.warn('Now working in %s', subdir)
+
+ # building an egg
+ log.warn('Building a Distribute egg in %s', to_dir)
+ _python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir)
+
+ finally:
+ os.chdir(old_wd)
+ shutil.rmtree(tmpdir)
+ # returning the result
+ log.warn(egg)
+ if not os.path.exists(egg):
+ raise IOError('Could not build the egg.')
+
+
+def _do_download(version, download_base, to_dir, download_delay):
+ egg = os.path.join(to_dir, 'distribute-%s-py%d.%d.egg'
+ % (version, sys.version_info[0], sys.version_info[1]))
+ if not os.path.exists(egg):
+ tarball = download_setuptools(version, download_base,
+ to_dir, download_delay)
+ _build_egg(egg, tarball, to_dir)
+ sys.path.insert(0, egg)
+ import setuptools
+ setuptools.bootstrap_install_from = egg
+
+
+def use_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
+ to_dir=os.curdir, download_delay=15, no_fake=True):
+ # making sure we use the absolute path
+ to_dir = os.path.abspath(to_dir)
+ was_imported = 'pkg_resources' in sys.modules or \
+ 'setuptools' in sys.modules
+ try:
+ try:
+ import pkg_resources
+
+ # Setuptools 0.7b and later is a suitable (and preferable)
+ # substitute for any Distribute version.
+ try:
+ pkg_resources.require("setuptools>=0.7b")
+ return
+ except (pkg_resources.DistributionNotFound,
+ pkg_resources.VersionConflict):
+ pass
+
+ if not hasattr(pkg_resources, '_distribute'):
+ if not no_fake:
+ _fake_setuptools()
+ raise ImportError
+ except ImportError:
+ return _do_download(version, download_base, to_dir, download_delay)
+ try:
+ pkg_resources.require("distribute>=" + version)
+ return
+ except pkg_resources.VersionConflict:
+ e = sys.exc_info()[1]
+ if was_imported:
+ sys.stderr.write(
+ "The required version of distribute (>=%s) is not available,\n"
+ "and can't be installed while this script is running. Please\n"
+ "install a more recent version first, using\n"
+ "'easy_install -U distribute'."
+ "\n\n(Currently using %r)\n" % (version, e.args[0]))
+ sys.exit(2)
+ else:
+ del pkg_resources, sys.modules['pkg_resources'] # reload ok
+ return _do_download(version, download_base, to_dir,
+ download_delay)
+ except pkg_resources.DistributionNotFound:
+ return _do_download(version, download_base, to_dir,
+ download_delay)
+ finally:
+ if not no_fake:
+ _create_fake_setuptools_pkg_info(to_dir)
+
+
+def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
+ to_dir=os.curdir, delay=15):
+ """Download distribute from a specified location and return its filename
+
+ `version` should be a valid distribute version number that is available
+ as an egg for download under the `download_base` URL (which should end
+ with a '/'). `to_dir` is the directory where the egg will be downloaded.
+ `delay` is the number of seconds to pause before an actual download
+ attempt.
+ """
+ # making sure we use the absolute path
+ to_dir = os.path.abspath(to_dir)
+ try:
+ from urllib.request import urlopen
+ except ImportError:
+ from urllib2 import urlopen
+ tgz_name = "distribute-%s.tar.gz" % version
+ url = download_base + tgz_name
+ saveto = os.path.join(to_dir, tgz_name)
+ src = dst = None
+ if not os.path.exists(saveto): # Avoid repeated downloads
+ try:
+ log.warn("Downloading %s", url)
+ src = urlopen(url)
+ # Read/write all in one block, so we don't create a corrupt file
+ # if the download is interrupted.
+ data = src.read()
+ dst = open(saveto, "wb")
+ dst.write(data)
+ finally:
+ if src:
+ src.close()
+ if dst:
+ dst.close()
+ return os.path.realpath(saveto)
+
+
+def _no_sandbox(function):
+ def __no_sandbox(*args, **kw):
+ try:
+ from setuptools.sandbox import DirectorySandbox
+ if not hasattr(DirectorySandbox, '_old'):
+ def violation(*args):
+ pass
+ DirectorySandbox._old = DirectorySandbox._violation
+ DirectorySandbox._violation = violation
+ patched = True
+ else:
+ patched = False
+ except ImportError:
+ patched = False
+
+ try:
+ return function(*args, **kw)
+ finally:
+ if patched:
+ DirectorySandbox._violation = DirectorySandbox._old
+ del DirectorySandbox._old
+
+ return __no_sandbox
+
+
+def _patch_file(path, content):
+ """Will backup the file then patch it"""
+ f = open(path)
+ existing_content = f.read()
+ f.close()
+ if existing_content == content:
+ # already patched
+ log.warn('Already patched.')
+ return False
+ log.warn('Patching...')
+ _rename_path(path)
+ f = open(path, 'w')
+ try:
+ f.write(content)
+ finally:
+ f.close()
+ return True
+
+_patch_file = _no_sandbox(_patch_file)
+
+
+def _same_content(path, content):
+ f = open(path)
+ existing_content = f.read()
+ f.close()
+ return existing_content == content
+
+
+def _rename_path(path):
+ new_name = path + '.OLD.%s' % time.time()
+ log.warn('Renaming %s to %s', path, new_name)
+ os.rename(path, new_name)
+ return new_name
+
+
+def _remove_flat_installation(placeholder):
+ if not os.path.isdir(placeholder):
+ log.warn('Unkown installation at %s', placeholder)
+ return False
+ found = False
+ for file in os.listdir(placeholder):
+ if fnmatch.fnmatch(file, 'setuptools*.egg-info'):
+ found = True
+ break
+ if not found:
+ log.warn('Could not locate setuptools*.egg-info')
+ return
+
+ log.warn('Moving elements out of the way...')
+ pkg_info = os.path.join(placeholder, file)
+ if os.path.isdir(pkg_info):
+ patched = _patch_egg_dir(pkg_info)
+ else:
+ patched = _patch_file(pkg_info, SETUPTOOLS_PKG_INFO)
+
+ if not patched:
+ log.warn('%s already patched.', pkg_info)
+ return False
+ # now let's move the files out of the way
+ for element in ('setuptools', 'pkg_resources.py', 'site.py'):
+ element = os.path.join(placeholder, element)
+ if os.path.exists(element):
+ _rename_path(element)
+ else:
+ log.warn('Could not find the %s element of the '
+ 'Setuptools distribution', element)
+ return True
+
+_remove_flat_installation = _no_sandbox(_remove_flat_installation)
+
+
+def _after_install(dist):
+ log.warn('After install bootstrap.')
+ placeholder = dist.get_command_obj('install').install_purelib
+ _create_fake_setuptools_pkg_info(placeholder)
+
+
+def _create_fake_setuptools_pkg_info(placeholder):
+ if not placeholder or not os.path.exists(placeholder):
+ log.warn('Could not find the install location')
+ return
+ pyver = '%s.%s' % (sys.version_info[0], sys.version_info[1])
+ setuptools_file = 'setuptools-%s-py%s.egg-info' % \
+ (SETUPTOOLS_FAKED_VERSION, pyver)
+ pkg_info = os.path.join(placeholder, setuptools_file)
+ if os.path.exists(pkg_info):
+ log.warn('%s already exists', pkg_info)
+ return
+
+ log.warn('Creating %s', pkg_info)
+ try:
+ f = open(pkg_info, 'w')
+ except EnvironmentError:
+ log.warn("Don't have permissions to write %s, skipping", pkg_info)
+ return
+ try:
+ f.write(SETUPTOOLS_PKG_INFO)
+ finally:
+ f.close()
+
+ pth_file = os.path.join(placeholder, 'setuptools.pth')
+ log.warn('Creating %s', pth_file)
+ f = open(pth_file, 'w')
+ try:
+ f.write(os.path.join(os.curdir, setuptools_file))
+ finally:
+ f.close()
+
+_create_fake_setuptools_pkg_info = _no_sandbox(
+ _create_fake_setuptools_pkg_info
+)
+
+
+def _patch_egg_dir(path):
+ # let's check if it's already patched
+ pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO')
+ if os.path.exists(pkg_info):
+ if _same_content(pkg_info, SETUPTOOLS_PKG_INFO):
+ log.warn('%s already patched.', pkg_info)
+ return False
+ _rename_path(path)
+ os.mkdir(path)
+ os.mkdir(os.path.join(path, 'EGG-INFO'))
+ pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO')
+ f = open(pkg_info, 'w')
+ try:
+ f.write(SETUPTOOLS_PKG_INFO)
+ finally:
+ f.close()
+ return True
+
+_patch_egg_dir = _no_sandbox(_patch_egg_dir)
+
+
+def _before_install():
+ log.warn('Before install bootstrap.')
+ _fake_setuptools()
+
+
+def _under_prefix(location):
+ if 'install' not in sys.argv:
+ return True
+ args = sys.argv[sys.argv.index('install') + 1:]
+ for index, arg in enumerate(args):
+ for option in ('--root', '--prefix'):
+ if arg.startswith('%s=' % option):
+ top_dir = arg.split('root=')[-1]
+ return location.startswith(top_dir)
+ elif arg == option:
+ if len(args) > index:
+ top_dir = args[index + 1]
+ return location.startswith(top_dir)
+ if arg == '--user' and USER_SITE is not None:
+ return location.startswith(USER_SITE)
+ return True
+
+
+def _fake_setuptools():
+ log.warn('Scanning installed packages')
+ try:
+ import pkg_resources
+ except ImportError:
+ # we're cool
+ log.warn('Setuptools or Distribute does not seem to be installed.')
+ return
+ ws = pkg_resources.working_set
+ try:
+ setuptools_dist = ws.find(
+ pkg_resources.Requirement.parse('setuptools', replacement=False)
+ )
+ except TypeError:
+ # old distribute API
+ setuptools_dist = ws.find(
+ pkg_resources.Requirement.parse('setuptools')
+ )
+
+ if setuptools_dist is None:
+ log.warn('No setuptools distribution found')
+ return
+ # detecting if it was already faked
+ setuptools_location = setuptools_dist.location
+ log.warn('Setuptools installation detected at %s', setuptools_location)
+
+ # if --root or --preix was provided, and if
+ # setuptools is not located in them, we don't patch it
+ if not _under_prefix(setuptools_location):
+ log.warn('Not patching, --root or --prefix is installing Distribute'
+ ' in another location')
+ return
+
+ # let's see if its an egg
+ if not setuptools_location.endswith('.egg'):
+ log.warn('Non-egg installation')
+ res = _remove_flat_installation(setuptools_location)
+ if not res:
+ return
+ else:
+ log.warn('Egg installation')
+ pkg_info = os.path.join(setuptools_location, 'EGG-INFO', 'PKG-INFO')
+ if (os.path.exists(pkg_info) and
+ _same_content(pkg_info, SETUPTOOLS_PKG_INFO)):
+ log.warn('Already patched.')
+ return
+ log.warn('Patching...')
+ # let's create a fake egg replacing setuptools one
+ res = _patch_egg_dir(setuptools_location)
+ if not res:
+ return
+ log.warn('Patching complete.')
+ _relaunch()
+
+
+def _relaunch():
+ log.warn('Relaunching...')
+ # we have to relaunch the process
+ # pip marker to avoid a relaunch bug
+ _cmd1 = ['-c', 'install', '--single-version-externally-managed']
+ _cmd2 = ['-c', 'install', '--record']
+ if sys.argv[:3] == _cmd1 or sys.argv[:3] == _cmd2:
+ sys.argv[0] = 'setup.py'
+ args = [sys.executable] + sys.argv
+ sys.exit(subprocess.call(args))
+
+
+def _extractall(self, path=".", members=None):
+ """Extract all members from the archive to the current working
+ directory and set owner, modification time and permissions on
+ directories afterwards. `path' specifies a different directory
+ to extract to. `members' is optional and must be a subset of the
+ list returned by getmembers().
+ """
+ import copy
+ import operator
+ from tarfile import ExtractError
+ directories = []
+
+ if members is None:
+ members = self
+
+ for tarinfo in members:
+ if tarinfo.isdir():
+ # Extract directories with a safe mode.
+ directories.append(tarinfo)
+ tarinfo = copy.copy(tarinfo)
+ tarinfo.mode = 448 # decimal for oct 0700
+ self.extract(tarinfo, path)
+
+ # Reverse sort directories.
+ if sys.version_info < (2, 4):
+ def sorter(dir1, dir2):
+ return cmp(dir1.name, dir2.name)
+ directories.sort(sorter)
+ directories.reverse()
+ else:
+ directories.sort(key=operator.attrgetter('name'), reverse=True)
+
+ # Set correct owner, mtime and filemode on directories.
+ for tarinfo in directories:
+ dirpath = os.path.join(path, tarinfo.name)
+ try:
+ self.chown(tarinfo, dirpath)
+ self.utime(tarinfo, dirpath)
+ self.chmod(tarinfo, dirpath)
+ except ExtractError:
+ e = sys.exc_info()[1]
+ if self.errorlevel > 1:
+ raise
+ else:
+ self._dbg(1, "tarfile: %s" % e)
+
+
+def _build_install_args(options):
+ """
+ Build the arguments to 'python setup.py install' on the distribute package
+ """
+ install_args = []
+ if options.user_install:
+ if sys.version_info < (2, 6):
+ log.warn("--user requires Python 2.6 or later")
+ raise SystemExit(1)
+ install_args.append('--user')
+ return install_args
+
+def _parse_args():
+ """
+ Parse the command line for options
+ """
+ parser = optparse.OptionParser()
+ parser.add_option(
+ '--user', dest='user_install', action='store_true', default=False,
+ help='install in user site package (requires Python 2.6 or later)')
+ parser.add_option(
+ '--download-base', dest='download_base', metavar="URL",
+ default=DEFAULT_URL,
+ help='alternative URL from where to download the distribute package')
+ options, args = parser.parse_args()
+ # positional arguments are ignored
+ return options
+
+def main(version=DEFAULT_VERSION):
+ """Install or upgrade setuptools and EasyInstall"""
+ options = _parse_args()
+ tarball = download_setuptools(download_base=options.download_base)
+ return _install(tarball, _build_install_args(options))
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/texter/setup.py b/texter/setup.py
index 72c3973..48211f6 100644
--- a/texter/setup.py
+++ b/texter/setup.py
@@ -1,9 +1,6 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
-from distribute_setup import use_setuptools
-use_setuptools()
-
import sys
from setuptools import find_packages, setup
@@ -12,13 +9,15 @@ if sys.version_info >= (3,):
setup(
name='texter',
- version="0.1",
+ version="0.2",
packages=find_packages(exclude=["scripts",]),
include_package_data = True,
+ install_requires = ["psylib"],
+
package_data = {
- "texter" : ["*.ui", "*.qrc", "*.png"]},
+ "texter" : ["*.ui", "*.qrc", "*.png", "*.ico", "*.html"]},
exclude_package_data = {'': ['.gitignore']},
@@ -47,7 +46,7 @@ setup(
""",
# FIXME: add license
- license = "LGPL",
+ license = "GPL",
# FIXME: add keywords
keywords = "",
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/build.sh b/texter/texter/build.sh
index 94ec27b..9b8cbaa 100644
--- a/texter/texter/build.sh
+++ b/texter/texter/build.sh
@@ -1,2 +1,2 @@
-pyuic4 -o texter_ui.py texter3.ui
-pyuic4 -o text_sorter_ui.py texter4.ui
+# pyuic4 -o texter_ui.py texter.ui
+pyuic4 -o edit_dialog_ui.py edit_dialog.ui
diff --git a/texter/texter/edit_dialog.ui b/texter/texter/edit_dialog.ui
new file mode 100644
index 0000000..4c5c3a9
--- /dev/null
+++ b/texter/texter/edit_dialog.ui
@@ -0,0 +1,507 @@
+
+
+ EditDialog
+
+
+
+ 0
+ 0
+ 1084
+ 633
+
+
+
+ Form
+
+
+ -
+
+
-
+
+
+ -
+
+
+
+ 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
+
+
+
+
+
+ -
+
+
+
-
+
+
+ 2
+
+
+
+ -
+
+
+ -
+
+
+ Remove
+
+
+
+ ../../../../../../../../
+
+
+
+
+
+
+
+
+
+
+ KArrowButton
+ QPushButton
+
+
+
+ KButtonGroup
+ QGroupBox
+
+ 1
+
+
+ KPushButton
+ QPushButton
+
+
+
+
+
+
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..6760f15 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: Tue May 27 18:18:57 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.QListView(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/images/document-new.png b/texter/texter/images/document-new.png
new file mode 100644
index 0000000..8431237
Binary files /dev/null and b/texter/texter/images/document-new.png differ
diff --git a/texter/texter/images/document-open-data.png b/texter/texter/images/document-open-data.png
new file mode 100644
index 0000000..69685ba
Binary files /dev/null and b/texter/texter/images/document-open-data.png differ
diff --git a/texter/texter/images/document-open-recent.png b/texter/texter/images/document-open-recent.png
new file mode 100644
index 0000000..681c8da
Binary files /dev/null and b/texter/texter/images/document-open-recent.png differ
diff --git a/texter/texter/images/document-save.png b/texter/texter/images/document-save.png
new file mode 100644
index 0000000..8072aea
Binary files /dev/null and b/texter/texter/images/document-save.png differ
diff --git a/texter/texter/images/edit-clear.png b/texter/texter/images/edit-clear.png
new file mode 100644
index 0000000..19a1665
Binary files /dev/null and b/texter/texter/images/edit-clear.png differ
diff --git a/texter/texter/images/edit-copy.png b/texter/texter/images/edit-copy.png
new file mode 100644
index 0000000..5cdeb5f
Binary files /dev/null and b/texter/texter/images/edit-copy.png differ
diff --git a/texter/texter/images/go-next-view-page.png b/texter/texter/images/go-next-view-page.png
new file mode 100644
index 0000000..f794c3d
Binary files /dev/null and b/texter/texter/images/go-next-view-page.png differ
diff --git a/texter/texter/images/go-previous-view-page.png b/texter/texter/images/go-previous-view-page.png
new file mode 100644
index 0000000..c78a1b5
Binary files /dev/null and b/texter/texter/images/go-previous-view-page.png differ
diff --git a/texter/texter/images/media-playback-stop.png b/texter/texter/images/media-playback-stop.png
new file mode 100644
index 0000000..180280e
Binary files /dev/null and b/texter/texter/images/media-playback-stop.png differ
diff --git a/texter/texter/images/media-record.png b/texter/texter/images/media-record.png
new file mode 100644
index 0000000..1dc4efd
Binary files /dev/null and b/texter/texter/images/media-record.png differ
diff --git a/texter/texter/images/view-refresh.png b/texter/texter/images/view-refresh.png
new file mode 100644
index 0000000..86b6f82
Binary files /dev/null and b/texter/texter/images/view-refresh.png differ
diff --git a/texter/texter/index.html b/texter/texter/index.html
new file mode 100644
index 0000000..df3af71
--- /dev/null
+++ b/texter/texter/index.html
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/texter/texter/main.py b/texter/texter/main.py
index a17c363..c084596 100644
--- a/texter/texter/main.py
+++ b/texter/texter/main.py
@@ -1,38 +1,54 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
+# This file is part of texter package
+#
+# texter 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.
+#
+# texter 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 texter. If not, see .
+#
+# Copyright (C) 2014 Stefan Kögl
+
+from __future__ import absolute_import
+
+
import cPickle
import os.path
import re
-import subprocess
import sys
-from math import pow
-
-from operator import itemgetter
+import traceback
from PyQt4 import QtCore, QtGui
+from PyQt4.QtCore import QBuffer, QByteArray, QIODevice
+from PyQt4.QtGui import QPixmap
-from PyKDE4.kdecore import ki18n, KCmdLineArgs, KAboutData
-from PyKDE4.kdeui import KDialog, KActionCollection, KRichTextWidget, KComboBox, KPushButton, KRichTextWidget, KMainWindow, KToolBar, KApplication, KAction, KToolBarSpacerAction, KSelectAction, KToggleAction, KShortcut
+from PyKDE4.kdeui import (KDialog, KActionCollection, KRichTextWidget,
+ KRichTextWidget, KMainWindow, KToolBar, KAction, KToolBarSpacerAction,
+ KSelectAction, KToggleAction, KShortcut)
-from texter_ui import Ui_MainWindow, _fromUtf8
-from text_sorter_ui import Ui_TextSorterDialog
-from text_model import TextModel
+from PyQt4.QtNetwork import QTcpServer, QTcpSocket
-appName = "texter"
-catalog = "448texter"
-programName = ki18n("4.48 Psychose Texter")
-version = "0.1"
+from chaosc.argparser_groups import ArgParser
+from chaosc.lib import resolve_host, logger
-aboutData = KAboutData(appName, catalog, programName, version)
+from psylib.mjpeg_streaming_server import *
+from psylib.psyqt_base import PsyQtClientBase
-KCmdLineArgs.init (sys.argv, aboutData)
+from texter.texter_ui import Ui_MainWindow, _fromUtf8
+from texter.edit_dialog_ui import Ui_EditDialog
+from texter.text_model import TextModel
-app = KApplication()
-
-for path in QtGui.QIcon.themeSearchPaths():
- print "%s/%s" % (path, QtGui.QIcon.themeName())
+qtapp = QtGui.QApplication([])
# NOTE: if the QIcon.fromTheme method does not find any icons, you can use
@@ -40,15 +56,20 @@ for path in QtGui.QIcon.themeSearchPaths():
# in your local icon directory:
# ln -s /your/icon/theme/directory $HOME/.icons/hicolor
-class TextSorterDialog(QtGui.QWidget, Ui_TextSorterDialog):
- def __init__(self, parent = None):
- super(TextSorterDialog, self).__init__(parent)
+def get_preview_text(text):
+ return re.sub(" +", " ", text.replace("\n", " ")).strip()[:20]
+
+
+class EditDialog(QtGui.QWidget, Ui_EditDialog):
+ def __init__(self, parent=None):
+ super(EditDialog, self).__init__(parent)
self.setupUi(self)
+ self.model = None
self.fill_list()
self.text_list.clicked.connect(self.slot_show_text)
- self.remove_button.clicked.connect(self.slot_removeItem)
+ self.remove_button.clicked.connect(self.slot_remove_item)
self.move_up_button.clicked.connect(self.slot_text_up)
self.move_down_button.clicked.connect(self.slot_text_down)
self.text_list.clicked.connect(self.slot_toggle_buttons)
@@ -71,9 +92,9 @@ class TextSorterDialog(QtGui.QWidget, Ui_TextSorterDialog):
def fill_list(self):
self.model = self.parent().parent().model
self.text_list.setModel(self.model)
- ix = self.parent().parent().current_index
- index = self.model.index(ix, 0)
- self.text_list.setCurrentIndex(index)
+ index = self.parent().parent().current_index
+ model_index = self.model.index(index, 0)
+ self.text_list.setCurrentIndex(model_index)
def slot_text_up(self):
@@ -103,12 +124,11 @@ class TextSorterDialog(QtGui.QWidget, Ui_TextSorterDialog):
def slot_show_text(self, model_index):
try:
- self.text_preview.setTextOrHtml(self.parent().parent().model.text_db[model_index.row()][1])
+ self.text_preview.setHtml(self.parent().parent().model.text_db[model_index.row()][1])
except IndexError:
pass
-
- def slot_removeItem(self):
+ def slot_remove_item(self):
index = self.text_list.currentIndex().row()
self.model.removeRows(index, 1)
index = self.model.index(0, 0)
@@ -116,63 +136,6 @@ class TextSorterDialog(QtGui.QWidget, Ui_TextSorterDialog):
self.text_list.clicked.emit(index)
self.parent().parent().db_dirty = True
-class FadeAnimation(QtCore.QObject):
- animation_started = QtCore.pyqtSignal()
- animation_finished = QtCore.pyqtSignal()
- animation_stopped = QtCore.pyqtSignal()
-
- def __init__(self, live_text, fade_steps=6, parent=None):
- super(FadeAnimation, self).__init__(parent)
- self.live_text = live_text
- self.fade_steps = fade_steps
- self.current_alpha = 255
- self.timer = None
-
-
- def start_animation(self):
- print "start_animation"
- self.animation_started.emit()
-
- if self.current_alpha == 255:
- self.fade_delta = 255 / self.fade_steps
- else:
- self.fade_delta = -255 / self.fade_steps
- self.timer = QtCore.QTimer(self)
- self.timer.timeout.connect(self.slot_animate)
- self.timer.start(100)
-
-
- def slot_animate(self):
- print "slot_animate"
- print "current_alpha", self.current_alpha
- if self.fade_delta > 0:
- if self.current_alpha > 0:
- self.live_text.setStyleSheet("color:%d, %d, %d;" % (self.current_alpha, self.current_alpha,self.current_alpha))
- self.current_alpha -= self.fade_delta
- else:
- self.live_text.setStyleSheet("color:black;")
- self.current_alpha = 0
- self.timer.stop()
- self.timer.timeout.disconnect(self.slot_animate)
- self.timer.deleteLater()
- self.timer = None
- self.animation_finished.emit()
- print "animation_finished"
- else:
- if self.current_alpha < 255:
- self.live_text.setStyleSheet("color:%d,%d, %d;" % (self.current_alpha, self.current_alpha,self.current_alpha))
- self.current_alpha -= self.fade_delta
- else:
- self.live_text.setStyleSheet("color:white")
- self.current_alpha = 255
- self.timer.stop()
- self.timer.timeout.disconnect(self.slot_animate)
- self.timer.deleteLater()
- self.timer = None
- self.animation_finished.emit()
- print "animation_finished"
-
-
class TextAnimation(QtCore.QObject):
animation_started = QtCore.pyqtSignal()
animation_finished = QtCore.pyqtSignal()
@@ -214,7 +177,6 @@ class TextAnimation(QtCore.QObject):
def slot_animate(self):
self.animation_started.emit()
- parent = self.parent()
if self.it is None:
src_root_frame = self.src_document.rootFrame()
@@ -271,13 +233,14 @@ class TextAnimation(QtCore.QObject):
self.count += 1
-class MainWindow(KMainWindow, Ui_MainWindow):
- def __init__(self, parent=None):
- super(MainWindow, self).__init__(parent)
-
+class MainWindow(KMainWindow, Ui_MainWindow, MjpegStreamingConsumerInterface):
+ def __init__(self, args, parent=None):
+ self.args = args
+ #super(MainWindow, self).__init__()
+ #PsyQtClientBase.__init__(self)
+ KMainWindow.__init__(self, parent)
self.is_streaming = False
- self.ffserver = None
- self.ffmpeg = None
+
self.live_center_action = None
self.preview_center_action = None
self.live_size_action = None
@@ -301,78 +264,81 @@ class MainWindow(KMainWindow, Ui_MainWindow):
self.is_auto_publish = False
self.setupUi(self)
+ self.win_id = self.live_text.winId()
- self.fade_animation = FadeAnimation(self.live_text, 6, self)
+ 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)
self.font = QtGui.QFont("monospace", self.default_size)
self.font.setStyleHint(QtGui.QFont.TypeWriter)
+ self.previous_action = None
+ self.next_action = None
+ self.publish_action = None
+ self.auto_publish_action = None
+ self.save_live_action = None
+ self.save_preview_action = None
+ self.save_action = None
+ self.dialog_widget = None
+ self.action_collection = None
+ self.streaming_action = None
+ self.text_combo = None
+ self.clear_live_action = None
+ self.clear_preview_action = None
+ self.toolbar = None
+ self.typer_animation_action = None
+ self.text_editor_action = None
- self.create_toolbar()
-
- #self.preview_text.document().setDefaultFont(self.font)
self.preview_text.setFont(self.font)
self.preview_text.setRichTextSupport(KRichTextWidget.RichTextSupport(0xffffffff))
self.preview_editor_collection = KActionCollection(self)
self.preview_text.createActions(self.preview_editor_collection)
self.live_text.setRichTextSupport(KRichTextWidget.RichTextSupport(0xffffffff))
- #self.live_text.document().setDefaultFont(self.font)
self.live_text.setFont(self.font)
self.live_editor_collection = KActionCollection(self)
self.live_text.createActions(self.live_editor_collection)
self.filter_editor_actions()
+ self.create_toolbar()
self.slot_load()
+ qtapp.focusChanged.connect(self.focusChanged)
+ self.start_streaming()
+
self.show()
+ timer = QtCore.QTimer()
+ timer.start(2000)
+ timer.timeout.connect(lambda: None)
- self.save_action.triggered.connect(self.slot_save)
+ def pubdir(self):
+ return os.path.dirname(os.path.abspath(__file__))
- self.publish_action.triggered.connect(self.slot_publish)
- self.clear_live_action.triggered.connect(self.slot_clear_live)
- self.clear_preview_action.triggered.connect(self.slot_clear_preview)
- self.text_combo.triggered[int].connect(self.slot_load_preview_text)
-
- app.focusChanged.connect(self.focusChanged)
- self.text_editor_action.triggered.connect(self.slot_open_dialog)
- self.save_live_action.triggered.connect(self.slot_save_live_text)
- self.save_preview_action.triggered.connect(self.slot_save_preview_text)
- self.streaming_action.triggered.connect(self.slot_toggle_streaming)
- self.auto_publish_action.toggled.connect(self.slot_auto_publish)
- self.typer_animation_action.toggled.connect(self.slot_toggle_animation)
- self.preview_size_action.triggered[QtGui.QAction].connect(self.slot_preview_font_size)
- self.live_size_action.triggered[QtGui.QAction].connect(self.slot_live_font_size)
-
- #self.fade_action.triggered.connect(self.slot_fade)
- self.next_action.triggered.connect(self.slot_next_item)
- self.previous_action.triggered.connect(self.slot_previous_item)
-
- self.getLiveCoords()
- print "desktop", app.desktop().availableGeometry()
-
-
- def getLiveCoords(self):
- public_rect = self.live_text.geometry()
- global_rect = QtCore.QRect(self.mapToGlobal(public_rect.topLeft()), self.mapToGlobal(public_rect.bottomRight()))
- x = global_rect.x()
- y = global_rect.y()
- self.statusBar().showMessage("live text editor dimensions: x=%r, y=%r, width=%r, height=%r" % (x, y, global_rect.width(), global_rect.height()))
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):
+ public_rect = self.live_text_rect()
+ #global_rect = QtCore.QRect(self.mapToGlobal(public_rect.topLeft()), self.mapToGlobal(public_rect.bottomRight()))
+ pixmap = QPixmap.grabWindow(self.win_id, public_rect.x() + 1, public_rect.y() + 1, 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",
"format_painter",
"direction_ltr",
"direction_rtl",
"format_font_family",
- #"format_font_size",
"format_text_background_color",
"format_list_style",
"format_list_indent_more",
@@ -382,7 +348,6 @@ class MainWindow(KMainWindow, Ui_MainWindow):
"format_text_strikeout",
"format_text_italic",
"format_align_right",
- #"format_align_justify",
"manage_link",
"format_text_subscript",
"format_text_superscript",
@@ -391,7 +356,6 @@ class MainWindow(KMainWindow, Ui_MainWindow):
for action in self.live_editor_collection.actions():
text = str(action.objectName())
- print "text", text
if text in disabled_action_names:
action.setVisible(False)
@@ -417,10 +381,10 @@ class MainWindow(KMainWindow, Ui_MainWindow):
self.slot_set_preview_defaults()
self.slot_set_live_defaults()
-
def create_toolbar(self):
self.toolbar = KToolBar(self, True, True)
+ self.toolbar.setIconDimensions(16)
self.toolbar.setAllowedAreas(QtCore.Qt.BottomToolBarArea)
self.toolbar.setMovable(False)
self.toolbar.setFloatable(False)
@@ -432,32 +396,31 @@ class MainWindow(KMainWindow, Ui_MainWindow):
self.action_collection.addAssociatedWidget(self.toolbar)
self.clear_live_action = self.action_collection.addAction("clear_live_action")
- icon = QtGui.QIcon.fromTheme(_fromUtf8("edit-clear"))
+ icon = QtGui.QIcon(":texter/images/edit-clear.png")
self.clear_live_action.setIcon(icon)
self.clear_live_action.setIconText("clear live")
self.clear_live_action.setShortcut(KShortcut(QtGui.QKeySequence(QtCore.Qt.ALT + QtCore.Qt.Key_Q)), KAction.ShortcutTypes(KAction.ActiveShortcut | KAction.DefaultShortcut))
self.save_live_action = self.action_collection.addAction("save_live_action")
- icon = QtGui.QIcon.fromTheme(_fromUtf8("document-new"))
+ icon = QtGui.QIcon(":texter/images/document-new.png")
self.save_live_action.setIcon(icon)
self.save_live_action.setIconText("save live")
self.save_live_action.setShortcut(KShortcut(QtGui.QKeySequence(QtCore.Qt.ALT + QtCore.Qt.Key_W)), KAction.ShortcutTypes(KAction.ActiveShortcut | KAction.DefaultShortcut))
self.clear_preview_action = self.action_collection.addAction("clear_preview_action")
- icon = QtGui.QIcon.fromTheme(_fromUtf8("edit-clear"))
+ icon = QtGui.QIcon(":texter/images/edit-clear.png")
self.clear_preview_action.setIcon(icon)
self.clear_preview_action.setIconText("clear preview")
- #self.clear_preview_action.setObjectName("clear_preview")
self.clear_preview_action.setShortcut(KShortcut(QtGui.QKeySequence(QtCore.Qt.ALT + QtCore.Qt.Key_A)), KAction.ShortcutTypes(KAction.ActiveShortcut | KAction.DefaultShortcut))
self.save_preview_action = self.action_collection.addAction("save_preview_action")
- icon = QtGui.QIcon.fromTheme(_fromUtf8("document-new"))
+ icon = QtGui.QIcon(":texter/images/document-new.png")
self.save_preview_action.setIcon(icon)
self.save_preview_action.setIconText("save preview")
self.save_preview_action.setShortcut(KShortcut(QtGui.QKeySequence(QtCore.Qt.ALT + QtCore.Qt.Key_S)), KAction.ShortcutTypes(KAction.ActiveShortcut | KAction.DefaultShortcut))
self.publish_action = self.action_collection.addAction("publish_action")
- icon = QtGui.QIcon.fromTheme(_fromUtf8("edit-copy"))
+ icon = QtGui.QIcon(":texter/images/edit-copy.png")
self.publish_action.setIcon(icon)
self.publish_action.setIconText("publish")
self.publish_action.setShortcutConfigurable(True)
@@ -467,14 +430,14 @@ class MainWindow(KMainWindow, Ui_MainWindow):
self.auto_publish_action = KToggleAction(self.action_collection)
self.action_collection.addAction("auto publish", self.auto_publish_action)
- icon = QtGui.QIcon.fromTheme(_fromUtf8("view-refresh"))
+ icon = QtGui.QIcon(":texter/images/view-refresh.png")
self.auto_publish_action.setIcon(icon)
self.auto_publish_action.setObjectName("auto_publish_action")
self.auto_publish_action.setIconText("auto publish")
self.auto_publish_action.setShortcut(KShortcut(QtGui.QKeySequence(QtCore.Qt.ALT + QtCore.Qt.Key_P)), KAction.ShortcutTypes(KAction.ActiveShortcut | KAction.DefaultShortcut))
self.typer_animation_action = KToggleAction(self.action_collection)
- icon = QtGui.QIcon.fromTheme(_fromUtf8("media-playback-stop"))
+ icon = QtGui.QIcon(":texter/images/media-playback-stop.png")
self.typer_animation_action.setIcon(icon)
self.typer_animation_action.setIconText("animate")
self.typer_animation_action.setObjectName("typer_animation_action")
@@ -482,7 +445,7 @@ class MainWindow(KMainWindow, Ui_MainWindow):
self.action_collection.addAction("typer_animation_action", self.typer_animation_action)
self.text_editor_action = self.action_collection.addAction("text_editor_action")
- icon = QtGui.QIcon.fromTheme(_fromUtf8("document-open-data"))
+ icon = QtGui.QIcon(":texter/images/document-open-data.png")
self.text_editor_action.setIcon(icon)
self.text_editor_action.setIconText("edit")
self.text_editor_action.setShortcut(KShortcut(QtGui.QKeySequence(QtCore.Qt.CTRL + QtCore.Qt.Key_O)), KAction.ShortcutTypes(KAction.ActiveShortcut | KAction.DefaultShortcut))
@@ -490,13 +453,13 @@ class MainWindow(KMainWindow, Ui_MainWindow):
self.toolbar.insertSeparator(self.text_editor_action)
self.save_action = self.action_collection.addAction("save_action")
- icon = QtGui.QIcon.fromTheme(_fromUtf8("document-save"))
+ icon = QtGui.QIcon(":texter/images/document-save.png")
self.save_action.setIcon(icon)
self.save_action.setIconText("save")
self.save_action.setShortcut(KShortcut(QtGui.QKeySequence(QtCore.Qt.CTRL + QtCore.Qt.Key_S)), KAction.ShortcutTypes(KAction.ActiveShortcut | KAction.DefaultShortcut))
self.streaming_action = KToggleAction(self.action_collection)
- icon = QtGui.QIcon.fromTheme(_fromUtf8("media-record"))
+ icon = QtGui.QIcon(":texter/images/media-record.png")
self.streaming_action.setIcon(icon)
self.streaming_action.setIconText("stream")
self.streaming_action.setObjectName("stream")
@@ -506,37 +469,49 @@ class MainWindow(KMainWindow, Ui_MainWindow):
spacer = KToolBarSpacerAction(self.action_collection)
self.action_collection.addAction("1_spacer", spacer)
- #self.fade_action = self.action_collection.addAction("fade_action")
- ##icon = QtGui.QIcon.fromTheme(_fromUtf8("go-previous-view-page"))
- ##self.fade_action.setIcon(icon)
- #self.fade_action.setIconText("fade")
- #self.fade_action.setShortcut(KShortcut(QtGui.QKeySequence(QtCore.Qt.ALT + QtCore.Qt.Key_F)), KAction.ShortcutTypes(KAction.ActiveShortcut | KAction.DefaultShortcut))
-
self.previous_action = self.action_collection.addAction("previous_action")
- icon = QtGui.QIcon.fromTheme(_fromUtf8("go-previous-view-page"))
+ icon = QtGui.QIcon(":texter/images/go-previous-view-page.png")
self.previous_action.setIcon(icon)
self.previous_action.setIconText("previous")
self.previous_action.setShortcut(KShortcut(QtGui.QKeySequence(QtCore.Qt.ALT + QtCore.Qt.Key_Left)), KAction.ShortcutTypes(KAction.ActiveShortcut | KAction.DefaultShortcut))
self.text_combo = KSelectAction(self.action_collection)
self.text_combo.setEditable(False)
- icon = QtGui.QIcon.fromTheme(_fromUtf8("document-open-recent"))
+ icon = QtGui.QIcon(":texter/images/document-open-recent.png")
self.text_combo.setIcon(icon)
self.text_combo.setIconText("saved texts")
self.text_combo.setObjectName("text_combo")
self.action_collection.addAction("saved texts", self.text_combo)
self.next_action = self.action_collection.addAction("next_action")
- icon = QtGui.QIcon.fromTheme(_fromUtf8("go-next-view-page"))
+ icon = QtGui.QIcon(":texter/images/go-next-view-page.png")
self.next_action.setIcon(icon)
self.next_action.setIconText("next")
self.next_action.setShortcut(KShortcut(QtGui.QKeySequence(QtCore.Qt.ALT + QtCore.Qt.Key_Right)), KAction.ShortcutTypes(KAction.ActiveShortcut | KAction.DefaultShortcut))
self.toolbar.addSeparator()
+ self.save_action.triggered.connect(self.slot_save)
+
+ self.publish_action.triggered.connect(self.slot_publish)
+ self.clear_live_action.triggered.connect(self.slot_clear_live)
+ self.clear_preview_action.triggered.connect(self.slot_clear_preview)
+ self.text_combo.triggered[int].connect(self.slot_load_preview_text)
+ self.text_editor_action.triggered.connect(self.slot_open_dialog)
+ self.save_live_action.triggered.connect(self.slot_save_live_text)
+ self.save_preview_action.triggered.connect(self.slot_save_preview_text)
+ self.streaming_action.triggered.connect(self.slot_toggle_streaming)
+ self.auto_publish_action.toggled.connect(self.slot_auto_publish)
+ self.typer_animation_action.toggled.connect(self.slot_toggle_animation)
+ self.preview_size_action.triggered[QtGui.QAction].connect(self.slot_preview_font_size)
+ self.live_size_action.triggered[QtGui.QAction].connect(self.slot_live_font_size)
+
+ self.next_action.triggered.connect(self.slot_next_item)
+ self.previous_action.triggered.connect(self.slot_previous_item)
+ self.streaming_action.setChecked(True)
def closeEvent(self, event):
- self.stop_streaming()
+ logger.info("closeEvent")
if self.db_dirty:
self.dialog = KDialog(self)
self.dialog.setCaption("4.48 texter - text db not saved")
@@ -545,23 +520,37 @@ class MainWindow(KMainWindow, Ui_MainWindow):
self.dialog.setButtons(KDialog.ButtonCodes(KDialog.Ok | KDialog.Cancel))
self.dialog.okClicked.connect(self.slot_save)
self.dialog.exec_()
+ event.accept()
+
+ def live_text_rect(self):
+ return self.live_text.geometry()
def stop_streaming(self):
self.is_streaming = False
- if self.ffmpeg is not None:
- self.ffmpeg.kill()
- self.ffmpeg = None
- if self.ffserver is not None:
- self.ffserver.kill()
- self.ffserver = None
+ self.http_server.stop()
def start_streaming(self):
- public_rect = self.live_text.geometry()
- global_rect = QtCore.QRect(self.mapToGlobal(public_rect.topLeft()), self.mapToGlobal(public_rect.bottomRight()))
- self.ffserver = subprocess.Popen("ffserver -f /etc/ffserver.conf", shell=True, close_fds=True)
- self.ffmpeg = subprocess.Popen("ffmpeg -f x11grab -show_region 1 -s 768x576 -r 30 -i :0.0+%d,%d -vcodec mjpeg -pix_fmt yuvj444p -r 30 -aspect 4:3 http://localhost:8090/webcam.ffm" % (global_rect.x()+3, global_rect.y()+3), shell=True, close_fds=True)
+ 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()
@@ -570,18 +559,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 get_preview_text(self, text):
- return re.sub(" +", " ", text.replace("\n", " ")).strip()[:20]
-
-
def slot_auto_publish(self, state):
self.is_auto_publish = bool(state)
@@ -589,11 +566,10 @@ class MainWindow(KMainWindow, Ui_MainWindow):
self.is_animate = bool(state)
def slot_toggle_streaming(self):
- if self.ffserver is None:
- self.start_streaming()
- else:
+ if self.is_streaming:
self.stop_streaming()
-
+ else:
+ self.start_streaming()
def slot_next_item(self):
try:
@@ -603,7 +579,6 @@ class MainWindow(KMainWindow, Ui_MainWindow):
except ZeroDivisionError:
pass
-
def slot_previous_item(self):
try:
self.current = (self.text_combo.currentItem() - 1) % len(self.model.text_db)
@@ -612,26 +587,22 @@ class MainWindow(KMainWindow, Ui_MainWindow):
except ZeroDivisionError:
pass
-
def slot_publish(self):
if self.is_animate:
self.animation.start_animation(self.preview_text, self.live_text, 0)
else:
self.live_text.setTextOrHtml(self.preview_text.textOrHtml())
-
def slot_live_font_size(self, action):
self.default_size = self.live_size_action.fontSize()
self.slot_set_preview_defaults()
self.slot_set_live_defaults()
-
def slot_preview_font_size(self, action):
self.default_size = self.preview_size_action.fontSize()
self.slot_set_live_defaults()
self.slot_set_preview_defaults()
-
def slot_toggle_publish(self, state=None):
if state:
@@ -639,7 +610,6 @@ class MainWindow(KMainWindow, Ui_MainWindow):
else:
self.slot_clear_live()
-
def slot_set_preview_defaults(self):
self.preview_center_action.setChecked(True)
self.preview_text.alignCenter()
@@ -668,23 +638,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 ix, 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 = ix
-
- 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:
@@ -697,7 +650,7 @@ class MainWindow(KMainWindow, Ui_MainWindow):
def slot_save_live_text(self):
text = self.live_text.toHtml()
- preview = self.get_preview_text(unicode(self.live_text.toPlainText()))
+ preview = get_preview_text(unicode(self.live_text.toPlainText()))
if not preview:
return
old_item = self.model.text_by_preview(preview)
@@ -720,7 +673,7 @@ class MainWindow(KMainWindow, Ui_MainWindow):
def slot_save_preview_text(self):
text = self.preview_text.toHtml()
- preview = self.get_preview_text(unicode(self.preview_text.toPlainText()))
+ preview = get_preview_text(unicode(self.preview_text.toPlainText()))
if not preview:
return
@@ -747,7 +700,6 @@ class MainWindow(KMainWindow, Ui_MainWindow):
cPickle.dump(self.model.text_db, f, cPickle.HIGHEST_PROTOCOL)
self.db_dirty = False
-
def slot_open_dialog(self):
self.current_index = self.text_combo.currentItem()
self.current_object = self.model.text_db[self.current_index]
@@ -756,42 +708,61 @@ class MainWindow(KMainWindow, Ui_MainWindow):
self.dialog = None
self.dialog = KDialog(self)
- self.dialog_widget = TextSorterDialog(self.dialog)
+ 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, 0)
- rect = app.desktop().availableGeometry()
- global_width = rect.width()
- global_height = rect.height()
- x = global_width - pos_x - 10
- #self.dialog.setFixedSize(x, global_height-40)
- self.dialog.okClicked.connect(self.fill_combo_box)
+ self.dialog.move(pos_x, self.pos().y())
self.dialog.exec_()
+ self.fill_combo_box()
def slot_load(self):
path = os.path.expanduser("~/.texter")
if not os.path.isdir(path):
os.mkdir(path)
try:
- f = open(os.path.join(path, "texter.db"))
+ db_file = open(os.path.join(path, "texter.db"))
except IOError:
return
try:
- self.model.text_db = [list(i) for i in cPickle.load(f)]
- except Exception, e:
- print e
+ self.model.text_db = [list(i) for i in cPickle.load(db_file)]
+ except ValueError, error:
+ logger.exception(error)
self.fill_combo_box()
self.text_combo.setCurrentItem(0)
self.slot_load_preview_text(0)
+ def sigint_handler(self, ex_cls, ex, tb):
+ """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))
def main():
- window = MainWindow()
- app.exec_()
+ arg_parser = ArgParser("texter")
+ 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=9001,
+ type=int, help='my port, defaults to 9001')
+ 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)
+
+ window = MainWindow(args, None)
+ sys.excepthook = window.sigint_handler
+ qtapp.exec_()
-if ( __name__ == '__main__' ):
+if __name__ == '__main__':
main()
diff --git a/texter/texter/text_model.py b/texter/texter/text_model.py
index b3a9262..e99e2dc 100644
--- a/texter/texter/text_model.py
+++ b/texter/texter/text_model.py
@@ -16,11 +16,9 @@ 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 role not in (1,3,4,5,6,7,8,9,10,13):
- print "role", role
if not index.isValid() or \
not 0 <= index.row() < self.rowCount():
return QVariant()
@@ -28,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()
@@ -47,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()
@@ -54,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/text_sorter.ui b/texter/texter/text_sorter.ui
deleted file mode 100644
index e5c8389..0000000
--- a/texter/texter/text_sorter.ui
+++ /dev/null
@@ -1,157 +0,0 @@
-
-
- text_sorter_dialog
-
-
-
- 0
- 0
-
-
-
- Dialog
-
-
- -
-
-
-
-
-
-
- 0
- 576
-
-
-
-
- 16777215
- 576
-
-
-
-
- -
-
-
-
-
-
-
- Remove
-
-
-
-
-
-
- -
-
-
- -
-
-
- 2
-
-
-
- -
-
-
- Qt::Vertical
-
-
-
- 20
- 40
-
-
-
-
-
-
-
- -
-
-
-
-
- -
-
-
- Qt::Horizontal
-
-
- QDialogButtonBox::Cancel|QDialogButtonBox::Ok
-
-
-
-
-
-
-
- KArrowButton
- QPushButton
-
-
-
- KRichTextEdit
- KTextEdit
-
-
-
- KButtonGroup
- QGroupBox
-
- 1
-
-
- KPushButton
- QPushButton
-
-
-
- KTextEdit
- QTextEdit
-
-
-
- KRichTextWidget
- KRichTextEdit
-
-
-
-
-
-
- buttonBox
- accepted()
- text_sorter_dialog
- accept()
-
-
- 248
- 254
-
-
- 157
- 274
-
-
-
-
- buttonBox
- rejected()
- text_sorter_dialog
- reject()
-
-
- 316
- 260
-
-
- 286
- 274
-
-
-
-
-
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.qrc b/texter/texter/texter.qrc
index b4b4a29..6416308 100644
--- a/texter/texter/texter.qrc
+++ b/texter/texter/texter.qrc
@@ -1,5 +1,16 @@
+ images/document-new.png
+ images/document-open-data.png
+ images/document-open-recent.png
+ images/document-save.png
+ images/edit-clear.png
+ images/edit-copy.png
+ images/go-next-view-page.png
+ images/go-previous-view-page.png
+ images/media-playback-stop.png
+ images/media-record.png
+ images/view-refresh.png
icon.png
diff --git a/texter/texter/texter.ui b/texter/texter/texter.ui
index 335ee8d..923809b 100644
--- a/texter/texter/texter.ui
+++ b/texter/texter/texter.ui
@@ -6,67 +6,13 @@
0
0
- 1207
- 634
+ 1475
+ 592
-
-
-
- 255
- 255
- 255
-
-
-
-
-
-
- 0
- 0
- 255
-
-
-
-
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- 0
- 0
- 0
-
-
-
@@ -76,70 +22,7 @@
-
-
-
- 255
- 255
- 255
-
-
-
-
-
-
- 255
- 255
- 255
-
-
-
-
-
- 0
- 0
- 255
-
-
-
-
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- 255
- 255
- 220
-
-
-
-
0
@@ -150,60 +33,6 @@
-
-
-
- 255
- 255
- 255
-
-
-
-
-
-
- 0
- 0
- 255
-
-
-
-
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- 0
- 0
- 0
-
-
-
@@ -213,70 +42,7 @@
-
-
-
- 255
- 255
- 255
-
-
-
-
-
-
- 255
- 255
- 255
-
-
-
-
-
- 0
- 0
- 255
-
-
-
-
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- 255
- 255
- 220
-
-
-
-
0
@@ -287,338 +53,67 @@
-
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- 0
- 0
- 255
-
-
-
-
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- 0
- 0
- 0
-
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- 255
- 255
- 255
-
-
-
-
-
-
- 0
- 0
- 0
+ 169
+ 167
+ 167
- 0
- 0
- 0
-
-
-
-
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- 255
- 255
- 220
-
-
-
-
-
-
- 0
- 0
- 0
+ 244
+ 244
+ 244
+
+
+ Helvetica
+ 7
+
+
4.48 Texter
+
+
+ :/texter/icon.png:/texter/icon.png
+
- -
-
-
-
-
-
- &Title
-
-
- item_title
-
-
-
- -
-
-
-
- 100
- 0
-
-
-
-
- -
-
-
- P&osition
-
-
- item_position_input
-
-
-
- -
-
-
-
- 50
- 16777215
-
-
-
-
- -
-
-
- S&election
-
-
- edit_item_selection
-
-
-
- -
-
-
-
- 100
- 0
-
-
-
-
- -
-
-
- &Add / Change
-
-
-
- -
-
-
- &Remove
-
-
-
- -
-
-
- Sa&ve
-
-
-
- -
-
-
- &Publish
-
-
- false
-
-
-
- -
-
-
- Clear Live
-
-
-
- -
-
-
- Clear Preview
-
-
-
- -
-
-
- Previous
-
-
-
- -
-
-
- Next
-
-
-
-
-
-
- -
-
-
- A&uto Publish
-
-
-
- -
-
-
- Qt::Horizontal
-
-
-
- 40
- 20
-
-
-
-
-
-
-
-
- 768
- 576
+ 775
+ 582
- 768
- 576
+ 775
+ 582
-
-
-
- 255
- 255
- 255
-
-
-
-
-
-
- 255
- 255
- 255
-
-
-
0
0
- 255
+ 0
@@ -627,36 +122,36 @@
0
0
+ 0
+
+
+
+
+
+
+ 0
+ 0
+ 0
+
+
+
+
+
+
+ 255
+ 255
255
-
-
-
- 255
- 255
- 255
-
-
-
-
-
-
- 255
- 255
- 255
-
-
-
0
0
- 255
+ 0
@@ -665,36 +160,36 @@
0
0
+ 0
+
+
+
+
+
+
+ 0
+ 0
+ 0
+
+
+
+
+
+
+ 255
+ 255
255
-
-
-
- 128
- 125
- 123
-
-
-
-
-
-
- 128
- 125
- 123
-
-
-
0
0
- 255
+ 0
@@ -703,6 +198,24 @@
0
0
+ 0
+
+
+
+
+
+
+ 0
+ 0
+ 0
+
+
+
+
+
+
+ 255
+ 255
255
@@ -710,14 +223,14 @@
-
-
- Monospace
- 22
-
-
- true
+ false
+
+
+ Qt::ScrollBarAlwaysOff
+
+
+ Qt::ScrollBarAlwaysOff
true
@@ -725,49 +238,40 @@
Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextEditable|Qt::TextEditorInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse
+
+ KRichTextWidget::SupportAlignment|KRichTextWidget::SupportFontFamily|KRichTextWidget::SupportFontSize|KRichTextWidget::SupportTextForegroundColor
+
-
+
+
+ 10
+ 0
+
+
- 400
- 576
+ 300
+ 582
- 768
- 576
+ 775
+ 582
-
-
-
- 255
- 255
- 255
-
-
-
-
-
-
- 255
- 255
- 255
-
-
-
0
0
- 255
+ 0
@@ -776,36 +280,18 @@
0
0
- 255
+ 0
-
-
-
- 255
- 255
- 255
-
-
-
-
-
-
- 255
- 255
- 255
-
-
-
0
0
- 255
+ 0
@@ -814,36 +300,18 @@
0
0
- 255
+ 0
-
-
-
- 128
- 125
- 123
-
-
-
-
-
-
- 128
- 125
- 123
-
-
-
0
0
- 255
+ 0
@@ -852,24 +320,18 @@
0
0
- 255
+ 0
-
-
- Monospace
- 22
-
-
-
- Qt::ActionsContextMenu
+
+ preview text
- true
+ false
QFrame::StyledPanel
@@ -880,13 +342,15 @@
Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextEditable|Qt::TextEditorInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse
+
+ KRichTextWidget::SupportAlignment|KRichTextWidget::SupportChangeListStyle|KRichTextWidget::SupportFontFamily|KRichTextWidget::SupportFontSize|KRichTextWidget::SupportTextForegroundColor
+
-
@@ -904,17 +368,13 @@
KRichTextEdit
-
- KIntNumInput
- QWidget
-
-
- item_title
- item_position_input
- remove_item_button
+ live_text
+ preview_text
-
+
+
+
diff --git a/texter/texter/texter2.ui b/texter/texter/texter2.ui
deleted file mode 100644
index c4e8da1..0000000
--- a/texter/texter/texter2.ui
+++ /dev/null
@@ -1,395 +0,0 @@
-
-
- MainWindow
-
-
-
- 0
- 0
- 1554
- 617
-
-
-
-
-
-
-
-
- 255
- 255
- 255
-
-
-
-
-
-
- 0
- 0
- 0
-
-
-
-
-
-
-
-
- 255
- 255
- 255
-
-
-
-
-
-
- 0
- 0
- 0
-
-
-
-
-
-
-
-
- 169
- 167
- 167
-
-
-
-
-
-
- 244
- 244
- 244
-
-
-
-
-
-
-
- 4.48 Texter
-
-
-
- :/texter/icon.png:/texter/icon.png
-
-
-
- -
-
-
-
-
-
-
- 768
- 576
-
-
-
-
- 768
- 576
-
-
-
-
- Monospace
- 22
-
-
-
- BlankCursor
-
-
-
-
-
-
-
-
-
-
-
- false
-
-
- true
-
-
- Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextEditable|Qt::TextEditorInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse
-
-
-
- -
-
-
-
-
-
- clear live text [F1]
-
-
-
-
-
-
-
- F1
-
-
-
- -
-
-
- save live text [F2]
-
-
-
-
-
-
-
-
- -
-
-
- Qt::Horizontal
-
-
-
- 40
- 20
-
-
-
-
- -
-
-
- starts/stops live textfield streaming [F9]
-
-
-
-
-
- true
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
- 400
- 576
-
-
-
-
- 768
- 576
-
-
-
-
- Monospace
- 22
-
-
-
- Qt::ActionsContextMenu
-
-
- preview text
-
-
- false
-
-
- QFrame::StyledPanel
-
-
- true
-
-
- Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextEditable|Qt::TextEditorInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse
-
-
-
- -
-
-
-
-
-
- clear preview text [F4]
-
-
-
-
-
-
-
-
-
-
-
-
-
- F2
-
-
-
- -
-
-
-
-
-
-
-
-
- -
-
-
- ArrowCursor
-
-
- go live with text [F5]
-
-
-
-
-
-
-
- F4
-
-
-
- -
-
-
- Qt::Vertical
-
-
-
- -
-
-
- load previous text [F6]
-
-
-
-
-
-
-
-
- -
-
-
- load next text [F7]
-
-
-
-
-
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- true
-
-
-
- -
-
-
- edit sorting of saved texts [F10]
-
-
-
-
-
-
-
-
- -
-
-
- Qt::Horizontal
-
-
-
- 40
- 20
-
-
-
-
-
-
-
-
-
-
-
-
-
- KRichTextEdit
- KTextEdit
-
-
-
- KComboBox
- QComboBox
-
-
-
- KPushButton
- QPushButton
-
-
-
- KTextEdit
- QTextEdit
-
-
-
- KRichTextWidget
- KRichTextEdit
-
-
-
-
- live_text
- preview_text
- clear_preview_button
- clear_live_button
-
-
-
-
-
-
diff --git a/texter/texter/texter3.ui b/texter/texter/texter3.ui
deleted file mode 100644
index 90aeb6e..0000000
--- a/texter/texter/texter3.ui
+++ /dev/null
@@ -1,403 +0,0 @@
-
-
- MainWindow
-
-
-
- 0
- 0
- 1475
- 651
-
-
-
-
-
-
-
-
- 255
- 255
- 255
-
-
-
-
-
-
- 0
- 0
- 0
-
-
-
-
-
-
-
-
- 255
- 255
- 255
-
-
-
-
-
-
- 0
- 0
- 0
-
-
-
-
-
-
-
-
- 169
- 167
- 167
-
-
-
-
-
-
- 244
- 244
- 244
-
-
-
-
-
-
-
- 4.48 Texter
-
-
-
- :/texter/icon.png:/texter/icon.png
-
-
-
- -
-
-
-
-
-
-
- 775
- 582
-
-
-
-
- 775
- 582
-
-
-
-
-
-
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- 255
- 255
- 255
-
-
-
-
-
-
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- 255
- 255
- 255
-
-
-
-
-
-
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- 255
- 255
- 255
-
-
-
-
-
-
-
- false
-
-
- Qt::ScrollBarAlwaysOff
-
-
- Qt::ScrollBarAlwaysOff
-
-
- true
-
-
- Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextEditable|Qt::TextEditorInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse
-
-
- KRichTextWidget::SupportAlignment|KRichTextWidget::SupportFontFamily|KRichTextWidget::SupportFontSize|KRichTextWidget::SupportTextForegroundColor
-
-
-
- -
-
-
-
- 10
- 0
-
-
-
-
- 300
- 582
-
-
-
-
- 775
- 582
-
-
-
-
-
-
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- 0
- 0
- 0
-
-
-
-
-
-
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- 0
- 0
- 0
-
-
-
-
-
-
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- 0
- 0
- 0
-
-
-
-
-
-
-
- preview text
-
-
- false
-
-
- QFrame::StyledPanel
-
-
- true
-
-
- Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextEditable|Qt::TextEditorInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse
-
-
- KRichTextWidget::SupportAlignment|KRichTextWidget::SupportChangeListStyle|KRichTextWidget::SupportFontFamily|KRichTextWidget::SupportFontSize|KRichTextWidget::SupportTextForegroundColor
-
-
-
- -
-
-
- Qt::Horizontal
-
-
- QSizePolicy::MinimumExpanding
-
-
-
- 40
- 20
-
-
-
-
-
-
- -
-
-
- Qt::Vertical
-
-
-
- 20
- 40
-
-
-
-
-
-
-
-
-
- KRichTextEdit
- KTextEdit
-
-
-
- KTextEdit
- QTextEdit
-
-
-
- KRichTextWidget
- KRichTextEdit
-
-
-
-
- live_text
- preview_text
-
-
-
-
-
-
diff --git a/texter/texter/texter4.ui b/texter/texter/texter4.ui
deleted file mode 100644
index c066dce..0000000
--- a/texter/texter/texter4.ui
+++ /dev/null
@@ -1,559 +0,0 @@
-
-
- TextSorterDialog
-
-
-
- 0
- 0
- 1084
- 633
-
-
-
- Form
-
-
- -
-
-
- 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
-
-
-
-
- -
-
-
-
-
-
-
- 2
-
-
-
- -
-
-
- -
-
-
- Remove
-
-
-
- ../../../../../../../../
-
-
-
-
-
-
- -
-
-
- Qt::Vertical
-
-
-
- 20
- 40
-
-
-
-
-
-
-
-
- KArrowButton
- QPushButton
-
-
-
- KRichTextEdit
- KTextEdit
-
-
-
- KButtonGroup
- QGroupBox
-
- 1
-
-
- KPushButton
- QPushButton
-
-
-
- KTextEdit
- QTextEdit
-
-
-
- KRichTextWidget
- KRichTextEdit
-
-
-
-
-
-
diff --git a/texter/texter/texter_rc.py b/texter/texter/texter_rc.py
index 6f17bd2..be779fe 100644
--- a/texter/texter/texter_rc.py
+++ b/texter/texter/texter_rc.py
@@ -2,7 +2,7 @@
# Resource object code
#
-# Created: Sa. Apr 12 08:49:54 2014
+# Created: So. Mai 11 12:37:13 2014
# by: The Resource Compiler for PyQt (Qt v4.8.5)
#
# WARNING! All changes made in this file will be lost!
@@ -47,6 +47,482 @@ qt_resource_data = "\
\x38\x8f\xeb\x38\x43\x35\x89\xdd\x94\xa5\xf7\x2f\xa8\x01\x6a\x80\
\x1a\xa0\x06\xf8\xdf\xf2\x1b\xf1\xb2\x57\x16\x4f\x60\xf0\x28\x00\
\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\
+\x00\x00\x02\x7f\
+\x89\
+\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
+\x00\x00\x10\x00\x00\x00\x10\x08\x03\x00\x00\x00\x28\x2d\x0f\x53\
+\x00\x00\x00\x01\x73\x52\x47\x42\x00\xae\xce\x1c\xe9\x00\x00\x00\
+\x09\x70\x48\x59\x73\x00\x00\x06\xec\x00\x00\x06\xec\x01\x1e\x75\
+\x38\x35\x00\x00\x00\x07\x74\x49\x4d\x45\x07\xd8\x07\x03\x0a\x15\
+\x1c\xbc\x0f\x78\x91\x00\x00\x01\x11\x50\x4c\x54\x45\x00\x00\x00\
+\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
+\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x01\x01\x00\x00\
+\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x32\x71\x2d\x8f\x90\x91\
+\x93\x93\x94\x8c\x8e\x8f\x90\x90\x91\x18\x48\x1d\x18\x49\x28\x1b\
+\x57\x19\x1d\x56\x17\x21\x62\x1e\x27\x70\x28\x32\x82\x34\x3d\x8e\
+\x3c\x3e\x7e\x18\x54\x9d\x4c\x55\x87\x5d\x55\x9e\x4f\x5b\x9d\x56\
+\x63\x9f\x38\x64\xa7\x53\x74\xa4\x77\x75\xac\x0c\x77\xb2\x50\x7c\
+\xb3\x78\x85\xbe\x20\x8b\xa9\x92\x8e\xc4\x23\x8f\x94\x92\x91\xc8\
+\x24\x99\xd2\x0b\x9b\x9d\x9e\x9e\x9f\xa0\x9f\x9f\xa0\x9f\xad\xa3\
+\xa0\xd7\x0c\xa2\xa3\xa3\xa6\xa7\xa8\xa7\xa7\xa8\xa8\xa8\xa8\xa8\
+\xd0\x6a\xaa\xd0\x72\xb9\xb9\xb9\xba\xba\xba\xbf\xde\x9a\xc5\xc5\
+\xc5\xc5\xc6\xc6\xc8\xca\xcb\xd2\xd2\xd2\xd2\xda\xd4\xd2\xe2\xd2\
+\xd8\xe9\xd6\xd9\xd9\xd9\xdb\xdb\xdb\xdf\xed\xde\xe3\xe3\xe3\xe9\
+\xef\xe9\xea\xea\xea\xee\xee\xee\xee\xef\xef\xef\xef\xef\xf0\xf0\
+\xf0\xf2\xf7\xf1\xf4\xf5\xf5\xf5\xf5\xf5\xf5\xf6\xf6\xf6\xf6\xf6\
+\xf7\xf7\xf7\xf8\xf8\xf8\xf8\xf9\xf9\xf9\xf9\xf9\xfa\xfa\xfa\xfb\
+\xfb\xfb\xfc\xfc\xfc\xfd\xfd\xfd\xfe\xfe\xfe\xff\xff\xff\x79\x1f\
+\xc0\x25\x00\x00\x00\x14\x74\x52\x4e\x53\x00\x10\x13\x15\x1b\x1c\
+\x24\x25\x26\x3a\x3b\x44\x4e\x4f\x7e\x8b\xdb\xdb\xdc\xdc\x37\x8a\
+\x8c\x11\x00\x00\x00\x01\x62\x4b\x47\x44\x5a\x03\xbb\xa5\xa2\x00\
+\x00\x00\xc7\x49\x44\x41\x54\x78\xda\x3d\x8c\x7b\x57\x01\x51\x14\
+\x47\xaf\x47\x1e\xdd\x48\x49\x09\x21\xa2\xbc\x9a\xd0\x28\xc9\x23\
+\x0a\x0d\xaa\x99\xdc\xb9\xe7\xce\xf7\xff\x20\x9d\xb3\xe6\xae\x7e\
+\x7f\x9d\xbd\xd7\x5e\x87\x05\xe3\xff\x0b\x30\x5a\xc2\xd3\x7b\x7b\
+\xe7\x5a\xec\x7e\x9c\xbd\x10\xa2\x74\x75\x7c\x48\x55\x42\xd9\xbf\
+\x42\xb8\xae\x9b\x7f\xa9\xd4\xb7\x1e\x47\xb1\x47\x94\x52\xe6\x9e\
+\x06\xb5\xf2\x0c\x85\x27\x10\x01\xe0\xf2\xb6\xd5\xb8\xae\x92\x20\
+\x56\x0a\xb2\xed\x6e\xa1\xb8\xe4\xec\x08\x05\x28\x69\x5b\x17\x37\
+\xe7\x77\x12\x48\x20\xc3\xca\x34\xce\x32\x0f\x00\x0a\x85\x42\x61\
+\x9b\x0a\xa6\xcd\x85\xb5\xfe\xf6\x0b\xd8\x18\xe0\x74\x46\xa6\xd1\
+\x1f\x6b\xf1\xf9\xe8\xdc\x4f\xb0\x72\xd2\x07\xfe\x8f\xaf\xd7\xde\
+\x9c\xaa\xd3\x18\x63\x21\xce\x79\x72\xf8\xfc\x41\x55\x2a\xca\xfc\
+\xe9\xea\x24\xa2\x99\x2a\x5a\x98\xee\x3f\xcd\x34\x32\x2a\x58\xbf\
+\xee\x5f\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\
+\x00\x00\x02\x6a\
+\x89\
+\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
+\x00\x00\x10\x00\x00\x00\x10\x08\x03\x00\x00\x00\x28\x2d\x0f\x53\
+\x00\x00\x00\x01\x73\x52\x47\x42\x00\xae\xce\x1c\xe9\x00\x00\x00\
+\x09\x70\x48\x59\x73\x00\x00\x06\xec\x00\x00\x06\xec\x01\x1e\x75\
+\x38\x35\x00\x00\x00\x07\x74\x49\x4d\x45\x07\xd8\x07\x03\x0f\x2d\
+\x32\x71\x8d\x0b\x4e\x00\x00\x01\x0b\x50\x4c\x54\x45\x00\x00\x00\
+\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
+\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
+\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x8f\x90\x91\x93\x93\x94\
+\x8c\x8e\x8f\x90\x90\x91\x0b\x47\x16\x16\x47\x1d\x18\x5c\x1c\x20\
+\x59\x1a\x23\x6e\x25\x28\x56\x32\x34\x83\x35\x3f\x8f\x3c\x40\x7e\
+\x17\x54\x98\x51\x54\x9d\x4c\x55\x95\x2a\x5a\x8a\x61\x5d\xa2\x58\
+\x61\xa5\x51\x73\xaf\x4e\x74\xa9\x70\x75\x91\x7c\x84\xb7\x0d\x84\
+\xbd\x23\x87\xaf\x89\x89\xc2\x20\x90\xc7\x24\x9d\xd4\x0d\x9f\x9f\
+\xa0\xa1\xd9\x0c\xa6\xa7\xa8\xa6\xc6\xa4\xa7\xa7\xa8\xa7\xd0\x6a\
+\xa8\xa8\xa8\xab\xd1\x71\xaf\xaf\xaf\xb0\xb0\xb0\xbf\xde\x99\xc0\
+\xc0\xc0\xc1\xc1\xc1\xc5\xc6\xc6\xc8\xca\xcb\xca\xd6\xcd\xcc\xe2\
+\xca\xd3\xdc\xd6\xd6\xe4\xd7\xe2\xee\xe1\xe5\xe5\xe5\xea\xea\xea\
+\xea\xef\xea\xed\xf2\xed\xee\xee\xee\xee\xef\xef\xee\xf2\xef\xef\
+\xef\xef\xf0\xf0\xf0\xf4\xf5\xf5\xf5\xf5\xf5\xf5\xf6\xf5\xf5\xf6\
+\xf6\xf6\xf6\xf6\xf6\xf7\xf6\xf7\xf7\xf7\xf8\xf8\xf8\xf8\xf9\xf8\
+\xf9\xf9\xf9\xfa\xfa\xfa\xfb\xfb\xfb\xfc\xfc\xfc\xfd\xfd\xfd\xfd\
+\xfe\xfd\xfe\xfe\xfe\xff\xff\xff\xd9\x1d\x54\xe2\x00\x00\x00\x13\
+\x74\x52\x4e\x53\x00\x10\x13\x15\x1b\x1c\x24\x25\x26\x3a\x44\x4e\
+\x4f\x6f\x7e\xdb\xdb\xdc\xdc\xc6\x90\x16\x3a\x00\x00\x00\x01\x62\
+\x4b\x47\x44\x58\xed\xb5\xc4\x8e\x00\x00\x00\xb9\x49\x44\x41\x54\
+\x18\x19\x05\xc1\x5d\x2e\x03\x51\x18\x00\xd0\xf3\xdd\x7e\x4a\x91\
+\x8c\x90\x88\x48\x08\x89\x58\x8d\xd5\x76\x05\x7e\x36\x60\x05\xbc\
+\xe0\x41\x24\xd5\xd0\x76\x66\x6a\xee\x38\x27\xcb\x2e\x80\x76\x44\
+\xee\xdf\xc1\xc7\x70\xc1\x7c\x8d\xe4\x3b\x7f\x16\x8b\xc9\x56\x4c\
+\x66\xb4\x69\x9c\xfe\xbe\xd5\xb3\xf7\xab\xe2\x1a\xf3\x64\xf1\x3a\
+\x6d\x86\x71\x35\x0d\x90\xe2\xb3\x64\x37\xd6\x12\x81\x90\x9c\xbf\
+\xac\x9a\x49\x15\x01\x72\x74\x7c\xf2\xfc\xd5\xd4\x21\x82\x52\x25\
+\x11\xcd\xe9\x4b\x4c\xa3\xa8\xb5\x48\x23\x71\x79\xb0\xd9\x29\x43\
+\xdf\x0e\x7b\x52\x40\x73\x54\xea\xd7\xb2\x2f\x87\x12\x8c\xe8\x96\
+\xb7\xb5\x7f\xda\x26\x08\x6a\xd7\xd7\xfe\x71\xb3\xcd\x76\x4e\xde\
+\x60\xc8\xfe\xbe\xed\xe4\xb0\xe6\x00\x65\x67\xf6\xb0\xe9\x49\x68\
+\xe7\xa0\xff\xc3\x3f\x9e\x71\x4d\xf4\x47\x46\x2f\xfd\x00\x00\x00\
+\x00\x49\x45\x4e\x44\xae\x42\x60\x82\
+\x00\x00\x02\x43\
+\x89\
+\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
+\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xff\x61\
+\x00\x00\x00\x04\x73\x42\x49\x54\x08\x08\x08\x08\x7c\x08\x64\x88\
+\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x01\xbb\x00\x00\x01\xbb\
+\x01\x3a\xec\xe3\xe2\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\
+\x74\x77\x61\x72\x65\x00\x77\x77\x77\x2e\x69\x6e\x6b\x73\x63\x61\
+\x70\x65\x2e\x6f\x72\x67\x9b\xee\x3c\x1a\x00\x00\x01\xc0\x49\x44\
+\x41\x54\x78\xda\x8d\x8f\xbd\x6b\x53\x51\x18\x87\x9f\x73\x72\xc1\
+\xb6\x41\x90\x82\x52\x2a\xc5\x39\xdd\x8c\x93\x75\xab\x83\x9b\xbb\
+\x53\xbb\xe4\x3f\x68\x07\x11\x8a\x25\xd4\x82\x43\x87\xa2\x20\x0e\
+\x15\x07\xe9\x6a\x16\xc5\x3d\xb5\x88\xa1\x1f\x42\xd3\x4d\x25\xe8\
+\xcd\x87\x83\x12\x92\x9b\x8f\x9b\x9c\xd7\xe6\xe0\xe1\x70\x89\x5f\
+\x3f\xf8\x71\xce\x19\x9e\xe7\x7d\x8f\x02\x2e\x00\xf3\xfc\x2b\xab\
+\xdc\xce\x5c\xcb\x2c\x65\x2f\x67\xa7\x0f\xab\x87\xf5\x72\xbb\xfc\
+\x40\x96\xe5\x49\x30\x82\x1b\x8d\xc6\x9e\x11\x41\x6b\x6d\xab\x94\
+\x4a\xf4\xd9\xc7\x1d\x76\x7f\xec\xb2\x78\x69\x91\xcd\xf9\x4d\xee\
+\x95\xef\xce\xa5\xbf\xa5\x1f\xab\x1d\x25\x1a\xc0\x08\x7f\xcd\xf6\
+\xe9\x36\xd5\x4e\x95\xfd\x4f\x05\x00\x3e\x57\x0b\x98\x41\x4d\x63\
+\x58\x0d\x00\x52\x29\xeb\xb1\xd3\xdc\x06\xda\x6f\x40\xed\x7b\x8d\
+\xe8\x4e\x84\xcb\x8b\x9b\x65\x00\xd4\x23\x35\x1b\x00\x16\x42\x04\
+\xf5\xeb\x0b\x16\x76\xe7\x59\x67\xae\xcc\x10\x3c\x57\x5c\x05\xde\
+\x2f\x09\xb9\x82\xe2\x40\x80\x8b\x84\xda\x4d\x76\xb0\x6b\xca\xdf\
+\x59\xc9\xac\x20\xe7\x35\x61\x0a\x9b\x0f\xc0\xb1\xc1\x30\xc5\x43\
+\xbf\x01\x78\x81\xff\x8a\x6d\x6e\x2e\x07\x1a\xb6\xbe\x6e\x91\x7e\
+\x99\x26\x0a\xf8\xc2\x04\x79\xb9\x25\x4f\x15\xb0\xd0\x6c\x36\xf7\
+\xbc\xc0\xd7\x6d\xe7\x4e\x97\x20\x08\x6e\x88\xc8\x5b\x7b\x07\x50\
+\x7e\x03\x57\x0f\x26\xce\x64\xbc\x00\xdc\xba\x6e\xb2\x83\xfe\x4f\
+\x40\x12\x76\x60\x42\x60\x8c\xb1\x6d\xb7\x23\x01\x62\x2f\x18\x03\
+\xc6\xe1\x4a\xa5\x42\x18\x86\x4c\x4c\x4e\xf2\xea\xf5\x9b\x77\xc0\
+\x11\xf8\x2c\x44\x51\x24\xdd\x6e\x57\xfa\xfd\xbe\xc4\x71\x2c\x83\
+\xc1\x40\x86\xc3\xa1\x18\x63\xec\xbb\x58\x2c\xda\xf7\xfd\x8d\x8d\
+\x13\x60\x4a\x44\x70\xc5\x0a\x3a\x9d\x3f\x0a\x5a\xad\x96\x94\x4a\
+\x25\x59\xcb\xe7\x47\xf0\xb4\x87\xbd\xe0\xfa\x99\xc0\xf4\x7a\xbd\
+\x11\x9c\x10\x8c\x52\xaf\x37\xcc\xda\xfa\xfa\xbe\x9f\x9c\xac\x02\
+\xce\x01\x59\x40\xf1\xfb\xc4\xc0\x91\x88\xc4\x30\x9e\x9f\xaf\xc9\
+\x06\x51\x54\x9d\xd3\x94\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\
+\x60\x82\
+\x00\x00\x02\xdf\
+\x89\
+\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
+\x00\x00\x10\x00\x00\x00\x10\x08\x03\x00\x00\x00\x28\x2d\x0f\x53\
+\x00\x00\x00\x03\x73\x42\x49\x54\x08\x08\x08\xdb\xe1\x4f\xe0\x00\
+\x00\x00\x09\x70\x48\x59\x73\x00\x00\x01\xbb\x00\x00\x01\xbb\x01\
+\x3a\xec\xe3\xe2\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\x74\
+\x77\x61\x72\x65\x00\x77\x77\x77\x2e\x69\x6e\x6b\x73\x63\x61\x70\
+\x65\x2e\x6f\x72\x67\x9b\xee\x3c\x1a\x00\x00\x01\x4d\x50\x4c\x54\
+\x45\xff\xff\xff\x59\x59\x59\x5c\x5c\x5c\x80\x80\x80\x83\x83\x83\
+\x56\x56\x56\x63\x63\x63\x79\x79\x79\x86\x86\x86\x57\x57\x57\x5e\
+\x5e\x5e\x7a\x7a\x7a\x81\x81\x81\x64\x64\x64\x74\x74\x74\x55\x55\
+\x55\x64\x64\x64\x74\x74\x74\x84\x84\x84\x53\x53\x53\x66\x66\x66\
+\x6f\x6f\x6f\x82\x82\x82\x5b\x5b\x5b\x7f\x7f\x7f\x52\x52\x52\x67\
+\x67\x67\x6b\x6b\x6b\x80\x80\x80\x55\x55\x55\x5f\x5f\x5f\x74\x74\
+\x74\x7d\x7d\x7d\x52\x52\x52\x54\x54\x54\x58\x58\x58\x5d\x5d\x5d\
+\x5f\x5f\x5f\x61\x61\x61\x66\x66\x66\x6a\x6a\x6a\x6b\x6b\x6b\x6d\
+\x6d\x6d\x6f\x6f\x6f\x71\x71\x71\x74\x74\x74\x75\x75\x75\x76\x76\
+\x76\x77\x77\x77\x79\x79\x79\x7a\x7a\x7a\x80\x80\x80\x81\x81\x81\
+\x88\x88\x88\x8f\x8f\x8f\x90\x90\x90\x9c\x9c\x9c\xb8\xb8\xb8\xba\
+\xba\xba\xbe\xbe\xbe\xce\xce\xce\xd0\xd0\xd0\xd3\xd3\xd3\xd4\xd4\
+\xd4\xd5\xd5\xd5\xd6\x04\x08\xd6\x35\x39\xd9\x06\x09\xd9\xd9\xd9\
+\xda\x1f\x22\xda\x45\x48\xdb\xdb\xdb\xdc\x55\x58\xdd\xdd\xdd\xde\
+\x37\x3a\xdf\x2d\x30\xdf\x39\x3c\xdf\x3d\x40\xdf\x60\x63\xe1\x08\
+\x0a\xe1\x39\x3b\xe2\xe2\xe2\xe3\x1c\x1e\xe7\x0b\x0c\xe7\x39\x3b\
+\xe8\xe8\xe8\xe9\xe9\xe9\xea\xea\xea\xeb\x2f\x30\xeb\x9e\xa0\xec\
+\x9e\xa0\xec\xec\xec\xed\xa3\xa5\xed\xa8\xaa\xed\xed\xed\xef\xef\
+\xef\xf0\xa0\xa1\xf0\xb8\xb9\xf1\xbd\xbf\xf1\xf1\xf1\xf2\xf2\xf2\
+\xf3\xf3\xf3\xf5\x0f\x0f\xf6\xf6\xf6\xfa\xfa\xfa\xfb\xfb\xfb\xfc\
+\xfa\xfa\xfc\xfc\xfc\xfd\xfd\xfd\xfe\xfa\xfa\xff\xff\xff\x7f\x3d\
+\xcd\x14\x00\x00\x00\x21\x74\x52\x4e\x53\x00\x01\x01\x01\x01\x26\
+\x26\x26\x26\x71\x71\x71\x71\x80\x80\x81\x81\x81\x81\xb5\xb5\xb5\
+\xb5\xb6\xb6\xe7\xe7\xe7\xe7\xf6\xf6\xf6\xf6\x96\x0a\xc3\x45\x00\
+\x00\x00\xd7\x49\x44\x41\x54\x18\x19\x05\xc1\xd1\x4e\x83\x30\x18\
+\x06\xd0\xaf\xf4\x83\xb6\x38\xe6\x46\x3a\x97\xc5\xc4\xf7\x7f\x21\
+\xaf\xbd\xd2\xe0\x2c\x66\x13\x18\x5d\xf9\x59\x3d\x87\x00\x60\x6b\
+\x56\x48\x72\x8b\x00\x08\xd0\xfa\x67\x10\x82\x6b\x88\x02\x42\xfb\
+\x17\x5a\x6d\x74\x5c\x8a\xed\xf9\x67\x25\xac\x77\xe6\x58\x97\xea\
+\x31\xfe\xf5\x7e\x98\x68\xbc\x76\x6f\xae\xd0\xc8\xe5\xc6\x7e\x78\
+\x61\xed\x36\xaf\x4e\xcf\x7d\x3a\xec\x74\x1b\xa2\x21\x0b\x65\x39\
+\x7f\x56\xba\xcb\x6d\x79\xfa\x35\xac\x8c\x12\x84\xca\x32\x5d\x5b\
+\xed\x5c\xc5\x9c\x35\xd5\xa8\xcc\xb6\xd7\x50\x44\x66\x9a\x1b\xc9\
+\xc7\x4e\x7a\xd9\x23\xcb\x98\x28\xab\xcc\xce\xab\x8b\xda\x37\x58\
+\x3b\xb9\xf3\x96\x2e\xbb\x46\x7b\x0f\x60\x5d\xbe\xd3\x9d\x29\x94\
+\xc1\xb6\xa5\x56\x79\x5d\xde\x87\x90\x88\x38\xe6\xe1\x70\x72\x94\
+\xee\x6b\x9a\x22\x88\xc7\xd9\xfa\x7b\xf7\xa4\xa6\x45\x42\xcc\x20\
+\x90\xe7\xae\xe6\x88\x24\xb7\x05\xc0\x3f\x59\xa3\x6c\xf8\xab\x3b\
+\x12\xed\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\
+\x00\x00\x02\xb9\
+\x89\
+\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
+\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xff\x61\
+\x00\x00\x00\x04\x73\x42\x49\x54\x08\x08\x08\x08\x7c\x08\x64\x88\
+\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x01\xbb\x00\x00\x01\xbb\
+\x01\x3a\xec\xe3\xe2\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\
+\x74\x77\x61\x72\x65\x00\x77\x77\x77\x2e\x69\x6e\x6b\x73\x63\x61\
+\x70\x65\x2e\x6f\x72\x67\x9b\xee\x3c\x1a\x00\x00\x02\x36\x49\x44\
+\x41\x54\x78\xda\x8d\x93\x4b\x68\x13\x51\x14\x86\xff\x99\xa4\x8d\
+\x12\xcd\x22\x2a\x51\x31\xad\xa4\xf5\x85\x56\x11\x44\xa9\x82\x14\
+\xb4\x2e\x02\x45\x21\x88\x0b\x37\x82\x1b\xf7\xa2\xa8\x28\xda\xba\
+\x93\x22\x92\x16\xb5\x50\x1b\xba\x71\xa1\xb8\xa8\x95\x22\x3e\x16\
+\x6e\x8a\x56\xac\x9a\x88\x91\xd0\xa4\xa0\x74\x9c\x49\x9a\x57\xf3\
+\xec\x4c\x26\xc7\x7b\xad\x03\xce\x10\x8b\x1f\xfc\x5c\x38\x33\xe7\
+\xbb\xe7\x32\x77\x04\x22\x82\x20\x08\x4e\x00\x3e\xfc\x0b\xa7\xc7\
+\x81\x03\xe7\x4e\x41\x68\xf6\x42\x2f\x7f\xc7\xc7\xd0\x63\xe4\xa5\
+\xaf\x44\x54\xb2\x63\x09\x1f\x11\x85\xd1\x80\x2f\x52\x11\xa7\xef\
+\xbc\x81\xbf\xb3\x1d\xfb\x3d\xc0\x94\x02\x4c\x4c\x1d\xba\x10\x7e\
+\xf5\xe4\x0c\x80\x51\xf0\x09\x18\x1d\xc4\xf8\x29\xcb\xa4\x24\x53\
+\x34\x9f\x4e\x53\x26\x9b\x25\x29\x99\x26\xd7\xc9\x21\x1a\x8f\xa4\
+\xa8\xbc\xa8\xd2\xc0\xe0\x20\x65\x72\x0b\x74\xeb\xe1\x24\x89\x3d\
+\x03\x45\x1c\xbd\xb7\x42\xc4\x5f\xd8\x6c\x36\x16\x11\xa2\x20\x40\
+\x60\x09\x8e\x4d\xc3\xe5\xdb\x82\xae\x36\x17\x6a\x9a\x8a\xf9\x54\
+\x0a\x92\x3c\x87\xc0\xc1\x4d\xd8\xb0\x7d\x87\x13\xb6\xfc\x65\x93\
+\x40\x14\xc5\xa5\x30\x11\x97\x84\x67\x15\x74\x77\xac\x07\xa0\xa3\
+\x50\x2c\x21\x18\x0c\x22\x70\xfc\x04\x8e\x74\x1d\x66\x75\x0f\x2b\
+\xab\x7b\xed\xd6\x09\xf8\xce\x5c\x22\x00\xd8\xb8\x6e\x15\xc6\x3f\
+\x49\xe8\xef\xf1\xa2\x5c\x2a\x61\x38\x34\x02\xb7\x7b\x0d\xda\xda\
+\xb7\x61\xdf\xed\x30\xd0\xa4\xcf\x35\x9e\x80\x49\xb8\xec\xbc\x7f\
+\x17\x72\xd5\x2c\xee\xbe\x4e\x40\x27\xa0\xa5\xa5\x15\x0e\x4f\x2b\
+\x46\xdf\x29\x48\x16\xd2\x1a\x3e\x3f\xea\x37\x4d\x60\x9c\xdd\x10\
+\xf9\x36\x7b\x71\x71\xe7\x7b\xdc\x7c\xfb\x03\xc1\xe7\x45\x1c\xdb\
+\xea\xc6\xcb\x99\x38\xf2\xab\x1d\xc0\xe4\xc8\x30\xa4\x68\xc2\x34\
+\x81\xc0\x1b\x2d\xb9\x76\x36\x80\xa7\xdd\x2b\xb1\x67\x6d\x06\x1f\
+\xa4\x38\x3a\xc5\x6f\x78\xe6\x77\x01\x91\x17\xf7\x89\x61\x5c\x24\
+\xfe\x19\xc3\x95\x4a\xc5\x98\x80\xaf\xa6\xfc\x79\x0f\x0c\xe3\xf9\
+\x6e\x56\x8b\x98\x8e\xc0\x8a\xcb\x46\xd7\x75\x96\x3a\x2a\xaa\x56\
+\x07\xa0\x83\xd1\x50\x60\x95\x71\xf8\x74\xb2\x2c\xc3\xde\xdc\x84\
+\xa1\x07\xa1\x09\x00\x51\x30\x4c\x37\xb1\x5a\xad\x92\xaa\xaa\xa4\
+\x69\x1a\xd5\x6a\x35\x62\x3b\x52\xbd\x5e\xff\x5d\x8b\xc5\x62\x6c\
+\x5d\xa4\xab\x7d\x7d\xd3\xdc\xcf\xfb\x58\xcc\x02\xa3\xd9\x2a\x58\
+\x28\x14\x68\x26\x11\xa7\x2b\xbd\xbd\xa6\x66\xb3\x60\x19\x94\x74\
+\x4e\xbf\x74\xfd\xc6\x98\xb5\x99\xe7\xff\x7e\x67\x40\x67\x89\x12\
+\x03\x16\x7e\x01\x57\xcf\x68\x6a\x5b\x49\x87\x93\x00\x00\x00\x00\
+\x49\x45\x4e\x44\xae\x42\x60\x82\
+\x00\x00\x02\x33\
+\x89\
+\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
+\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xff\x61\
+\x00\x00\x00\x04\x73\x42\x49\x54\x08\x08\x08\x08\x7c\x08\x64\x88\
+\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x01\xbb\x00\x00\x01\xbb\
+\x01\x3a\xec\xe3\xe2\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\
+\x74\x77\x61\x72\x65\x00\x77\x77\x77\x2e\x69\x6e\x6b\x73\x63\x61\
+\x70\x65\x2e\x6f\x72\x67\x9b\xee\x3c\x1a\x00\x00\x01\xb0\x49\x44\
+\x41\x54\x78\xda\xa5\x93\x3f\x48\xc3\x40\x18\xc5\x5f\xa2\xd8\x74\
+\xab\xda\xc1\x3a\xd6\x55\xc4\xcd\x41\x10\xff\xd0\xad\x45\xa1\xe2\
+\x24\xae\x8e\xea\xe0\x20\xc5\x4d\xb0\xe8\xac\xb8\xbb\xa8\xb8\xd9\
+\xc1\x41\x67\x37\x05\xc7\x42\xc1\x0e\xd6\x52\x41\xcd\x35\x7f\x9a\
+\xb6\x97\x78\x5f\x22\xe1\x42\x1d\x0a\x7d\xb9\x7c\x97\xc0\xbd\xdf\
+\x3d\xbe\x5c\x14\xcf\xf3\x30\x88\x86\xa9\xe4\xb2\xd9\xef\x56\xcb\
+\x49\xec\xec\xed\x43\x96\xa2\x52\x55\xa1\xaa\x0a\x14\x05\x50\x10\
+\xdc\xa7\x27\x45\x68\x5a\xec\xe7\xae\x54\x1a\xf5\x01\x96\x69\x25\
+\x26\x52\x13\x58\x58\x5c\x82\x27\x03\x80\xc0\x28\x0a\x41\xc4\xf0\
+\xe7\xdb\x9b\x2b\x54\x2a\x95\x44\x98\xc0\x75\x5d\x68\x31\x0d\xda\
+\xc8\x10\x2e\x1f\x5e\xa3\x29\xfc\x42\x23\x48\xb1\x95\x99\x01\xe7\
+\xdc\xf7\x84\x00\x2e\x5e\xba\xdd\xae\xa0\x03\x9b\x2b\xd3\xb4\x3e\
+\x4c\x42\x3d\x72\xbd\xbf\x59\x3c\x98\x4e\x87\xd6\xfa\x9e\x48\x02\
+\xa2\x36\xed\x36\x99\x43\x79\x01\x81\x06\x5c\x02\x04\x90\xde\x04\
+\x2e\xe7\x3e\xd5\x10\x80\xeb\xfb\x27\xc8\x0a\xbf\x12\xa5\x10\x57\
+\x3e\x33\x47\x6b\xc9\xd3\x9b\x80\x19\x36\x32\xf3\xb3\xb2\x1b\x9e\
+\x64\x26\x56\xd3\x74\xfe\x49\xe0\x72\x70\x41\x3d\xdc\xdd\x46\x9f\
+\x22\x8f\xd4\x44\x1e\x34\xb1\x7f\x05\x9e\x68\x0f\x3a\x1d\x90\x8e\
+\x8a\xc7\x88\xc7\xe3\x61\x7c\x3a\x03\x24\x8a\xbd\x91\x5f\xf7\xe7\
+\xa9\x74\x3a\xda\x83\x8e\xd8\xdd\x69\xb7\x41\x2a\x97\xcb\xb8\x38\
+\x3b\x87\x65\x5b\x38\x28\x14\x20\x2b\x35\x39\x89\xda\x47\x8d\x3e\
+\x21\x79\x24\x80\xd8\xdd\x71\x1c\x90\x18\x63\x30\x4d\x13\x86\x61\
+\x40\xd7\x75\xc8\xf2\x84\xd1\x15\xd1\x93\xc9\x24\xde\xaa\xd5\x48\
+\x13\xbf\x18\xd3\xc7\xe8\x34\x32\x9d\xc1\xb6\x6d\x4a\xd4\x03\x20\
+\x30\x6d\x66\x5b\x16\xb8\x9c\x40\xe1\x7c\x99\x35\x9b\x8f\xba\xce\
+\xc6\x1b\x9f\x0d\xe4\xd6\x56\x41\xaa\xd7\xeb\x90\xf5\x5e\xab\x51\
+\x0e\x3c\xbf\xbc\x40\x1d\x52\x7f\x20\x34\xf0\xef\xfc\x0b\xfb\xd2\
+\x1e\xf6\x17\x50\x4f\x5c\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\
+\x60\x82\
+\x00\x00\x02\x63\
+\x89\
+\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
+\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xff\x61\
+\x00\x00\x00\x01\x73\x52\x47\x42\x00\xae\xce\x1c\xe9\x00\x00\x00\
+\x06\x62\x4b\x47\x44\x00\xff\x00\xff\x00\xff\xa0\xbd\xa7\x93\x00\
+\x00\x00\x09\x70\x48\x59\x73\x00\x00\x6e\xba\x00\x00\x6e\xba\x01\
+\xd6\xde\xb1\x17\x00\x00\x00\x07\x74\x49\x4d\x45\x07\xd9\x0a\x16\
+\x0d\x00\x3a\x08\xf3\xbd\x44\x00\x00\x01\xe3\x49\x44\x41\x54\x78\
+\xda\x8d\x92\x3f\x68\x53\x51\x14\xc6\xbf\x73\xef\x7d\x7f\x92\x34\
+\x8d\x49\x15\x42\xc5\x14\x8b\x83\xe8\xa6\x4b\x90\x0a\x8a\x4b\x07\
+\x09\x0e\x4e\x0e\x42\x10\x5c\x1c\x1d\x9c\x9d\x9c\x05\xd7\x2e\x82\
+\x9b\x9b\x19\x3a\x18\x3a\xb4\x81\x88\x8b\x53\x51\x04\x21\x81\x56\
+\xa9\x22\xea\x4b\x1a\xfb\x5e\xee\xbb\x9e\xfb\x4c\x87\xa6\xde\xe0\
+\x0f\x3e\x1e\x9c\x77\xbe\x73\xbe\xfb\xee\x23\x4c\x28\x16\x8b\x0b\
+\x51\x14\xf9\x00\x08\xff\x46\xb3\xf6\x58\x06\x0e\x96\x8c\x03\xad\
+\xb5\xe9\x74\x3a\xaf\x00\x9c\x9b\x5e\xa0\xae\x3f\xf9\x68\x46\x31\
+\x10\xb1\x18\xd3\xed\x76\x49\x4a\x09\x36\xa1\x5e\xaf\xc3\xd2\x6a\
+\xb5\xd0\x68\x34\x6e\x6e\x6e\x6d\xd1\xd5\x95\x95\xfb\x00\x76\x31\
+\x81\xea\x8f\x3f\xc4\x6b\xf7\xce\x78\xa3\x38\xc5\xe5\xb3\x85\x23\
+\x91\x78\x39\x88\x08\xfd\x7e\x1f\xb5\x5a\x0d\x96\x17\x6f\x06\x78\
+\xba\xfe\x19\x4a\x12\x42\x05\x28\x41\xa0\xde\xd7\x03\x0c\x0e\xfe\
+\x0e\xe0\x04\x98\x4e\xd0\x6e\xb7\x51\x2a\x95\x10\x04\x01\xee\xac\
+\xde\x40\xd9\x3f\x85\x62\x28\xf1\xe8\x25\x0f\x02\x81\x86\xb1\xc6\
+\x90\x13\x30\xd6\x74\x2c\x41\xb3\xd9\x3c\x92\x6c\x6f\x10\x61\x94\
+\x68\x10\x01\x0a\xb0\x4f\xc2\x84\xe9\x04\xce\xda\xa1\x47\x09\x12\
+\x94\x0f\xf8\x25\x70\x2c\x01\x1c\x35\xdb\x9f\x63\xb1\x17\xb4\xfc\
+\x60\xc3\xfc\xdc\x4f\x80\x78\x1f\x03\xed\xf1\x26\xe5\xbc\x68\x62\
+\x69\x3d\xc6\x9c\xe4\x7e\x3f\x8f\x52\xde\x83\x22\x21\xc7\x0f\x6f\
+\x5d\x54\x5f\x76\x7a\xb8\xbb\x7a\x01\xdf\x87\xda\xf9\x27\x19\x56\
+\xa5\x20\xf1\x7c\x7d\x1b\xd5\xd3\x4b\x58\x7b\xfd\x7e\xac\x0a\xa1\
+\x4f\xbf\x46\x09\xde\xf5\x87\xb8\xf6\xed\x37\x76\x7f\x68\x08\xc7\
+\x80\x94\xb5\x78\x42\x66\xbd\x57\x2a\x09\xac\x57\x85\x81\x4f\x9e\
+\x12\x3c\x59\x21\xf4\x88\x8b\x12\x44\x8e\x04\x06\xb6\x27\xeb\xb5\
+\x1e\xf6\x66\x03\xb0\x30\xef\x63\xb1\x1c\x60\x3e\x27\xf8\x7e\x69\
+\xe6\x37\x20\x32\xb6\x37\xf3\x58\xaf\x0a\x7d\x45\xb5\x93\x39\xcc\
+\x89\x2a\xde\x6e\xef\x40\x08\x81\x59\xa4\x69\x8a\x4b\xe7\xab\xa8\
+\x54\x72\xb0\x5e\xba\xfd\xac\x67\x12\x73\x18\x9b\xf0\x7f\x98\xec\
+\x38\x1e\xe9\xcc\x51\x66\x2d\xcf\x74\xbb\x2f\xe5\xd3\x1f\x03\xf5\
+\xc1\x56\xb6\x09\x0d\x58\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\
+\x60\x82\
+\x00\x00\x03\x22\
+\x89\
+\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
+\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xff\x61\
+\x00\x00\x00\x06\x62\x4b\x47\x44\x00\xff\x00\xff\x00\xff\xa0\xbd\
+\xa7\x93\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x06\xec\x00\x00\
+\x06\xec\x01\x1e\x75\x38\x35\x00\x00\x00\x07\x74\x49\x4d\x45\x07\
+\xd8\x01\x03\x12\x35\x13\x24\x41\x46\xca\x00\x00\x02\xaf\x49\x44\
+\x41\x54\x38\xcb\x95\x92\x59\x48\x54\x61\x18\x86\xdf\xff\xec\xce\
+\x9c\x99\xd1\x99\xb1\x74\x74\x5c\xa6\x2c\x4b\xa4\xdd\x68\xca\x16\
+\x2c\x83\x0a\x8c\x24\x8d\x16\x23\x91\x08\xea\x22\xc2\xa0\x20\x8a\
+\x0a\x82\xa0\xe8\x22\x22\x28\x93\x2e\x0a\xb2\x81\x88\x92\xd2\x8b\
+\xa0\x05\x53\x88\x2e\x24\xb2\xc5\x2c\x4b\x31\x9d\xc5\xd4\x59\xce\
+\xcc\x59\xfe\xd3\x55\x51\x12\xa1\xdf\xed\xc3\xfb\xf0\xf1\x7d\x2f\
+\x30\x85\x79\x7c\xfb\xfc\xc1\x47\x4d\xc7\xdf\x6e\xa9\x58\xc1\x4c\
+\x66\xcc\x54\x04\x7a\x2a\xd6\xe0\x74\xb9\xba\x5b\x9f\x74\xd2\x69\
+\x0b\x9e\x3d\xbc\x5e\x26\x30\xe6\x42\x03\x7c\xd3\xbf\xf8\x7f\x05\
+\x17\x8f\xed\x75\x06\x87\xbf\x9f\x83\x69\x7e\x6a\x6e\x69\x7f\x3a\
+\x2d\x41\xf5\xfa\xa5\x2c\x2f\x8a\x87\x48\x3c\x5c\xf1\xb9\x7f\xb8\
+\xe7\xc6\x9d\x47\x74\xca\x82\x8b\x47\xb7\xbb\x76\x56\xf9\xbb\xe6\
+\xe4\x65\x9c\x8e\xc6\x53\x60\x23\x51\xf1\x1e\xc0\xfd\xe2\xf7\x08\
+\xc9\x7a\xe0\xf7\xaf\x07\x00\x76\x72\xb8\xa4\x04\xa8\x58\xba\xec\
+\xbe\xc7\x93\x5d\xae\xa8\x3a\x58\x35\x0a\x97\x68\xc9\x11\x83\xb1\
+\x9c\xd5\xa1\x48\x5b\x35\x30\xb3\x60\xed\xca\xbb\xb3\x16\x2d\xd8\
+\x53\xde\xd3\x33\xc0\x4d\x16\xec\xab\xaa\xaf\xb5\xdb\xd2\x2a\x95\
+\x44\x1c\x48\xa5\x40\xa9\x0e\xaa\xe8\xa6\xaf\xbc\x7c\x53\x77\x52\
+\x69\x2a\x74\xbb\x66\xcf\x98\x5b\x92\x67\x1a\x26\x84\x1c\xcf\x66\
+\xf2\x67\xf8\xcc\x81\x8d\xb2\x35\x4d\xec\x9e\x91\x5b\xe0\x63\x08\
+\x80\x78\x04\x0c\xcb\x40\xfa\x18\x1c\xcb\x1d\x50\xa3\x9a\xc4\x43\
+\xe2\x04\xc0\x92\x86\xbe\xae\x8e\x8e\x1f\x5f\x86\xea\xff\xda\x20\
+\xd3\x21\x5d\xb3\xd8\x6c\x3e\x99\xd3\x10\x1e\x1d\x47\x22\xa1\x41\
+\x07\x01\xf3\x2d\x74\x55\x0c\x46\x76\x8b\x43\x51\x28\x02\x8f\x11\
+\x49\x1f\x0b\xf6\x47\xf6\x1f\x06\x94\xdf\x37\xb8\x79\x72\xdb\x51\
+\x30\xec\x91\xcc\x0c\x3b\x26\xc6\xa2\xd0\xd4\x24\x5c\x32\x07\x81\
+\x67\x20\xbf\xfa\x30\xd3\x37\x18\x16\x34\x9e\x10\xdb\x44\x0c\x6e\
+\x40\x0a\xd9\xac\x85\xcb\xe3\xc9\x36\x16\x00\x9a\x4f\x6c\xdd\x91\
+\xe9\x94\xaf\x24\x12\x3a\x03\x6a\xc0\x2a\x71\xd0\x35\x15\x76\xbb\
+\x05\x9a\xaa\x9b\x65\xef\x87\xa8\x91\x6e\x67\x6e\x39\xac\x3d\x23\
+\xe9\x0e\x7e\x4d\x70\x5c\x2a\x10\x85\xa2\x89\xe2\xe2\x02\xe6\x54\
+\xfd\xba\xc5\xb2\x55\x6c\xec\xfd\x1a\xe2\xf2\xb2\x1d\xa6\x95\x63\
+\xa1\xa8\x29\x64\xbb\x6d\x00\x28\x32\x24\x9e\xc6\xf3\x3c\x4c\xbb\
+\xd3\xfe\xae\xac\xb2\xf4\xf8\xa8\x84\x86\xb6\x7c\x6f\x38\xe9\x72\
+\x23\x34\x38\xf8\x86\x9b\x5f\xe4\x9d\x00\x54\x5a\x9a\xef\x31\x19\
+\x83\x42\x92\x58\x83\x81\xc8\x5a\x25\x16\x34\x09\xf0\x92\xf0\xb6\
+\x93\xd3\xbb\xbc\x7e\x5f\x93\xc5\x29\xf7\x6e\xd8\xb5\x2a\xda\x77\
+\xf9\x45\x6d\x6b\x38\xbc\xb8\x71\x7c\xbc\x85\x04\x2e\xd4\x59\xa8\
+\x61\xf8\x89\x89\x6a\x01\xc4\xc7\x89\xc4\x2b\xf0\x4c\x2e\x55\x35\
+\x19\x94\x4d\xa9\x2c\xdb\x6c\x18\xe4\xac\xa6\x93\x70\xe0\x75\x54\
+\x0f\x04\x02\x7f\xbd\x9d\x00\x40\xeb\xa5\x3a\xd6\xa4\x44\x34\x28\
+\xb1\x9b\x84\x7a\x35\x83\xce\x8b\xc7\xe2\xb3\xb2\x1c\xb2\xd2\x1b\
+\x52\x9e\x67\x2d\x21\x2f\x6b\x6a\x02\xff\xac\xfc\x4f\x89\x4f\x0d\
+\x65\xb8\x03\x8d\x9c\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\
+\x82\
+\x00\x00\x01\xf3\
+\x89\
+\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
+\x00\x00\x10\x00\x00\x00\x10\x08\x04\x00\x00\x00\xb5\xfa\x37\xea\
+\x00\x00\x00\x02\x73\x42\x49\x54\x08\x08\x55\xec\x46\x04\x00\x00\
+\x00\x09\x70\x48\x59\x73\x00\x00\x01\xbb\x00\x00\x01\xbb\x01\x3a\
+\xec\xe3\xe2\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\x74\x77\
+\x61\x72\x65\x00\x77\x77\x77\x2e\x69\x6e\x6b\x73\x63\x61\x70\x65\
+\x2e\x6f\x72\x67\x9b\xee\x3c\x1a\x00\x00\x01\x72\x49\x44\x41\x54\
+\x28\xcf\x45\x91\x4f\x2b\x04\x71\x1c\xc6\xbf\xf3\x32\xe4\x6c\xde\
+\x80\xa2\x44\x39\xbb\xec\x1f\xb3\x23\x6d\x9b\x25\x8b\x10\xfd\x76\
+\x36\xec\xa2\x31\x89\x13\x0e\x2e\xca\xfb\x98\x4d\x7b\xf0\x12\xb4\
+\x85\x83\x36\x17\x97\xb1\xa2\xe9\x47\xfd\x0e\x63\xb6\x8f\x83\xb5\
+\x9e\xe7\xf6\xf4\xf4\x1c\x9e\x8f\x20\xbf\x3e\xb1\x8f\x83\x20\xf4\
+\x23\x3f\x0a\xc2\xe3\xe0\xc4\xfe\xcb\x05\x41\x7c\xeb\x50\xd5\x4d\
+\x95\x1a\x3e\x3e\x35\xaa\xd4\xcd\xa1\xf2\xad\x7e\xa1\x61\xd5\x9b\
+\xdb\xec\xd2\xa2\xcd\x13\x1d\x1e\x68\xb1\xc3\x36\xf5\x66\xc3\x42\
+\x04\xf1\xd4\x06\x17\xdc\xf3\x4a\xcc\x27\x5f\x68\xde\x78\xe6\x9c\
+\x0d\x3c\x85\x88\xb2\x37\x8d\xc7\x23\x1f\x18\xbe\xe9\xd1\x23\x25\
+\x41\xf3\x42\x8d\x4d\xa3\x6c\x59\x0f\x16\xb9\xe5\x1d\xc3\x15\x67\
+\x1c\xa0\xd8\x42\x91\xa2\x69\xb3\xc8\xfa\x91\x54\xc2\x15\xee\xd0\
+\xa4\x5c\xd2\xa0\x82\x4b\x8e\x59\x20\xa1\xcb\x1a\x95\x50\xca\x91\
+\x47\x9b\x98\x94\x3d\x96\xc9\x31\xc5\x38\xd3\x40\x4a\xcc\x3e\xe5\
+\x48\x4a\x51\x95\x47\x34\x3d\xb2\x8c\x32\x84\x30\xc4\x28\xd0\x43\
+\x73\x40\x29\x92\x62\x58\xee\x2f\xb8\x4c\x30\xc2\x30\x23\x4c\xf4\
+\x17\x96\x29\x86\x52\x0c\x1c\x6e\xe8\x92\xb0\xc4\x0c\x93\x8c\x31\
+\xc9\x0c\x90\xd0\xc1\xa1\x78\x24\xf3\x76\xc1\xac\xf2\x8c\x26\xe5\
+\x5f\x29\x1a\x8f\x82\x99\xb7\x05\x71\x55\x96\x53\x5e\xd0\x24\xa4\
+\x83\x1f\xae\xc9\xe2\x2a\x44\x90\x39\xcb\x6d\x66\x58\xa3\x4d\x97\
+\x18\x4d\x4c\x87\x2a\x19\xdc\xe6\x9c\xd5\x87\xe5\x5a\x8e\xca\x9b\
+\x0c\x25\x76\xd8\x63\x81\x0c\x79\xe3\x28\xd7\x1a\xd0\x44\x90\x82\
+\xed\x04\xf9\x30\x17\xe5\xa2\x7c\xe8\x04\x85\x01\xee\x1f\x8d\x42\
+\x66\xb3\xf5\x8b\xfe\x99\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\
+\x60\x82\
+\x00\x00\x03\xa3\
+\x89\
+\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
+\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xff\x61\
+\x00\x00\x00\x04\x73\x42\x49\x54\x08\x08\x08\x08\x7c\x08\x64\x88\
+\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x01\xbb\x00\x00\x01\xbb\
+\x01\x3a\xec\xe3\xe2\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\
+\x74\x77\x61\x72\x65\x00\x77\x77\x77\x2e\x69\x6e\x6b\x73\x63\x61\
+\x70\x65\x2e\x6f\x72\x67\x9b\xee\x3c\x1a\x00\x00\x03\x20\x49\x44\
+\x41\x54\x78\xda\x6d\x93\xcd\x6b\x5c\x55\x18\xc6\x9f\xf7\xdc\x73\
+\xee\xd7\x24\x33\x49\x27\xa9\x49\x27\xa1\x4d\x86\xd6\x18\x34\x4e\
+\x28\xad\x8d\x42\x68\xfd\xa0\xd0\xa2\xfe\x01\x45\xb0\xb8\x75\xa3\
+\xe0\xd2\x55\x45\x37\x0a\xba\x13\x37\x82\x3b\x17\x12\x14\x14\x8d\
+\x45\x51\x6a\x21\x15\xa3\x26\x25\xc1\x8f\x36\x89\x69\xd2\x74\xec\
+\x24\x73\x33\x9d\xfb\x71\xce\x3d\xf7\x78\x47\x0c\x66\xd1\x1f\x3c\
+\xbb\xf3\xbc\xe7\xe5\x79\xdf\x97\x8c\x31\xd8\x0f\x3d\x47\x55\x30\
+\x5c\xb0\xba\xac\x27\x47\x2a\x23\x93\xe5\xae\x32\x5b\xd9\x5c\x59\
+\xaa\x6f\xd5\x2f\x43\xe3\x43\xf3\x99\xf9\xf3\xbf\x77\x43\x00\x78\
+\xa7\xc0\x9e\x91\x81\xf0\x4a\x79\xb4\xfc\xc6\xf9\x13\xe7\xdd\x4a\
+\x7f\x05\x83\x7d\x83\xb0\x98\x85\x20\x08\x70\xe3\xf6\x0d\xcc\x5c\
+\x9d\x89\xb7\xd7\xb6\x2f\x21\xc3\x8f\xa5\x4a\xf1\x93\x24\x48\xe2\
+\x4e\x81\x7f\xcd\xcc\xa1\x6f\xa7\x4e\x9d\x9a\x3a\x33\xf9\x94\x60\
+\x36\xc3\xfd\xd0\x52\xe3\xea\xc2\x15\xf5\xeb\xfa\x2f\x74\xf1\xe9\
+\x97\xf8\x7b\x1f\xbf\x0b\x8e\x1c\x62\x78\xb5\x36\xf6\xf0\xd4\x89\
+\xb1\xc7\x44\x10\x06\xa0\x90\xd0\x68\x34\xb0\x71\x7b\x03\x64\x08\
+\xd5\xe1\x2a\xbc\x1e\x0f\x1d\x26\x8e\xd6\xc4\x44\xb5\x86\xcc\x18\
+\x30\x10\x78\xfe\xfb\x83\xfd\x03\x3d\x97\x8e\x1e\x19\x17\x8d\x66\
+\x03\x3a\xd5\x98\xff\x79\x1e\x7f\xac\xfc\x0e\x47\x0b\x78\xb6\x87\
+\x2b\x73\xdf\xa3\xb7\x52\xc6\xc9\xe3\x27\xd1\x5b\xec\x05\x0c\x60\
+\x19\x0b\x8c\x18\x38\x11\x5e\xa8\x94\x86\x9c\x76\xdc\x46\x47\x8b\
+\xd7\x17\xf1\x77\x7d\xd3\x14\x5c\x21\x43\xa9\x2e\x27\x99\xd2\x3e\
+\x13\x67\x9b\x71\xc3\x9e\x5d\x9d\xa5\xe9\xf1\x69\xb8\xc6\x05\x49\
+\x82\x45\x04\xee\xf8\xe2\x8c\xd1\xa0\xed\xe6\x36\x76\x83\x5d\x6c\
+\xdc\x5a\x87\xef\x8b\x68\x37\x4a\x1e\xed\x24\x9e\x77\x38\x09\x0b\
+\xcf\x98\x47\x88\x1e\x38\x7c\x10\x2d\xde\x42\x74\x2f\x42\xb0\x13\
+\x80\x32\x18\x2e\x1c\x56\x8b\xb5\x42\x1a\x04\xa8\xdf\xad\xc3\xd2\
+\x0c\x6d\xa9\xbe\xde\x1b\x57\x97\xe7\xf6\x89\x22\xf3\x44\xaf\x87\
+\xf4\x4e\x82\xb5\xe0\x26\x5a\x77\x5a\x30\xed\x0c\x69\xac\xbf\x24\
+\xff\x82\x90\x59\xdb\x12\xe0\x80\x72\x14\x84\x22\x48\xa9\x67\xf5\
+\x4c\x76\x76\xdf\x6e\xb8\x00\x4a\x02\x16\x81\x8c\xce\x72\x69\x63\
+\x94\xf9\xd4\xb4\x98\x4c\xf4\x35\x84\x29\x62\x11\x43\x97\x35\xcc\
+\x28\xe0\xb8\xd6\x74\x27\x5c\xfc\xcf\x21\x46\xf4\x11\x4d\x50\x9f\
+\xae\x65\x5c\x0c\x5b\xaa\xbb\xe8\xc4\xc8\xe1\xa9\xcc\xbe\x70\x6d\
+\xf1\x38\x0e\xa4\x84\x3e\x20\xeb\xce\x50\x2e\x0a\x87\x96\xd8\x4f\
+\xec\x79\xfa\x1c\x44\xe4\xd8\xfc\x5c\x69\xd8\x9d\x4b\x87\xb2\xd3\
+\x89\xa2\xd5\xb8\x94\x6e\x5a\xcb\x6c\x8b\x2e\xd2\x26\xe1\x59\x3c\
+\xe4\xb9\x62\x1e\x23\x70\xe5\x40\x0a\x5f\xd8\x28\x38\x36\xca\xdc\
+\x87\x6a\x68\x63\x81\xe0\x0d\xf0\x34\xa4\xf4\xaf\x58\xa5\x8b\xa1\
+\x94\xcb\xaa\x95\xad\x45\xbf\xa9\xaf\xa2\x0f\xd4\x2a\xcb\xc3\x5a\
+\x8e\x62\xf5\xba\xbf\x23\x94\xcb\x3b\x73\xe7\x79\x01\x01\xee\x13\
+\x0a\xc7\x6c\xb2\x26\x18\xc9\xfe\x4c\x14\x7c\xbb\xda\xe5\xd8\xe3\
+\x25\xdb\x1f\x4b\x16\xf4\x6b\xf1\x56\x7a\x04\x39\x7b\x3b\xfb\x4e\
+\x63\x2b\xfc\xa6\xe7\xa6\x9b\xf4\x30\x0f\xae\x10\xa0\x02\x61\xf7\
+\x60\x8c\x9d\x43\x11\x9a\x83\x31\x5a\xfd\x09\x8a\xdc\xa9\x46\xd7\
+\xe5\xe9\xb0\x29\x97\x00\x7c\x87\x9c\xfd\xc7\x44\x00\x5e\xee\x3e\
+\xe0\xbe\xe5\x8d\x72\xaf\x32\x56\x64\xf1\xb1\x14\xed\xb2\x84\x75\
+\x8b\xe1\xee\x5c\xdb\xe8\x6b\x59\x18\xde\x53\x6f\x02\x78\x3b\xef\
+\x5c\x22\xe7\x7e\xe7\x7c\x18\xc0\x8b\xb9\x9e\xb0\x18\x1d\x37\x06\
+\x94\x19\xb3\x00\xe0\x87\x5c\xef\xe7\xc6\x75\xec\xe3\x1f\x51\x72\
+\x6b\x1e\x49\x70\xdf\x2c\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\
+\x60\x82\
+\x00\x00\x01\xe5\
+\x89\
+\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
+\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xff\x61\
+\x00\x00\x00\x01\x73\x52\x47\x42\x00\xae\xce\x1c\xe9\x00\x00\x00\
+\x06\x62\x4b\x47\x44\x00\xff\x00\xff\x00\xff\xa0\xbd\xa7\x93\x00\
+\x00\x00\x09\x70\x48\x59\x73\x00\x00\x06\xec\x00\x00\x06\xec\x01\
+\x1e\x75\x38\x35\x00\x00\x00\x07\x74\x49\x4d\x45\x07\xd8\x03\x14\
+\x12\x23\x3b\xba\x62\x67\xa1\x00\x00\x01\x65\x49\x44\x41\x54\x78\
+\xda\x95\x90\xcf\x4a\xc3\x40\x10\xc6\x67\x37\xa6\xa5\x81\x5e\x8a\
+\xa7\x3c\x86\x78\xf1\x01\xd4\x1e\xac\x6f\xe0\xc5\x73\xbd\xd8\x53\
+\x2d\xf8\x08\xbe\x4b\xcf\x3e\x80\xa0\x10\x3c\xfb\x00\x56\xda\x54\
+\x4a\x82\x25\x7f\x77\x3a\xb3\x6c\xe8\x9a\x90\xa2\x1f\x7c\x7c\xbb\
+\xec\xcc\x6f\x77\x56\x00\xc0\x80\x7c\x46\x16\x70\x58\x48\x7e\x21\
+\x7f\x43\x4d\x57\xf8\x37\x31\x60\x04\x35\x1d\x55\x37\xaf\xc2\x10\
+\xa4\x94\x20\x85\x00\x41\x29\x38\xd9\xa6\xb0\xdf\xef\x73\x88\x26\
+\xc0\xc8\x71\x1c\x0d\x88\xa3\x08\x82\x20\x00\x2d\x0b\xd0\xed\x76\
+\x39\x4e\xc8\xa9\x35\xd2\x2b\x2f\x46\x48\xda\x6c\x36\x18\xc5\x31\
+\xce\xe7\x73\xfc\xc7\x48\xc3\xc6\x0b\xf8\x56\x56\xb8\x5e\x83\xc3\
+\x23\x19\x0b\xb2\x3d\x92\xe7\x79\x1c\xb2\x02\xe8\x22\x0d\x31\x00\
+\xdd\x4c\x7b\x9d\x55\x33\x67\xdb\x1f\x48\xab\xd0\xec\xf7\x10\xeb\
+\x53\xa1\x15\x60\x15\xd9\x40\xa7\x06\xa6\x6c\x00\xea\x07\x36\x60\
+\x0f\x3e\x0c\x68\x42\x44\xad\xb9\x28\x0a\x08\xc3\x10\x3a\x1d\x17\
+\x7a\x3d\x0f\x14\x08\xa4\xb2\x82\x01\x8a\x0e\xd1\x75\x5d\x51\xdd\
+\xac\xb3\x76\x2b\x37\xfb\xbe\x0f\xac\xed\xf6\x07\x1e\x67\xb3\x67\
+\x5a\x06\xbc\x3f\x26\xdf\x90\x6f\x8d\x9f\x90\x94\x24\x09\x66\x59\
+\x86\x0c\x2f\xcb\x12\x17\x5f\x0b\x54\x4a\x61\x1c\xc7\x38\xbe\x1b\
+\xbf\x51\xdd\x29\xb4\x68\xc8\x80\x34\x4d\x31\xcf\x73\x06\xe8\xc6\
+\xe5\x6a\xa9\x9b\x27\x93\xfb\x77\xaa\xb9\x26\xcb\x36\xc0\x05\x35\
+\x2a\xb4\x94\x15\x25\x7e\x2e\x57\x38\x7d\x98\xf2\x93\x2f\x7f\x7d\
+\x3e\x34\x35\x20\x9f\x9b\x22\x5b\x68\x66\xfe\x20\x2b\x30\xda\x01\
+\x68\x0b\xe8\x88\x71\x7b\x2b\x7d\x00\x00\x00\x00\x49\x45\x4e\x44\
+\xae\x42\x60\x82\
"
qt_resource_name = "\
@@ -54,16 +530,82 @@ qt_resource_name = "\
\x07\xac\xfa\xc2\
\x00\x74\
\x00\x65\x00\x78\x00\x74\x00\x65\x00\x72\
+\x00\x06\
+\x07\x03\x7d\xc3\
+\x00\x69\
+\x00\x6d\x00\x61\x00\x67\x00\x65\x00\x73\
\x00\x08\
\x0a\x61\x5a\xa7\
\x00\x69\
\x00\x63\x00\x6f\x00\x6e\x00\x2e\x00\x70\x00\x6e\x00\x67\
+\x00\x15\
+\x0f\x21\xb3\x87\
+\x00\x67\
+\x00\x6f\x00\x2d\x00\x6e\x00\x65\x00\x78\x00\x74\x00\x2d\x00\x76\x00\x69\x00\x65\x00\x77\x00\x2d\x00\x70\x00\x61\x00\x67\x00\x65\
+\x00\x2e\x00\x70\x00\x6e\x00\x67\
+\x00\x19\
+\x0d\x16\x46\x27\
+\x00\x67\
+\x00\x6f\x00\x2d\x00\x70\x00\x72\x00\x65\x00\x76\x00\x69\x00\x6f\x00\x75\x00\x73\x00\x2d\x00\x76\x00\x69\x00\x65\x00\x77\x00\x2d\
+\x00\x70\x00\x61\x00\x67\x00\x65\x00\x2e\x00\x70\x00\x6e\x00\x67\
+\x00\x10\
+\x0c\xbc\x2e\x67\
+\x00\x64\
+\x00\x6f\x00\x63\x00\x75\x00\x6d\x00\x65\x00\x6e\x00\x74\x00\x2d\x00\x6e\x00\x65\x00\x77\x00\x2e\x00\x70\x00\x6e\x00\x67\
+\x00\x10\
+\x08\x12\xae\xa7\
+\x00\x6d\
+\x00\x65\x00\x64\x00\x69\x00\x61\x00\x2d\x00\x72\x00\x65\x00\x63\x00\x6f\x00\x72\x00\x64\x00\x2e\x00\x70\x00\x6e\x00\x67\
+\x00\x18\
+\x00\x8c\x38\x27\
+\x00\x64\
+\x00\x6f\x00\x63\x00\x75\x00\x6d\x00\x65\x00\x6e\x00\x74\x00\x2d\x00\x6f\x00\x70\x00\x65\x00\x6e\x00\x2d\x00\x72\x00\x65\x00\x63\
+\x00\x65\x00\x6e\x00\x74\x00\x2e\x00\x70\x00\x6e\x00\x67\
+\x00\x11\
+\x0f\xe3\xd5\x67\
+\x00\x64\
+\x00\x6f\x00\x63\x00\x75\x00\x6d\x00\x65\x00\x6e\x00\x74\x00\x2d\x00\x73\x00\x61\x00\x76\x00\x65\x00\x2e\x00\x70\x00\x6e\x00\x67\
+\
+\x00\x16\
+\x04\x98\x82\x67\
+\x00\x64\
+\x00\x6f\x00\x63\x00\x75\x00\x6d\x00\x65\x00\x6e\x00\x74\x00\x2d\x00\x6f\x00\x70\x00\x65\x00\x6e\x00\x2d\x00\x64\x00\x61\x00\x74\
+\x00\x61\x00\x2e\x00\x70\x00\x6e\x00\x67\
+\x00\x0e\
+\x0d\x8b\x39\xe7\
+\x00\x65\
+\x00\x64\x00\x69\x00\x74\x00\x2d\x00\x63\x00\x6c\x00\x65\x00\x61\x00\x72\x00\x2e\x00\x70\x00\x6e\x00\x67\
+\x00\x17\
+\x09\x10\x6a\x47\
+\x00\x6d\
+\x00\x65\x00\x64\x00\x69\x00\x61\x00\x2d\x00\x70\x00\x6c\x00\x61\x00\x79\x00\x62\x00\x61\x00\x63\x00\x6b\x00\x2d\x00\x73\x00\x74\
+\x00\x6f\x00\x70\x00\x2e\x00\x70\x00\x6e\x00\x67\
+\x00\x10\
+\x08\x15\x13\x67\
+\x00\x76\
+\x00\x69\x00\x65\x00\x77\x00\x2d\x00\x72\x00\x65\x00\x66\x00\x72\x00\x65\x00\x73\x00\x68\x00\x2e\x00\x70\x00\x6e\x00\x67\
+\x00\x0d\
+\x01\x1c\xb1\xa7\
+\x00\x65\
+\x00\x64\x00\x69\x00\x74\x00\x2d\x00\x63\x00\x6f\x00\x70\x00\x79\x00\x2e\x00\x70\x00\x6e\x00\x67\
"
qt_resource_struct = "\
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\
-\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x02\
-\x00\x00\x00\x12\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
+\x00\x00\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x02\
+\x00\x00\x00\x12\x00\x02\x00\x00\x00\x0b\x00\x00\x00\x04\
+\x00\x00\x00\x24\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
+\x00\x00\x00\xee\x00\x00\x00\x00\x00\x01\x00\x00\x0c\x4b\
+\x00\x00\x01\xfa\x00\x00\x00\x00\x00\x01\x00\x00\x1c\x6a\
+\x00\x00\x01\x4c\x00\x00\x00\x00\x00\x01\x00\x00\x11\x3f\
+\x00\x00\x00\xc8\x00\x00\x00\x00\x00\x01\x00\x00\x09\x68\
+\x00\x00\x01\xd4\x00\x00\x00\x00\x00\x01\x00\x00\x18\xc3\
+\x00\x00\x01\xa0\x00\x00\x00\x00\x00\x01\x00\x00\x16\xcc\
+\x00\x00\x00\xa2\x00\x00\x00\x00\x00\x01\x00\x00\x07\x21\
+\x00\x00\x00\x6a\x00\x00\x00\x00\x00\x01\x00\x00\x04\xb3\
+\x00\x00\x01\x7e\x00\x00\x00\x00\x00\x01\x00\x00\x13\xa6\
+\x00\x00\x00\x3a\x00\x00\x00\x00\x00\x01\x00\x00\x02\x30\
+\x00\x00\x01\x24\x00\x00\x00\x00\x00\x01\x00\x00\x0f\x08\
"
def qInitResources():
diff --git a/texter/texter/texter_ui.py b/texter/texter/texter_ui.py
index cb7f74b..cdc27fc 100644
--- a/texter/texter/texter_ui.py
+++ b/texter/texter/texter_ui.py
@@ -1,8 +1,8 @@
# -*- coding: utf-8 -*-
-# Form implementation generated from reading ui file 'texter3.ui'
+# Form implementation generated from reading ui file 'texter.ui'
#
-# Created: Mon Apr 21 22:38:51 2014
+# Created: Sat May 10 18:57:19 2014
# by: PyQt4 UI code generator 4.10.3
#
# WARNING! All changes made in this file will be lost!
@@ -26,7 +26,7 @@ except AttributeError:
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName(_fromUtf8("MainWindow"))
- MainWindow.resize(1475, 651)
+ MainWindow.resize(1475, 592)
palette = QtGui.QPalette()
brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
brush.setStyle(QtCore.Qt.SolidPattern)
@@ -47,6 +47,11 @@ class Ui_MainWindow(object):
brush.setStyle(QtCore.Qt.SolidPattern)
palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Base, brush)
MainWindow.setPalette(palette)
+ font = QtGui.QFont()
+ font.setFamily(_fromUtf8("Helvetica"))
+ font.setPointSize(8)
+ font.setBold(True)
+ MainWindow.setFont(font)
icon = QtGui.QIcon()
icon.addPixmap(QtGui.QPixmap(_fromUtf8(":/texter/icon.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
MainWindow.setWindowIcon(icon)
@@ -57,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)
@@ -102,7 +107,7 @@ class Ui_MainWindow(object):
self.live_text.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.live_text.setAcceptRichText(True)
self.live_text.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByKeyboard|QtCore.Qt.LinksAccessibleByMouse|QtCore.Qt.TextBrowserInteraction|QtCore.Qt.TextEditable|QtCore.Qt.TextEditorInteraction|QtCore.Qt.TextSelectableByKeyboard|QtCore.Qt.TextSelectableByMouse)
- self.live_text.setRichTextSupport(KRichTextWidget.RichTextSupportValues(KRichTextWidget.SupportAlignment|KRichTextWidget.SupportChangeListStyle|KRichTextWidget.SupportFontFamily|KRichTextWidget.SupportFontSize|KRichTextWidget.SupportTextForegroundColor))
+ self.live_text.setRichTextSupport(KRichTextWidget.RichTextSupportValues(KRichTextWidget.SupportAlignment|KRichTextWidget.SupportFontFamily|KRichTextWidget.SupportFontSize|KRichTextWidget.SupportTextForegroundColor))
self.live_text.setObjectName(_fromUtf8("live_text"))
self.horizontalLayout.addWidget(self.live_text)
self.preview_text = KRichTextWidget(self.centralwidget)
@@ -111,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(769, 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)
@@ -140,11 +145,7 @@ class Ui_MainWindow(object):
self.preview_text.setRichTextSupport(KRichTextWidget.RichTextSupportValues(KRichTextWidget.SupportAlignment|KRichTextWidget.SupportChangeListStyle|KRichTextWidget.SupportFontFamily|KRichTextWidget.SupportFontSize|KRichTextWidget.SupportTextForegroundColor))
self.preview_text.setObjectName(_fromUtf8("preview_text"))
self.horizontalLayout.addWidget(self.preview_text)
- spacerItem = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.MinimumExpanding, QtGui.QSizePolicy.Minimum)
- self.horizontalLayout.addItem(spacerItem)
self.verticalLayout.addLayout(self.horizontalLayout)
- spacerItem1 = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
- self.verticalLayout.addItem(spacerItem1)
MainWindow.setCentralWidget(self.centralwidget)
self.retranslateUi(MainWindow)