Skip to content

Commit

Permalink
Merge pull request emacs-eaf#283 from manateelazycat/revert-282-featu…
Browse files Browse the repository at this point in the history
…re/websocket

Revert "Use Websocket+Jsonrpc to replace Dbus"
  • Loading branch information
MatthewZMD authored Apr 20, 2020
2 parents baa06d6 + aacf448 commit 5c351ec
Show file tree
Hide file tree
Showing 29 changed files with 367 additions and 2,654 deletions.
17 changes: 12 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ EAF is an extensible framework, one can develop any Qt5 application and integrat
1. Install EAF dependencies:

```Bash
sudo pacman -S python-pyqt5 python-pyqt5-sip python-pyqtwebengine python-qrcode python-feedparser python-markdown nodejs aria2 libreoffice filebrowser
sudo pacman -S python-pyqt5 python-pyqt5-sip python-pyqtwebengine python-qrcode python-feedparser python-dbus python-pyinotify python-markdown nodejs aria2 libreoffice filebrowser
yay -S python-pymupdf python-grip
```

Expand Down Expand Up @@ -87,11 +87,13 @@ Packages listed as **Core** are mandatory for EAF to work, whereas other package
| Package | Dependent | Description |
| :-------- | :------ | :------ |
| python-pyqt5, python-pyqt5-sip | Core | Essential GUI library |
| python-dbus | Core | DBus IPC to connect Python with Elisp |
| python-pyqtwebengine | Core | Chromium based web rendering engine |
| python-pymupdf | PDF Viewer | PDF rendering engine |
| python-grip | Markdown Previewer | Markdown rendering server |
| python-qrcode | File Sender, File Receiver, Airshare | Render QR code pointing to local files |
| python-feedparser | RSS Reader | Parse RSS feeds |
| python-pyinotify | Mermaid | Monitor *.mmd file change status |
| python-markdown | Mermaid | Covert markdown format to mermaid html format |
| nodejs | Terminal | Communicate between browser and local TTY |
| aria2 | Browser | Download files from the web |
Expand Down Expand Up @@ -122,6 +124,11 @@ Packages listed as **Core** are mandatory for EAF to work, whereas other package

- To open the file under the cursor in `dired` using appropriate EAF Application, use `eaf-open-this-from-dired` instead.

```
NOTE:
EAF use DBus' session bus, it must run by a general user.
Please don't use EAF when Emacs is started with sudo or root user, a root user can only access DBus's system bus.
```

