changed from ffmpeg grabbing to internal grabbing and added a async http server
This commit is contained in:
parent
28cc9798e7
commit
d7f3a7daea
|
@ -1,38 +1,49 @@
|
||||||
#!/usr/bin/python
|
#!/usr/bin/python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- 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 <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
# Copyright (C) 2014 Stefan Kögl
|
||||||
|
|
||||||
|
from __future__ import absolute_import
|
||||||
|
|
||||||
|
|
||||||
import cPickle
|
import cPickle
|
||||||
import os.path
|
import os.path
|
||||||
import re
|
import re
|
||||||
import subprocess
|
|
||||||
import sys
|
|
||||||
from math import pow
|
|
||||||
|
|
||||||
from operator import itemgetter
|
|
||||||
|
|
||||||
from PyQt4 import QtCore, QtGui
|
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,
|
||||||
from PyKDE4.kdeui import KDialog, KActionCollection, KRichTextWidget, KComboBox, KPushButton, KRichTextWidget, KMainWindow, KToolBar, KApplication, KAction, KToolBarSpacerAction, KSelectAction, KToggleAction, KShortcut
|
KRichTextWidget, KMainWindow, KToolBar, KAction, KToolBarSpacerAction,
|
||||||
|
KSelectAction, KToggleAction, KShortcut)
|
||||||
|
|
||||||
from texter_ui import Ui_MainWindow, _fromUtf8
|
from PyQt4.QtNetwork import QTcpServer, QTcpSocket
|
||||||
from text_sorter_ui import Ui_TextSorterDialog
|
|
||||||
from text_model import TextModel
|
|
||||||
|
|
||||||
appName = "texter"
|
from chaosc.argparser_groups import ArgParser
|
||||||
catalog = "448texter"
|
from chaosc.lib import resolve_host
|
||||||
programName = ki18n("4.48 Psychose Texter")
|
|
||||||
version = "0.1"
|
|
||||||
|
|
||||||
aboutData = KAboutData(appName, catalog, programName, version)
|
from texter.texter_ui import Ui_MainWindow, _fromUtf8
|
||||||
|
from texter.text_sorter_ui import Ui_TextSorterDialog
|
||||||
|
from texter.text_model import TextModel
|
||||||
|
|
||||||
KCmdLineArgs.init (sys.argv, aboutData)
|
app = QtGui.QApplication([])
|
||||||
|
|
||||||
app = KApplication()
|
|
||||||
|
|
||||||
for path in QtGui.QIcon.themeSearchPaths():
|
|
||||||
print "%s/%s" % (path, QtGui.QIcon.themeName())
|
|
||||||
|
|
||||||
|
|
||||||
# NOTE: if the QIcon.fromTheme method does not find any icons, you can use
|
# NOTE: if the QIcon.fromTheme method does not find any icons, you can use
|
||||||
|
@ -40,6 +51,140 @@ for path in QtGui.QIcon.themeSearchPaths():
|
||||||
# in your local icon directory:
|
# in your local icon directory:
|
||||||
# ln -s /your/icon/theme/directory $HOME/.icons/hicolor
|
# ln -s /your/icon/theme/directory $HOME/.icons/hicolor
|
||||||
|
|
||||||
|
def get_preview_text(text):
|
||||||
|
return re.sub(" +", " ", text.replace("\n", " ")).strip()[:20]
|
||||||
|
|
||||||
|
class MjpegStreamingServer(QTcpServer):
|
||||||
|
|
||||||
|
def __init__(self, server_address, parent=None):
|
||||||
|
super(MjpegStreamingServer, self).__init__(parent)
|
||||||
|
self.server_address = server_address
|
||||||
|
self.newConnection.connect(self.start_streaming)
|
||||||
|
self.widget = parent
|
||||||
|
self.sockets = list()
|
||||||
|
self.img_data = None
|
||||||
|
self.timer = QtCore.QTimer()
|
||||||
|
self.timer.timeout.connect(self.render_image)
|
||||||
|
self.timer.start(80)
|
||||||
|
self.stream_clients = list()
|
||||||
|
self.regex = re.compile("^GET /(\w+?)\.(\w+?) HTTP/(\d+\.\d+)$")
|
||||||
|
self.html_map = dict()
|
||||||
|
self.coords = parent.live_text_rect()
|
||||||
|
|
||||||
|
def handle_request(self):
|
||||||
|
sock = self.sender()
|
||||||
|
sock_id = id(sock)
|
||||||
|
print "handle_request", sock
|
||||||
|
if sock.state() in (QTcpSocket.UnconnectedState, QTcpSocket.ClosingState):
|
||||||
|
print "connection closed"
|
||||||
|
self.sockets.remove(sock)
|
||||||
|
sock.deleteLater()
|
||||||
|
return
|
||||||
|
|
||||||
|
client_data = str(sock.readAll())
|
||||||
|
print "request", repr(client_data)
|
||||||
|
line = client_data.split("\r\n")[0]
|
||||||
|
print "first line", repr(line)
|
||||||
|
try:
|
||||||
|
resource, ext, http_version = self.regex.match(line).groups()
|
||||||
|
print "resource, ext, http_version", resource, ext, http_version
|
||||||
|
except AttributeError:
|
||||||
|
print "regex not matched"
|
||||||
|
sock.write("HTTP/1.1 404 Not Found\r\n")
|
||||||
|
else:
|
||||||
|
if ext == "ico":
|
||||||
|
directory = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
data = open(os.path.join(directory, "favicon.ico"), "rb").read()
|
||||||
|
sock.write(QByteArray('HTTP/1.1 200 Ok\r\nContent-Type: image/x-ico\r\n\r\n%s' % data))
|
||||||
|
if ext == "html":
|
||||||
|
directory = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
data = open(os.path.join(directory, "index.html"), "rb").read() % sock_id
|
||||||
|
self.html_map[sock_id] = None
|
||||||
|
sock.write(QByteArray('HTTP/1.1 200 Ok\r\nContent-Type: text/html;encoding: utf-8\r\n\r\n%s' % data))
|
||||||
|
#sock.close()
|
||||||
|
elif ext == "mjpeg":
|
||||||
|
try:
|
||||||
|
_, html_sock_id = resource.split("_", 1)
|
||||||
|
html_sock_id = int(html_sock_id)
|
||||||
|
except ValueError:
|
||||||
|
html_sock_id = None
|
||||||
|
|
||||||
|
if sock not in self.stream_clients:
|
||||||
|
print "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:
|
||||||
|
print "not found/handled"
|
||||||
|
sock.write("HTTP/1.1 404 Not Found\r\n")
|
||||||
|
self.sockets.remove(sock)
|
||||||
|
sock.close()
|
||||||
|
|
||||||
|
def remove_stream_client(self):
|
||||||
|
sock = self.sender()
|
||||||
|
sock_id = id(sock)
|
||||||
|
print "remove_stream_client", sock, sock_id
|
||||||
|
if sock.state() == QTcpSocket.UnconnectedState:
|
||||||
|
self.sockets.remove(sock)
|
||||||
|
print "removed sock", sock
|
||||||
|
sock.close()
|
||||||
|
try:
|
||||||
|
self.stream_clients.remove(sock)
|
||||||
|
except ValueError, error:
|
||||||
|
print "sock was not in stream_clients", error
|
||||||
|
|
||||||
|
try:
|
||||||
|
stream_client = self.html_map.pop(sock_id)
|
||||||
|
except KeyError, error:
|
||||||
|
print "socket has no child socket"
|
||||||
|
else:
|
||||||
|
print "html socket has linked stream socket to remove", stream_client, id(stream_client)
|
||||||
|
stream_client.close()
|
||||||
|
try:
|
||||||
|
self.stream_clients.remove(stream_client)
|
||||||
|
except ValueError, error:
|
||||||
|
print "error", error
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.sockets.remove(stream_client)
|
||||||
|
except ValueError, error:
|
||||||
|
print "error", error
|
||||||
|
|
||||||
|
def render_image(self):
|
||||||
|
if not self.stream_clients:
|
||||||
|
return
|
||||||
|
|
||||||
|
pixmap = QPixmap.grabWidget(self.widget.live_text, *self.coords)
|
||||||
|
buf = QBuffer()
|
||||||
|
buf.open(QIODevice.WriteOnly)
|
||||||
|
pixmap.save(buf, "JPG", 25)
|
||||||
|
self.img_data = buf.data()
|
||||||
|
len_data = len(self.img_data)
|
||||||
|
array = QByteArray("--2342\r\nContent-Type: image/jpeg\r\nContent-length: %d\r\n\r\n%s\r\n\r\n\r\n" % (len_data, self.img_data))
|
||||||
|
for sock in self.stream_clients:
|
||||||
|
sock.write(array)
|
||||||
|
|
||||||
|
def start_streaming(self):
|
||||||
|
while self.hasPendingConnections():
|
||||||
|
sock = self.nextPendingConnection()
|
||||||
|
sock.readyRead.connect(self.handle_request)
|
||||||
|
sock.disconnected.connect(self.remove_stream_client)
|
||||||
|
self.sockets.append(sock)
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
for sock in self.sockets:
|
||||||
|
sock.close()
|
||||||
|
sock.deleteLater()
|
||||||
|
for sock in self.stream_clients:
|
||||||
|
sock.close()
|
||||||
|
sock.deleteLater()
|
||||||
|
self.stream_clients = list()
|
||||||
|
self.sockets = list()
|
||||||
|
self.html_map = dict()
|
||||||
|
self.close()
|
||||||
|
|
||||||
|
|
||||||
class TextSorterDialog(QtGui.QWidget, Ui_TextSorterDialog):
|
class TextSorterDialog(QtGui.QWidget, Ui_TextSorterDialog):
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
super(TextSorterDialog, self).__init__(parent)
|
super(TextSorterDialog, self).__init__(parent)
|
||||||
|
@ -48,13 +193,13 @@ class TextSorterDialog(QtGui.QWidget, Ui_TextSorterDialog):
|
||||||
self.fill_list()
|
self.fill_list()
|
||||||
|
|
||||||
self.text_list.clicked.connect(self.slot_show_text)
|
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_up_button.clicked.connect(self.slot_text_up)
|
||||||
self.move_down_button.clicked.connect(self.slot_text_down)
|
self.move_down_button.clicked.connect(self.slot_text_down)
|
||||||
self.text_list.clicked.connect(self.slot_toggle_buttons)
|
self.text_list.clicked.connect(self.slot_toggle_buttons)
|
||||||
self.move_up_button.setEnabled(False)
|
self.move_up_button.setEnabled(False)
|
||||||
self.move_down_button.setEnabled(False)
|
self.move_down_button.setEnabled(False)
|
||||||
|
self.model = None
|
||||||
|
|
||||||
def slot_toggle_buttons(self, index):
|
def slot_toggle_buttons(self, index):
|
||||||
row = index.row()
|
row = index.row()
|
||||||
|
@ -71,9 +216,9 @@ class TextSorterDialog(QtGui.QWidget, Ui_TextSorterDialog):
|
||||||
def fill_list(self):
|
def fill_list(self):
|
||||||
self.model = self.parent().parent().model
|
self.model = self.parent().parent().model
|
||||||
self.text_list.setModel(self.model)
|
self.text_list.setModel(self.model)
|
||||||
ix = self.parent().parent().current_index
|
index = self.parent().parent().current_index
|
||||||
index = self.model.index(ix, 0)
|
model_index = self.model.index(index, 0)
|
||||||
self.text_list.setCurrentIndex(index)
|
self.text_list.setCurrentIndex(model_index)
|
||||||
|
|
||||||
|
|
||||||
def slot_text_up(self):
|
def slot_text_up(self):
|
||||||
|
@ -107,8 +252,7 @@ class TextSorterDialog(QtGui.QWidget, Ui_TextSorterDialog):
|
||||||
except IndexError:
|
except IndexError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def slot_remove_item(self):
|
||||||
def slot_removeItem(self):
|
|
||||||
index = self.text_list.currentIndex().row()
|
index = self.text_list.currentIndex().row()
|
||||||
self.model.removeRows(index, 1)
|
self.model.removeRows(index, 1)
|
||||||
index = self.model.index(0, 0)
|
index = self.model.index(0, 0)
|
||||||
|
@ -128,7 +272,6 @@ class FadeAnimation(QtCore.QObject):
|
||||||
self.current_alpha = 255
|
self.current_alpha = 255
|
||||||
self.timer = None
|
self.timer = None
|
||||||
|
|
||||||
|
|
||||||
def start_animation(self):
|
def start_animation(self):
|
||||||
print "start_animation"
|
print "start_animation"
|
||||||
self.animation_started.emit()
|
self.animation_started.emit()
|
||||||
|
@ -141,7 +284,6 @@ class FadeAnimation(QtCore.QObject):
|
||||||
self.timer.timeout.connect(self.slot_animate)
|
self.timer.timeout.connect(self.slot_animate)
|
||||||
self.timer.start(100)
|
self.timer.start(100)
|
||||||
|
|
||||||
|
|
||||||
def slot_animate(self):
|
def slot_animate(self):
|
||||||
print "slot_animate"
|
print "slot_animate"
|
||||||
print "current_alpha", self.current_alpha
|
print "current_alpha", self.current_alpha
|
||||||
|
@ -214,7 +356,6 @@ class TextAnimation(QtCore.QObject):
|
||||||
|
|
||||||
def slot_animate(self):
|
def slot_animate(self):
|
||||||
self.animation_started.emit()
|
self.animation_started.emit()
|
||||||
parent = self.parent()
|
|
||||||
|
|
||||||
if self.it is None:
|
if self.it is None:
|
||||||
src_root_frame = self.src_document.rootFrame()
|
src_root_frame = self.src_document.rootFrame()
|
||||||
|
@ -272,12 +413,11 @@ class TextAnimation(QtCore.QObject):
|
||||||
|
|
||||||
|
|
||||||
class MainWindow(KMainWindow, Ui_MainWindow):
|
class MainWindow(KMainWindow, Ui_MainWindow):
|
||||||
def __init__(self, parent=None):
|
def __init__(self, args, parent=None):
|
||||||
super(MainWindow, self).__init__(parent)
|
super(MainWindow, self).__init__(parent)
|
||||||
|
self.args = args
|
||||||
self.is_streaming = False
|
self.is_streaming = False
|
||||||
self.ffserver = None
|
self.http_server = MjpegStreamingServer((args.http_host, args.http_port), self)
|
||||||
self.ffmpeg = None
|
|
||||||
self.live_center_action = None
|
self.live_center_action = None
|
||||||
self.preview_center_action = None
|
self.preview_center_action = None
|
||||||
self.live_size_action = None
|
self.live_size_action = None
|
||||||
|
@ -307,8 +447,22 @@ class MainWindow(KMainWindow, Ui_MainWindow):
|
||||||
self.font = QtGui.QFont("monospace", self.default_size)
|
self.font = QtGui.QFont("monospace", self.default_size)
|
||||||
self.font.setStyleHint(QtGui.QFont.TypeWriter)
|
self.font.setStyleHint(QtGui.QFont.TypeWriter)
|
||||||
|
|
||||||
|
self.previous_action = None
|
||||||
self.create_toolbar()
|
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.preview_text.document().setDefaultFont(self.font)
|
#self.preview_text.document().setDefaultFont(self.font)
|
||||||
self.preview_text.setFont(self.font)
|
self.preview_text.setFont(self.font)
|
||||||
|
@ -322,48 +476,28 @@ class MainWindow(KMainWindow, Ui_MainWindow):
|
||||||
self.live_editor_collection = KActionCollection(self)
|
self.live_editor_collection = KActionCollection(self)
|
||||||
self.live_text.createActions(self.live_editor_collection)
|
self.live_text.createActions(self.live_editor_collection)
|
||||||
self.filter_editor_actions()
|
self.filter_editor_actions()
|
||||||
|
self.create_toolbar()
|
||||||
self.slot_load()
|
self.slot_load()
|
||||||
|
|
||||||
|
|
||||||
|
app.focusChanged.connect(self.focusChanged)
|
||||||
|
self.start_streaming()
|
||||||
|
self.get_live_coords()
|
||||||
|
|
||||||
self.show()
|
self.show()
|
||||||
|
|
||||||
self.save_action.triggered.connect(self.slot_save)
|
def get_live_coords(self):
|
||||||
|
|
||||||
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()
|
public_rect = self.live_text.geometry()
|
||||||
global_rect = QtCore.QRect(self.mapToGlobal(public_rect.topLeft()), self.mapToGlobal(public_rect.bottomRight()))
|
global_rect = QtCore.QRect(self.mapToGlobal(public_rect.topLeft()), self.mapToGlobal(public_rect.bottomRight()))
|
||||||
x = global_rect.x()
|
x = global_rect.x()
|
||||||
y = global_rect.y()
|
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()))
|
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):
|
def get_preview_coords(self):
|
||||||
public_rect = self.preview_text.geometry()
|
public_rect = self.preview_text.geometry()
|
||||||
global_rect = QtCore.QRect(self.mapToGlobal(public_rect.topLeft()), self.mapToGlobal(public_rect.bottomRight()))
|
global_rect = QtCore.QRect(self.mapToGlobal(public_rect.topLeft()), self.mapToGlobal(public_rect.bottomRight()))
|
||||||
return global_rect.x(), global_rect.y()
|
return global_rect.x(), global_rect.y()
|
||||||
|
|
||||||
|
|
||||||
def filter_editor_actions(self):
|
def filter_editor_actions(self):
|
||||||
|
|
||||||
disabled_action_names = [
|
disabled_action_names = [
|
||||||
|
@ -391,7 +525,7 @@ class MainWindow(KMainWindow, Ui_MainWindow):
|
||||||
|
|
||||||
for action in self.live_editor_collection.actions():
|
for action in self.live_editor_collection.actions():
|
||||||
text = str(action.objectName())
|
text = str(action.objectName())
|
||||||
print "text", text
|
#print "text", text
|
||||||
if text in disabled_action_names:
|
if text in disabled_action_names:
|
||||||
action.setVisible(False)
|
action.setVisible(False)
|
||||||
|
|
||||||
|
@ -417,7 +551,6 @@ class MainWindow(KMainWindow, Ui_MainWindow):
|
||||||
self.slot_set_preview_defaults()
|
self.slot_set_preview_defaults()
|
||||||
self.slot_set_live_defaults()
|
self.slot_set_live_defaults()
|
||||||
|
|
||||||
|
|
||||||
def create_toolbar(self):
|
def create_toolbar(self):
|
||||||
|
|
||||||
self.toolbar = KToolBar(self, True, True)
|
self.toolbar = KToolBar(self, True, True)
|
||||||
|
@ -534,9 +667,27 @@ class MainWindow(KMainWindow, Ui_MainWindow):
|
||||||
|
|
||||||
self.toolbar.addSeparator()
|
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.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.streaming_action.setChecked(True)
|
||||||
|
|
||||||
def closeEvent(self, event):
|
def closeEvent(self, event):
|
||||||
self.stop_streaming()
|
|
||||||
if self.db_dirty:
|
if self.db_dirty:
|
||||||
self.dialog = KDialog(self)
|
self.dialog = KDialog(self)
|
||||||
self.dialog.setCaption("4.48 texter - text db not saved")
|
self.dialog.setCaption("4.48 texter - text db not saved")
|
||||||
|
@ -546,20 +697,15 @@ class MainWindow(KMainWindow, Ui_MainWindow):
|
||||||
self.dialog.okClicked.connect(self.slot_save)
|
self.dialog.okClicked.connect(self.slot_save)
|
||||||
self.dialog.exec_()
|
self.dialog.exec_()
|
||||||
|
|
||||||
|
def live_text_rect(self):
|
||||||
|
return 3, 3, 768, 576
|
||||||
|
|
||||||
def stop_streaming(self):
|
def stop_streaming(self):
|
||||||
self.is_streaming = False
|
self.is_streaming = False
|
||||||
if self.ffmpeg is not None:
|
self.http_server.stop()
|
||||||
self.ffmpeg.kill()
|
|
||||||
self.ffmpeg = None
|
|
||||||
if self.ffserver is not None:
|
|
||||||
self.ffserver.kill()
|
|
||||||
self.ffserver = None
|
|
||||||
|
|
||||||
def start_streaming(self):
|
def start_streaming(self):
|
||||||
public_rect = self.live_text.geometry()
|
self.http_server.listen(port=9009)
|
||||||
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.is_streaming = True
|
self.is_streaming = True
|
||||||
|
|
||||||
def focusChanged(self, old, new):
|
def focusChanged(self, old, new):
|
||||||
|
@ -572,16 +718,11 @@ class MainWindow(KMainWindow, Ui_MainWindow):
|
||||||
|
|
||||||
def custom_clear(self, cursor):
|
def custom_clear(self, cursor):
|
||||||
cursor.beginEditBlock()
|
cursor.beginEditBlock()
|
||||||
cursor.movePosition(QtGui.QTextCursor.Start);
|
cursor.movePosition(QtGui.QTextCursor.Start)
|
||||||
cursor.movePosition(QtGui.QTextCursor.End, QtGui.QTextCursor.KeepAnchor);
|
cursor.movePosition(QtGui.QTextCursor.End, QtGui.QTextCursor.KeepAnchor)
|
||||||
cursor.removeSelectedText()
|
cursor.removeSelectedText()
|
||||||
cursor.endEditBlock()
|
cursor.endEditBlock()
|
||||||
|
|
||||||
|
|
||||||
def get_preview_text(self, text):
|
|
||||||
return re.sub(" +", " ", text.replace("\n", " ")).strip()[:20]
|
|
||||||
|
|
||||||
|
|
||||||
def slot_auto_publish(self, state):
|
def slot_auto_publish(self, state):
|
||||||
self.is_auto_publish = bool(state)
|
self.is_auto_publish = bool(state)
|
||||||
|
|
||||||
|
@ -589,11 +730,10 @@ class MainWindow(KMainWindow, Ui_MainWindow):
|
||||||
self.is_animate = bool(state)
|
self.is_animate = bool(state)
|
||||||
|
|
||||||
def slot_toggle_streaming(self):
|
def slot_toggle_streaming(self):
|
||||||
if self.ffserver is None:
|
if self.is_streaming:
|
||||||
self.start_streaming()
|
|
||||||
else:
|
|
||||||
self.stop_streaming()
|
self.stop_streaming()
|
||||||
|
else:
|
||||||
|
self.start_streaming()
|
||||||
|
|
||||||
def slot_next_item(self):
|
def slot_next_item(self):
|
||||||
try:
|
try:
|
||||||
|
@ -603,7 +743,6 @@ class MainWindow(KMainWindow, Ui_MainWindow):
|
||||||
except ZeroDivisionError:
|
except ZeroDivisionError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def slot_previous_item(self):
|
def slot_previous_item(self):
|
||||||
try:
|
try:
|
||||||
self.current = (self.text_combo.currentItem() - 1) % len(self.model.text_db)
|
self.current = (self.text_combo.currentItem() - 1) % len(self.model.text_db)
|
||||||
|
@ -612,26 +751,22 @@ class MainWindow(KMainWindow, Ui_MainWindow):
|
||||||
except ZeroDivisionError:
|
except ZeroDivisionError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def slot_publish(self):
|
def slot_publish(self):
|
||||||
if self.is_animate:
|
if self.is_animate:
|
||||||
self.animation.start_animation(self.preview_text, self.live_text, 0)
|
self.animation.start_animation(self.preview_text, self.live_text, 0)
|
||||||
else:
|
else:
|
||||||
self.live_text.setTextOrHtml(self.preview_text.textOrHtml())
|
self.live_text.setTextOrHtml(self.preview_text.textOrHtml())
|
||||||
|
|
||||||
|
|
||||||
def slot_live_font_size(self, action):
|
def slot_live_font_size(self, action):
|
||||||
self.default_size = self.live_size_action.fontSize()
|
self.default_size = self.live_size_action.fontSize()
|
||||||
self.slot_set_preview_defaults()
|
self.slot_set_preview_defaults()
|
||||||
self.slot_set_live_defaults()
|
self.slot_set_live_defaults()
|
||||||
|
|
||||||
|
|
||||||
def slot_preview_font_size(self, action):
|
def slot_preview_font_size(self, action):
|
||||||
self.default_size = self.preview_size_action.fontSize()
|
self.default_size = self.preview_size_action.fontSize()
|
||||||
self.slot_set_live_defaults()
|
self.slot_set_live_defaults()
|
||||||
self.slot_set_preview_defaults()
|
self.slot_set_preview_defaults()
|
||||||
|
|
||||||
|
|
||||||
def slot_toggle_publish(self, state=None):
|
def slot_toggle_publish(self, state=None):
|
||||||
|
|
||||||
if state:
|
if state:
|
||||||
|
@ -639,7 +774,6 @@ class MainWindow(KMainWindow, Ui_MainWindow):
|
||||||
else:
|
else:
|
||||||
self.slot_clear_live()
|
self.slot_clear_live()
|
||||||
|
|
||||||
|
|
||||||
def slot_set_preview_defaults(self):
|
def slot_set_preview_defaults(self):
|
||||||
self.preview_center_action.setChecked(True)
|
self.preview_center_action.setChecked(True)
|
||||||
self.preview_text.alignCenter()
|
self.preview_text.alignCenter()
|
||||||
|
@ -675,11 +809,11 @@ class MainWindow(KMainWindow, Ui_MainWindow):
|
||||||
|
|
||||||
self.text_combo.clear()
|
self.text_combo.clear()
|
||||||
current_row = -1
|
current_row = -1
|
||||||
for ix, list_obj in enumerate(self.model.text_db):
|
for index, list_obj in enumerate(self.model.text_db):
|
||||||
preview, text = list_obj
|
preview, text = list_obj
|
||||||
self.text_combo.addAction(preview)
|
self.text_combo.addAction(preview)
|
||||||
if list_obj == self.current_object:
|
if list_obj == self.current_object:
|
||||||
current_row = ix
|
current_row = index
|
||||||
|
|
||||||
if current_row == -1:
|
if current_row == -1:
|
||||||
current_row = self.current_index
|
current_row = self.current_index
|
||||||
|
@ -697,7 +831,7 @@ class MainWindow(KMainWindow, Ui_MainWindow):
|
||||||
|
|
||||||
def slot_save_live_text(self):
|
def slot_save_live_text(self):
|
||||||
text = self.live_text.toHtml()
|
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:
|
if not preview:
|
||||||
return
|
return
|
||||||
old_item = self.model.text_by_preview(preview)
|
old_item = self.model.text_by_preview(preview)
|
||||||
|
@ -720,7 +854,7 @@ class MainWindow(KMainWindow, Ui_MainWindow):
|
||||||
|
|
||||||
def slot_save_preview_text(self):
|
def slot_save_preview_text(self):
|
||||||
text = self.preview_text.toHtml()
|
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:
|
if not preview:
|
||||||
return
|
return
|
||||||
|
@ -747,7 +881,6 @@ class MainWindow(KMainWindow, Ui_MainWindow):
|
||||||
cPickle.dump(self.model.text_db, f, cPickle.HIGHEST_PROTOCOL)
|
cPickle.dump(self.model.text_db, f, cPickle.HIGHEST_PROTOCOL)
|
||||||
self.db_dirty = False
|
self.db_dirty = False
|
||||||
|
|
||||||
|
|
||||||
def slot_open_dialog(self):
|
def slot_open_dialog(self):
|
||||||
self.current_index = self.text_combo.currentItem()
|
self.current_index = self.text_combo.currentItem()
|
||||||
self.current_object = self.model.text_db[self.current_index]
|
self.current_object = self.model.text_db[self.current_index]
|
||||||
|
@ -758,13 +891,8 @@ class MainWindow(KMainWindow, Ui_MainWindow):
|
||||||
self.dialog = KDialog(self)
|
self.dialog = KDialog(self)
|
||||||
self.dialog_widget = TextSorterDialog(self.dialog)
|
self.dialog_widget = TextSorterDialog(self.dialog)
|
||||||
self.dialog.setMainWidget(self.dialog_widget)
|
self.dialog.setMainWidget(self.dialog_widget)
|
||||||
pos_x, pos_y = self.getPreviewCoords()
|
pos_x, pos_y = self.get_preview_coords()
|
||||||
self.dialog.move(pos_x, 0)
|
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.okClicked.connect(self.fill_combo_box)
|
||||||
self.dialog.exec_()
|
self.dialog.exec_()
|
||||||
|
|
||||||
|
@ -773,25 +901,37 @@ class MainWindow(KMainWindow, Ui_MainWindow):
|
||||||
if not os.path.isdir(path):
|
if not os.path.isdir(path):
|
||||||
os.mkdir(path)
|
os.mkdir(path)
|
||||||
try:
|
try:
|
||||||
f = open(os.path.join(path, "texter.db"))
|
db_file = open(os.path.join(path, "texter.db"))
|
||||||
except IOError:
|
except IOError:
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.model.text_db = [list(i) for i in cPickle.load(f)]
|
self.model.text_db = [list(i) for i in cPickle.load(db_file)]
|
||||||
except Exception, e:
|
except ValueError, error:
|
||||||
print e
|
print error
|
||||||
|
|
||||||
self.fill_combo_box()
|
self.fill_combo_box()
|
||||||
self.text_combo.setCurrentItem(0)
|
self.text_combo.setCurrentItem(0)
|
||||||
self.slot_load_preview_text(0)
|
self.slot_load_preview_text(0)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
window = MainWindow()
|
arg_parser = ArgParser("dump_grabber")
|
||||||
|
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)
|
||||||
|
|
||||||
|
window = MainWindow(args)
|
||||||
app.exec_()
|
app.exec_()
|
||||||
|
|
||||||
|
|
||||||
if ( __name__ == '__main__' ):
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
Loading…
Reference in New Issue