Add terminal emulator application.

master
Andy Stewart 7 years ago
parent e8c0310c29
commit 939d2c2244
  1. 23
      README.md
  2. 48
      app/terminal/buffer.py
  3. 129
      core/buffer.py
  4. 146
      core/fake_key_event.py
  5. 1
      docs/TODOLIST.md
  6. 17
      eaf.el
  7. 3
      eaf.py

@ -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.

@ -0,0 +1,48 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright (C) 2018 Andy Stewart
#
# Author: Andy Stewart <lazycat.manatee@gmail.com>
# Maintainer: Andy Stewart <lazycat.manatee@gmail.com>
#
# 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 <http://www.gnu.org/licenses/>.
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()

@ -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,
'''<backtab>''': Qt.Key_Backtab,
'''<home>''': Qt.Key_Home,
'''<end>''': Qt.Key_End,
'''<left>''': Qt.Key_Left,
'''<right>''': Qt.Key_Right,
'''<up>''': Qt.Key_Up,
'''<down>''': Qt.Key_Down,
'''<prior>''': Qt.Key_PageUp,
'''<next>''': 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", "<backtab>"]:
text = ""
if event_string == "<backtab>":
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)

@ -1,146 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright (C) 2018 Andy Stewart
#
# Author: Andy Stewart <lazycat.manatee@gmail.com>
# Maintainer: Andy Stewart <lazycat.manatee@gmail.com>
#
# 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 <http://www.gnu.org/licenses/>.
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,
'''<backtab>''': Qt.Key_Backtab,
'''<home>''': Qt.Key_Home,
'''<end>''': Qt.Key_End,
'''<left>''': Qt.Key_Left,
'''<right>''': Qt.Key_Right,
'''<up>''': Qt.Key_Up,
'''<down>''': Qt.Key_Down,
'''<prior>''': Qt.Key_PageUp,
'''<next>''': 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", "<backtab>"]:
text = ""
if event_string == "<backtab>":
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)

@ -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.

@ -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)

@ -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):

Loading…
Cancel
Save