## Wiki
It is **highly** suggested to read the [Wiki](https://github.com/manateelazycat/emacs-application-framework/wiki) first before using EAF.
Expand All @@ -133,7 +140,7 @@ Wiki consists of documentations on Keybinding, Customization, Design and TODOLIS
### How does EAF work?
EAF implements three major functionalities:
1. Integrate PyQt program window into Emacs frame using QWindow Reparent technology.
2. Listen to EAF buffer's keyboard event flow and control the keyboard input of PyQt program via websocket + jsonrpc.
2. Listen to EAF buffer's keyboard event flow and control the keyboard input of PyQt program via DBus IPC.
3. Create a window compositer to make a PyQt program window adapt Emacs's Window/Buffer design.

### EAF vs EXWM?
Expand All @@ -145,9 +152,9 @@ EAF implements three major functionalities:

Both projects are similar in terms of interface, but they are two completely different projects with different goals in mind. Sometimes one may find EAF is more suitable than EXWM, sometimes it's the other way around. Please do not meaninglessly compare them.

### EAF is (currently) Linux/Windows only. Why?
1. Qt5's QGraphicsScene technology does not work on MacOS.
2. Cross-process reparent does not work on MacOS.
### EAF is (currently) Linux only. Why?
1. DBus is Linux-specific technology, it's difficult to support DBus in other operating systems.
2. Qt5's QGraphicsScene technology does not work on MacOS.

If you've figure them out, PRs are always welcome!

Expand Down
19 changes: 12 additions & 7 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ EAF是一个可编程扩展的框架,你可以开发自己的Qt5应用并集

```Bash
sudo pacman -S python-pyqt5 python-pyqt5-sip python-pyqtwebengine python-qrcode python-feedparser
python-markdown nodejs aria2 libreoffice filebrowser
python-dbus python-pyinotify python-markdown nodejs aria2 libreoffice filebrowser
yay -S python-pymupdf python-grip
```

Expand All @@ -71,7 +71,6 @@ git clone https://github.com/manateelazycat/emacs-application-framework.git --de
如果你使用[use-package](https://github.com/jwiegley/use-package),下面有一个简单的配置文件供你参考:

```Elisp
(use-package eaf
:load-path "~/.emacs.d/site-lisp/emacs-application-framework" ; Set to "/usr/share/emacs/site-lisp/eaf" if installed from AUR
:custom
Expand All @@ -88,11 +87,13 @@ git clone https://github.com/manateelazycat/emacs-application-framework.git --de
| 包名 | 依赖 | 解释 |
| :-------- | :------ | :------ |
| python-pyqt5, python-pyqt5-sip | 核心 | GUI图形库 |
| python-dbus | 核心 | DBus库,用于在Emacs和Python进程间通讯 |
| python-pyqtwebengine | 核心 | 基于Chromium的浏览器引擎 |
| python-pymupdf | PDF阅读器 | 解析PDF文件 |
| python-grip | Markdown预览 | 建立Markdown文件的HTML服务 |
| python-qrcode | 文件上传,文件下载,文字传输 | 根据文件信息生成二维码 |
| python-feedparser | RSS阅读器 | 解析RSS/Atom信息 |
| python-pyinotify | 流程图 | 监听 mmd 格式文件的变动 |
| python-markdown | 流程图 | 转换 mmd 格式为 mermaid 识别的 html 格式 |
| aria2 | 浏览器 | 下载网络文件 |
| nodejs | 终端模拟器 | 通过浏览器与本地TTY交互 |
Expand Down Expand Up @@ -123,6 +124,10 @@ git clone https://github.com/manateelazycat/emacs-application-framework.git --de

-`dired`文件管理器中,建议绑定按键到命令 `eaf-open-this-from-dired` ,它会自动用合适的EAF应用来打开文件。

```
注意:
EAF使用DBus的普通权限总线 (session bus),请不要用 sudo 来启动EAF,root用户只能访问系统权限总线 (system bus)
```

## Wiki
强烈建议使用EAF之前浏览一遍[Wiki](https://github.com/manateelazycat/emacs-application-framework/wiki)
Expand All @@ -134,7 +139,7 @@ Wiki包括架构设计,按键绑定,自定义选项和任务列表等文档
### EAF是怎么工作的?
EAF主要实现这几个功能:
1. 利用QWindow的Reparent技术来实现PyQt应用进程的窗口粘贴到Emacs对应的Buffer区域
2. 通过websocket jsonrpc来实现Emacs进程和Python进程的控制指令和跨进程消息通讯
2. 通过DBus IPC来实现Emacs进程和Python进程的控制指令和跨进程消息通讯
3. 通过Qt5的QGraphicsScene来实现镜像窗口,以对应Emacs的Buffer/Window模型

### EAF vs EXWM?
Expand All @@ -147,11 +152,11 @@ EAF主要实现这几个功能:

或许EAF和EXWM看起来有点相似,但它们在设计和理念上是两个完全不同的项目。所以请大家多多学习X11和Qt的区别,理解技术的本质,避免无意义的比较和争论。

### 为什么EAF只能在Linux/Windows下工作
1. Qt5的QGraphicsScene技术无法在MacOS下正常工作,也就无法实现Qt5应用的镜像窗口以支持Emacs的Buffer/Window模型
2. 跨进程 reparent 技术在 MacOS 下不能正常工作。
### 为什么EAF只能在Linux下工作
1. DBus是Linux下专用的进程间通讯技术,其他操作系统可能无法支持DBus
2. Qt5的QGraphicsScene技术无法在MacOS下正常工作,也就无法实现Qt5应用的镜像窗口以支持Emacs的Buffer/Window模型

欢迎操作系统级别黑客移植EAF,目前为止,我知道的主要的迁移障碍就只有两个:QGraphicsScene
欢迎操作系统级别黑客移植EAF,目前为止,我知道的主要的迁移障碍就只有两个:DBus,QGraphicsScene

### `[EAF] *eaf* aborted (core dumped)` 奔溃了怎么办?
请检查 `*eaf*` 这个窗口的内容。通常是EAF的Python依赖没有安装好,如果你确定依赖没有问题,请附带 `*eaf*` 窗口的内容给我们提交issue,那里面有很多线索可以帮助我们排查问题。
Expand Down
4 changes: 2 additions & 2 deletions app/airshare/buffer.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@
import qrcode

class AppBuffer(Buffer):
def __init__(self, buffer_id, url, config_dir, arguments, emacs_var_dict, module_path, async_call_emacs):
Buffer.__init__(self, buffer_id, url, arguments, emacs_var_dict, module_path, async_call_emacs, False)
def __init__(self, buffer_id, url, config_dir, arguments, emacs_var_dict, module_path):
Buffer.__init__(self, buffer_id, url, arguments, emacs_var_dict, module_path, False)

self.add_widget(AirShareWidget(url, QColor(0, 0, 0, 255)))

Expand Down
4 changes: 2 additions & 2 deletions app/browser/buffer.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@
import os

class AppBuffer(BrowserBuffer):
def __init__(self, buffer_id, url, config_dir, arguments, emacs_var_dict, module_path, async_call_emacs):
BrowserBuffer.__init__(self, buffer_id, url, config_dir, arguments, emacs_var_dict, module_path, async_call_emacs, False)
def __init__(self, buffer_id, url, config_dir, arguments, emacs_var_dict, module_path):
BrowserBuffer.__init__(self, buffer_id, url, config_dir, arguments, emacs_var_dict, module_path, False)

self.config_dir = config_dir

Expand Down
50 changes: 7 additions & 43 deletions app/camera/buffer.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,23 +23,18 @@
from PyQt5.QtGui import QBrush
from PyQt5.QtGui import QColor
from PyQt5.QtMultimedia import QCameraInfo, QCamera, QCameraImageCapture
from PyQt5.QtMultimediaWidgets import QGraphicsVideoItem, QVideoWidget
from PyQt5.QtMultimediaWidgets import QGraphicsVideoItem
from PyQt5.QtWidgets import QGraphicsScene, QGraphicsView
from PyQt5.QtWidgets import QWidget, QVBoxLayout
from core.buffer import Buffer
from pathlib import Path
import time
import os
import platform
import shutil

class AppBuffer(Buffer):
def __init__(self, buffer_id, url, config_dir, arguments, emacs_var_dict, module_path, async_call_emacs):
Buffer.__init__(self, buffer_id, url, arguments, emacs_var_dict, module_path, async_call_emacs, True)
if platform.system() == "Windows":
self.add_widget(WindowsCameraWidget(QColor(0, 0, 0, 255)))
else:
self.add_widget(CameraWidget(QColor(0, 0, 0, 255)))
def __init__(self, buffer_id, url, config_dir, arguments, emacs_var_dict, module_path):
Buffer.__init__(self, buffer_id, url, arguments, emacs_var_dict, module_path, True)
self.add_widget(CameraWidget(QColor(0, 0, 0, 255)))

def all_views_hide(self):
# Need stop camera if all view will hide, otherwise camera will crash.
Expand All @@ -51,9 +46,9 @@ def some_view_show(self):

def take_photo(self):
if os.path.exists(os.path.expanduser(self.emacs_var_dict["eaf-camera-save-path"])):
self.message_to_emacs.emit(self.buffer_widget.take_photo(self.emacs_var_dict["eaf-camera-save-path"]))
self.buffer_widget.take_photo(self.emacs_var_dict["eaf-camera-save-path"])
else:
self.message_to_emacs.emit(self.buffer_widget.take_photo("~/Downloads"))
self.buffer_widget.take_photo("~/Downloads")

def destroy_buffer(self):
self.buffer_widget.stop_camera()
Expand Down Expand Up @@ -100,38 +95,7 @@ def take_photo(self, camera_save_path):
save_path = str(Path(os.path.expanduser(camera_save_path)))
photo_path = os.path.join(save_path, "EAF_Camera_Photo_" + time.strftime("%Y-%m-%d_%H:%M:%S", time.localtime(int(time.time()))))
image_capture.capture(photo_path)
return "Captured Photo at " + photo_path

def stop_camera(self):
self.camera.stop()


class WindowsCameraWidget(QWidget):
def __init__(self, background_color):
QWidget.__init__(self)

self.video_widget = QVideoWidget()
self.layout = QVBoxLayout(self)
self.layout.setContentsMargins(0, 0, 0, 0)
self.layout.addWidget(self.video_widget)
self.available_cameras = QCameraInfo.availableCameras()
# Set the default camera.
self.select_camera(0)

def select_camera(self, i):
self.camera = QCamera(self.available_cameras[i])
self.camera.setViewfinder(self.video_widget)
self.camera.setCaptureMode(QCamera.CaptureStillImage)
self.camera.start()

def take_photo(self, camera_save_path):
save_path = str(Path(os.path.expanduser(camera_save_path)))
photo_path = os.path.join(save_path, "EAF_Camera_Photo_" + time.strftime("%Y-%m-%d_%H-%M-%S", time.localtime(int(time.time()))) + ".jpg")

self.image_capture = QCameraImageCapture(self.camera)
self.image_capture.imageSaved.connect(lambda id, file_path : os.rename(file_path, photo_path))
self.image_capture.capture()
return "Captured Photo at " + photo_path
self.message_to_emacs.emit("Captured Photo at " + photo_path)

def stop_camera(self):
self.camera.stop()
Expand Down
14 changes: 2 additions & 12 deletions app/demo/buffer.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,8 @@
from core.buffer import Buffer

class AppBuffer(Buffer):
def __init__(self, buffer_id, url, config_dir, arguments, emacs_var_dict, module_path, async_call_emacs):
Buffer.__init__(self, buffer_id, url, arguments, emacs_var_dict, module_path, async_call_emacs, True)
def __init__(self, buffer_id, url, config_dir, arguments, emacs_var_dict, module_path):
Buffer.__init__(self, buffer_id, url, arguments, emacs_var_dict, module_path, True)

self.add_widget(QPushButton("Hello, EAF hacker, it's working!!!"))
self.buffer_widget.setStyleSheet("font-size: 100px")
self.async_call_emacs("eaf-get-theme-mode", success_cb=self.update_background)

def update_background(self, theme):
if theme == "dark":
self.background_color = QColor(233, 129, 35, 255)
else:
self.background_color = QColor(255, 255, 255, 255)

self.setBackgroundBrush(self.background_color)
self.update()
4 changes: 2 additions & 2 deletions app/file-browser/buffer.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@
from core.buffer import Buffer

class AppBuffer(Buffer):
def __init__(self, buffer_id, url, config_dir, argument, emacs_var_dict, module_path, async_call_emacs):
Buffer.__init__(self, buffer_id, url, argument, emacs_var_dict, module_path, async_call_emacs, False)
def __init__(self, buffer_id, url, config_dir, argument, emacs_var_dict, module_path):
Buffer.__init__(self, buffer_id, url, argument, emacs_var_dict, module_path, False)

self.add_widget(FileUploaderWidget(url, QColor(0, 0, 0, 255)))

Expand Down
4 changes: 2 additions & 2 deletions app/file-sender/buffer.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@
import socket

class AppBuffer(Buffer):
def __init__(self, buffer_id, url, config_dir, arguments, emacs_var_dict, module_path, async_call_emacs):
Buffer.__init__(self, buffer_id, url, arguments, emacs_var_dict, module_path, async_call_emacs, False)
def __init__(self, buffer_id, url, config_dir, arguments, emacs_var_dict, module_path):
Buffer.__init__(self, buffer_id, url, arguments, emacs_var_dict, module_path, False)

self.add_widget(FileTransferWidget(url, QColor(0, 0, 0, 255)))

Expand Down
8 changes: 4 additions & 4 deletions app/image-viewer/buffer.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,19 +27,19 @@
import os

class AppBuffer(BrowserBuffer):
def __init__(self, buffer_id, url, config_dir, arguments, emacs_var_dict, module_path, async_call_emacs):
BrowserBuffer.__init__(self, buffer_id, url, config_dir, arguments, emacs_var_dict, module_path, async_call_emacs, False)
def __init__(self, buffer_id, url, config_dir, arguments, emacs_var_dict, module_path):
BrowserBuffer.__init__(self, buffer_id, url, config_dir, arguments, emacs_var_dict, module_path, False)

self.load_image(url)

def load_image(self, url):
self.url = url
self.parent_dir = os.path.abspath(os.path.join(url, os.pardir))
self.image_name = os.path.basename(url)
self.buffer_widget.setUrl(QUrl.fromLocalFile(self.url))
self.buffer_widget.setUrl(QUrl("file://" + self.url))

def is_image_file(self, f):
return Path(f).suffix.lower() in [".jpg", ".jpeg", ".png", ".bmp", ".gif", ".svg", ".webp"]
return Path(f).suffix.lower() in ["jpg", "jpeg", "png", "bmp", "gif", "svg", "webp"]

def get_same_dir_images(self):
files = [f for f in os.listdir(self.parent_dir) if os.path.isfile(os.path.join(self.parent_dir, f))]
Expand Down
10 changes: 5 additions & 5 deletions app/js-video-player/buffer.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,12 @@ class AppBuffer(BrowserBuffer):

export_org_json = QtCore.pyqtSignal(str, str)

def __init__(self, buffer_id, url, config_dir, arguments, emacs_var_dict, module_path, async_call_emacs):
BrowserBuffer.__init__(self, buffer_id, url, config_dir, arguments, emacs_var_dict, module_path, async_call_emacs, False)
def __init__(self, buffer_id, url, config_dir, arguments, emacs_var_dict, module_path):
BrowserBuffer.__init__(self, buffer_id, url, config_dir, arguments, emacs_var_dict, module_path, False)

self.url = url
index_file = os.path.abspath(os.path.join(os.path.dirname(__file__), "index.html"))
self.buffer_widget.setUrl(QUrl.fromLocalFile(index_file))
index_file = "file://" + (os.path.join(os.path.dirname(__file__), "index.html"))
self.buffer_widget.setUrl(QUrl(index_file))

for method_name in ["toggle_play", "forward", "backward", "restart", "increase_volume", "decrease_volume"]:
self.build_js_method(method_name)
Expand All @@ -55,7 +55,7 @@ def restore_seek_position(self):
self.buffer_widget.eval_js("set_current_time('{}');".format(self.position))

def play_video(self):
self.buffer_widget.eval_js("play('{}');".format(QUrl.fromLocalFile(self.url).toString()))
self.buffer_widget.eval_js("play('{}');".format("file://" + self.url))

def build_js_method(self, method_name):
def _do ():
Expand Down
6 changes: 3 additions & 3 deletions app/markdown-previewer/buffer.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@
import threading

class AppBuffer(BrowserBuffer):
def __init__(self, buffer_id, url, config_dir, arguments, emacs_var_dict, module_path, async_call_emacs):
BrowserBuffer.__init__(self, buffer_id, url, config_dir, arguments, emacs_var_dict, module_path, async_call_emacs, False)
def __init__(self, buffer_id, url, config_dir, arguments, emacs_var_dict, module_path):
BrowserBuffer.__init__(self, buffer_id, url, config_dir, arguments, emacs_var_dict, module_path, False)

# Get free port to render markdown.
self.port = get_free_port()
Expand All @@ -51,4 +51,4 @@ def load_markdown_server(self):

paths = os.path.split(self.url)
if len(paths) > 0:
self.change_title(paths[-1] + " preview")
self.change_title(paths[-1])
Loading

0 comments on commit 5c351ec

Please sign in to comment.