diff --git a/README.md b/README.md index 636bd4a..05a9378 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,7 @@ Using this framework, you can use PyQt to develop powerful GUI programs to exten ```Bash sudo pip3 install dbus-python pymupdf grip qrcode python-xlib pyqt5 pyqtwebengine + sudo pacman -S qtermwidget-git ``` 2. Clone this repository and add below code in your ~/.emacs @@ -51,15 +52,16 @@ Using this framework, you can use PyQt to develop powerful GUI programs to exten ### Package description. -| Debian Package | Use for | -| :-------- | :---- | -| dbus-python | DBus IPC for python and elisp | -| pymupdf | Render engine required for PDF Viewer | -| grip | Markdown render server for Markdown Previewer | -| qrcode | Render local file QR code | -| python-xlib | Stick app window into emacs frame | -| pyqt5 | GUI library required for application development | -| pyqtwebengine | QtWebEngine for browser application | +| Debian Package | Package Repo | Use for | +| :-------- | :-------- | :---- | +| dbus-python | pip3 | DBus IPC for python and elisp | +| pymupdf | pip3 | Render engine required for PDF Viewer | +| grip | pip3 | Markdown render server for Markdown Previewer | +| qrcode | pip3 | Render local file QR code | +| python-xlib | pip3 | Stick app window into emacs frame | +| pyqt5 | pip3 | GUI library required for application development | +| pyqtwebengine | pip3 | QtWebEngine for browser application | +| qtermwidget-git | pacman | QTermWidget is terminal emulator for PyQt5 | ### Or run EAF with docker @@ -82,11 +84,12 @@ There are mainly three obstacles: | Markdown previewer | Type 'eaf-open' RET markdown filepath | | Org file previewer | Type 'eaf-open' RET org filepath | | Camera | Type 'eaf-open-camera' | -| Demo | Type 'eaf-open-demo' | +| Terminal | Type 'eaf-open-terminal' | | File Sender | Type 'eaf-file-sender-qrcode' | | | Or use 'eaf-file-sender-qrcode-in-dired' | | File Receiver | Type 'eaf-file-receiver-qrcode' | | Airshare | Type 'eaf-file-transfer-airshare' | +| Demo | Type 'eaf-open-demo' | Please check [Key binding](./docs/KEYBINDING.md) to check keybinding of application. diff --git a/app/terminal/buffer.py b/app/terminal/buffer.py new file mode 100644 index 0000000..1397cda --- /dev/null +++ b/app/terminal/buffer.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# Copyright (C) 2018 Andy Stewart +# +# Author: Andy Stewart +# Maintainer: Andy Stewart +# +# This program 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 +# any later version. +# +# This program 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 this program. If not, see . + +from PyQt5.QtGui import QColor, QFont +from core.buffer import Buffer +import QTermWidget + +class AppBuffer(Buffer): + def __init__(self, buffer_id, url, arguments): + Buffer.__init__(self, buffer_id, url, arguments, True, QColor(0, 0, 0, 255)) + + self.add_widget(QTermWidget.QTermWidget()) + + self.buffer_widget.setTerminalFont(QFont('WenQuanYi Micro Hei Mono', 12)) + self.buffer_widget.setColorScheme('Linux') + + self.buffer_widget.finished.connect(self.request_close_buffer) + + def get_key_event_widgets(self): + return self.buffer_widget.children() + + def fake_key_event_filter(self, event_string): + if event_string == "RET": + self.buffer_widget.sendText("\n") + + def zoom_out(self): + self.buffer_widget.zoomOut() + + def zoom_in(self): + self.buffer_widget.zoomIn() diff --git a/core/buffer.py b/core/buffer.py index 1e2781d..e82a220 100644 --- a/core/buffer.py +++ b/core/buffer.py @@ -22,8 +22,109 @@ from PyQt5 import QtCore from PyQt5.QtGui import QBrush, QColor from PyQt5.QtWidgets import QGraphicsScene +from PyQt5.QtCore import Qt, QEvent +from PyQt5.QtGui import QKeyEvent +from PyQt5.QtWidgets import QApplication + import abc +qt_key_dict = { + '''a''': Qt.Key_A, + '''b''': Qt.Key_B, + '''c''': Qt.Key_C, + '''d''': Qt.Key_D, + '''e''': Qt.Key_E, + '''f''': Qt.Key_F, + '''g''': Qt.Key_G, + '''h''': Qt.Key_H, + '''i''': Qt.Key_I, + '''j''': Qt.Key_J, + '''k''': Qt.Key_K, + '''l''': Qt.Key_L, + '''m''': Qt.Key_M, + '''n''': Qt.Key_N, + '''o''': Qt.Key_O, + '''p''': Qt.Key_P, + '''q''': Qt.Key_Q, + '''r''': Qt.Key_R, + '''s''': Qt.Key_S, + '''t''': Qt.Key_T, + '''u''': Qt.Key_U, + '''v''': Qt.Key_V, + '''w''': Qt.Key_W, + '''x''': Qt.Key_X, + '''y''': Qt.Key_Y, + '''z''': Qt.Key_Z, + + '''A''': Qt.Key_A, + '''B''': Qt.Key_B, + '''C''': Qt.Key_C, + '''D''': Qt.Key_D, + '''E''': Qt.Key_E, + '''F''': Qt.Key_F, + '''G''': Qt.Key_G, + '''H''': Qt.Key_H, + '''I''': Qt.Key_I, + '''J''': Qt.Key_J, + '''K''': Qt.Key_K, + '''L''': Qt.Key_L, + '''M''': Qt.Key_M, + '''N''': Qt.Key_N, + '''O''': Qt.Key_O, + '''P''': Qt.Key_P, + '''Q''': Qt.Key_Q, + '''R''': Qt.Key_R, + '''S''': Qt.Key_S, + '''T''': Qt.Key_T, + '''U''': Qt.Key_U, + '''V''': Qt.Key_V, + '''W''': Qt.Key_W, + '''X''': Qt.Key_X, + '''Y''': Qt.Key_Y, + '''Z''': Qt.Key_Z, + + '''0''': Qt.Key_0, + '''1''': Qt.Key_1, + '''2''': Qt.Key_2, + '''3''': Qt.Key_3, + '''4''': Qt.Key_4, + '''5''': Qt.Key_5, + '''6''': Qt.Key_6, + '''7''': Qt.Key_7, + '''8''': Qt.Key_8, + '''9''': Qt.Key_9, + ''';''': Qt.Key_Semicolon, + '''.''': Qt.Key_Period, + ''',''': Qt.Key_Comma, + '''+''': Qt.Key_Plus, + '''-''': Qt.Key_Minus, + '''=''': Qt.Key_Equal, + '''[''': Qt.Key_BracketLeft, + ''']''': Qt.Key_BracketRight, + '''@''': Qt.Key_At, + '''"''': Qt.Key_QuoteDbl, + '''$''': Qt.Key_Dollar, + '''%''': Qt.Key_Percent, + '''/''': Qt.Key_Slash, + '''SPC''': Qt.Key_Space, + '''RET''': Qt.Key_Return, + '''DEL''': Qt.Key_Backspace, + '''TAB''': Qt.Key_Tab, + '''''': Qt.Key_Backtab, + '''''': Qt.Key_Home, + '''''': Qt.Key_End, + '''''': Qt.Key_Left, + '''''': Qt.Key_Right, + '''''': Qt.Key_Up, + '''''': Qt.Key_Down, + '''''': Qt.Key_PageUp, + '''''': Qt.Key_PageDown, +} + +qt_text_dict = { + "SPC": " " +} + class Buffer(QGraphicsScene): __metaclass__ = abc.ABCMeta @@ -127,5 +228,33 @@ class Buffer(QGraphicsScene): def execute_function(self, function_name): getattr(self, function_name)() + def fake_key_event_filter(self, event_string): + pass + + def fake_key_event(self, event_string): + # Init. + text = event_string + modifier = Qt.NoModifier + + # Get key text. + if event_string in qt_text_dict: + text = qt_text_dict[event_string] + + if event_string in ["TAB", ""]: + text = "" + + if event_string == "": + modifier = Qt.ShiftModifier + + if event_string.isupper() : + modifier = Qt.ShiftModifier + + print("Press: ", event_string) + + # NOTE: don't ignore text argument, otherwise QWebEngineView not respond key event. + key_press = QKeyEvent(QEvent.KeyPress, qt_key_dict[event_string], modifier, text) + for widget in self.get_key_event_widgets(): + QApplication.sendEvent(widget, key_press) + self.fake_key_event_filter(event_string) diff --git a/core/fake_key_event.py b/core/fake_key_event.py deleted file mode 100644 index f2a2d31..0000000 --- a/core/fake_key_event.py +++ /dev/null @@ -1,146 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -# Copyright (C) 2018 Andy Stewart -# -# Author: Andy Stewart -# Maintainer: Andy Stewart -# -# This program 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 -# any later version. -# -# This program 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 this program. If not, see . - -from PyQt5.QtCore import Qt, QEvent -from PyQt5.QtGui import QKeyEvent -from PyQt5.QtWidgets import QApplication - -qt_key_dict = { - '''a''': Qt.Key_A, - '''b''': Qt.Key_B, - '''c''': Qt.Key_C, - '''d''': Qt.Key_D, - '''e''': Qt.Key_E, - '''f''': Qt.Key_F, - '''g''': Qt.Key_G, - '''h''': Qt.Key_H, - '''i''': Qt.Key_I, - '''j''': Qt.Key_J, - '''k''': Qt.Key_K, - '''l''': Qt.Key_L, - '''m''': Qt.Key_M, - '''n''': Qt.Key_N, - '''o''': Qt.Key_O, - '''p''': Qt.Key_P, - '''q''': Qt.Key_Q, - '''r''': Qt.Key_R, - '''s''': Qt.Key_S, - '''t''': Qt.Key_T, - '''u''': Qt.Key_U, - '''v''': Qt.Key_V, - '''w''': Qt.Key_W, - '''x''': Qt.Key_X, - '''y''': Qt.Key_Y, - '''z''': Qt.Key_Z, - - '''A''': Qt.Key_A, - '''B''': Qt.Key_B, - '''C''': Qt.Key_C, - '''D''': Qt.Key_D, - '''E''': Qt.Key_E, - '''F''': Qt.Key_F, - '''G''': Qt.Key_G, - '''H''': Qt.Key_H, - '''I''': Qt.Key_I, - '''J''': Qt.Key_J, - '''K''': Qt.Key_K, - '''L''': Qt.Key_L, - '''M''': Qt.Key_M, - '''N''': Qt.Key_N, - '''O''': Qt.Key_O, - '''P''': Qt.Key_P, - '''Q''': Qt.Key_Q, - '''R''': Qt.Key_R, - '''S''': Qt.Key_S, - '''T''': Qt.Key_T, - '''U''': Qt.Key_U, - '''V''': Qt.Key_V, - '''W''': Qt.Key_W, - '''X''': Qt.Key_X, - '''Y''': Qt.Key_Y, - '''Z''': Qt.Key_Z, - - '''0''': Qt.Key_0, - '''1''': Qt.Key_1, - '''2''': Qt.Key_2, - '''3''': Qt.Key_3, - '''4''': Qt.Key_4, - '''5''': Qt.Key_5, - '''6''': Qt.Key_6, - '''7''': Qt.Key_7, - '''8''': Qt.Key_8, - '''9''': Qt.Key_9, - ''';''': Qt.Key_Semicolon, - '''.''': Qt.Key_Period, - ''',''': Qt.Key_Comma, - '''+''': Qt.Key_Plus, - '''-''': Qt.Key_Minus, - '''=''': Qt.Key_Equal, - '''[''': Qt.Key_BracketLeft, - ''']''': Qt.Key_BracketRight, - '''@''': Qt.Key_At, - '''"''': Qt.Key_QuoteDbl, - '''$''': Qt.Key_Dollar, - '''%''': Qt.Key_Percent, - '''SPC''': Qt.Key_Space, - '''RET''': Qt.Key_Return, - '''DEL''': Qt.Key_Backspace, - '''TAB''': Qt.Key_Tab, - '''''': Qt.Key_Backtab, - '''''': Qt.Key_Home, - '''''': Qt.Key_End, - '''''': Qt.Key_Left, - '''''': Qt.Key_Right, - '''''': Qt.Key_Up, - '''''': Qt.Key_Down, - '''''': Qt.Key_PageUp, - '''''': Qt.Key_PageDown, -} - -qt_text_dict = { - "SPC": " " -} - -def fake_key_event(event_string, app_buffer): - # Init. - text = event_string - modifier = Qt.NoModifier - - # Get key text. - if event_string in qt_text_dict: - text = qt_text_dict[event_string] - - if event_string in ["TAB", ""]: - text = "" - - if event_string == "": - modifier = Qt.ShiftModifier - - if event_string.isupper() : - modifier = Qt.ShiftModifier - - print("Press: ", event_string) - - # NOTE: don't ignore text argument, otherwise QWebEngineView not respond key event. - key_press = QKeyEvent(QEvent.KeyPress, qt_key_dict[event_string], modifier, text) - - for widget in app_buffer.get_key_event_widgets(): - QApplication.sendEvent(widget, key_press) diff --git a/docs/TODOLIST.md b/docs/TODOLIST.md index c5164a5..bc18971 100644 --- a/docs/TODOLIST.md +++ b/docs/TODOLIST.md @@ -7,7 +7,6 @@ * PdfViewer: split window first, then scroll event cause view blank * PdfViewer: allow select text. * PdfViewer: use self.document.getToC() fetch and render bookmark. -* Terminal: https://github.com/lxqt/qtermwidget looks support pyqt5 too. ;) * Support HiDPI? I haven't HiDPI screen. * Make EAF support bookmark. * Aria2 client is awesome, welcome to PR. diff --git a/eaf.el b/eaf.el index 88e8364..462a016 100644 --- a/eaf.el +++ b/eaf.el @@ -199,6 +199,13 @@ by `dired-find-alternate-file'. Otherwise they will be opened normally with `dir :type 'cons :group 'eaf) +(defcustom eaf-terminal-keybinding + '(("C--" . "zoom_out") + ("C-=" . "zoom_in")) + "The keybinding of terminal." + :type 'cons + :group 'eaf) + (defun eaf-call (method &rest args) (apply 'dbus-call-method :session ; use the session (not system) bus @@ -425,6 +432,11 @@ We need calcuate render allocation to make sure no black border around render co (let ((function-name-value (assoc key-desc eaf-browser-keybinding))) (when function-name-value (eaf-call "execute_function" buffer-id (cdr function-name-value)))) + ) + ((equal buffer-app-name "terminal") + (let ((function-name-value (assoc key-desc eaf-terminal-keybinding))) + (when function-name-value + (eaf-call "execute_function" buffer-id (cdr function-name-value)))) ))) ((or (equal key-command "nil") @@ -639,6 +651,11 @@ Otherwise call send_key message to Python side." (interactive) (eaf-open "eaf-camera" "camera")) +(defun eaf-open-terminal () + "Open EAF terminal application." + (interactive) + (eaf-open "eaf-terminal" "terminal")) + (defun eaf-open-qutebrowser () "Open EAF Qutebrowser application." (interactive) diff --git a/eaf.py b/eaf.py index d52bb36..64da040 100755 --- a/eaf.py +++ b/eaf.py @@ -26,7 +26,6 @@ from app.browser.buffer import AppBuffer as NeverUsed # noqa from PyQt5.QtNetwork import QNetworkProxy from PyQt5.QtWidgets import QApplication -from core.fake_key_event import fake_key_event from core.view import View from dbus.mainloop.glib import DBusGMainLoop import dbus @@ -241,7 +240,7 @@ class EAF(dbus.service.Object): def send_key(self, buffer_id, event_string): # Send event to buffer when found match buffer. if buffer_id in self.buffer_dict: - fake_key_event(event_string, self.buffer_dict[buffer_id]) + self.buffer_dict[buffer_id].fake_key_event(event_string) @dbus.service.method(EAF_DBUS_NAME, in_signature="sss", out_signature="") def handle_input_message(self, buffer_id, callback_type, callback_result):