Merge pull request #13 from maxking/py3-qt6

Port to using Python3 and Pyside6
master
Sébastien Helleu 3 years ago committed by GitHub
commit e139bca02f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,7 +1,9 @@
language: python language: python
python: python:
- "2.7" - "3.7"
- "3.8"
- "3.9"
install: install:
- pip install flake8 - pip install flake8

@ -25,13 +25,13 @@ Following packages are *required*:
* WeeChat (version >= 0.3.7) on local or remote machine, with relay plugin * WeeChat (version >= 0.3.7) on local or remote machine, with relay plugin
enabled and listening on a port with protocol "weechat" enabled and listening on a port with protocol "weechat"
* Python 2.x >= 2.6 * Python 3.7+
* PySide (recommended, packages: python.pyside.*) or PyQt4 (python-qt4) * PySide6
=== Install via source distribution === Install via source distribution
---- ----
$ python setup.py install $ pip install .
---- ----
== WeeChat setup == WeeChat setup

@ -20,10 +20,8 @@
# along with QWeeChat. If not, see <http://www.gnu.org/licenses/>. # along with QWeeChat. If not, see <http://www.gnu.org/licenses/>.
# #
import qt_compat from PySide6 import QtCore
from PySide6 import QtWidgets as QtGui
QtCore = qt_compat.import_module('QtCore')
QtGui = qt_compat.import_module('QtGui')
class AboutDialog(QtGui.QDialog): class AboutDialog(QtGui.QDialog):
@ -44,7 +42,7 @@ class AboutDialog(QtGui.QDialog):
vbox = QtGui.QVBoxLayout() vbox = QtGui.QVBoxLayout()
for msg in messages: for msg in messages:
label = QtGui.QLabel(msg.decode('utf-8')) label = QtGui.QLabel(msg)
label.setAlignment(QtCore.Qt.AlignHCenter) label.setAlignment(QtCore.Qt.AlignHCenter)
vbox.addWidget(label) vbox.addWidget(label)
vbox.addLayout(hbox) vbox.addLayout(hbox)

@ -19,22 +19,20 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with QWeeChat. If not, see <http://www.gnu.org/licenses/>. # along with QWeeChat. If not, see <http://www.gnu.org/licenses/>.
# #
from pkg_resources import resource_filename from pkg_resources import resource_filename
import qt_compat
from chat import ChatTextEdit
from input import InputLineEdit
import weechat.color as color
QtCore = qt_compat.import_module('QtCore') from qweechat.chat import ChatTextEdit
QtGui = qt_compat.import_module('QtGui') from qweechat.input import InputLineEdit
from qweechat.weechat import color
from PySide6 import QtCore, QtGui, QtWidgets
class GenericListWidget(QtGui.QListWidget): class GenericListWidget(QtWidgets.QListWidget):
"""Generic QListWidget with dynamic size.""" """Generic QListWidget with dynamic size."""
def __init__(self, *args): def __init__(self, *args):
QtGui.QListWidget.__init__(*(self,) + args) super().__init__(*args)
self.setMaximumWidth(100) self.setMaximumWidth(100)
self.setTextElideMode(QtCore.Qt.ElideNone) self.setTextElideMode(QtCore.Qt.ElideNone)
self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
@ -52,17 +50,17 @@ class GenericListWidget(QtGui.QListWidget):
def clear(self, *args): def clear(self, *args):
"""Re-implement clear to set dynamic size after clear.""" """Re-implement clear to set dynamic size after clear."""
QtGui.QListWidget.clear(*(self,) + args) QtWidgets.QListWidget.clear(*(self,) + args)
self.auto_resize() self.auto_resize()
def addItem(self, *args): def addItem(self, *args):
"""Re-implement addItem to set dynamic size after add.""" """Re-implement addItem to set dynamic size after add."""
QtGui.QListWidget.addItem(*(self,) + args) QtWidgets.QListWidget.addItem(*(self,) + args)
self.auto_resize() self.auto_resize()
def insertItem(self, *args): def insertItem(self, *args):
"""Re-implement insertItem to set dynamic size after insert.""" """Re-implement insertItem to set dynamic size after insert."""
QtGui.QListWidget.insertItem(*(self,) + args) QtWidgets.QListWidget.insertItem(*(self,) + args)
self.auto_resize() self.auto_resize()
@ -70,7 +68,7 @@ class BufferListWidget(GenericListWidget):
"""Widget with list of buffers.""" """Widget with list of buffers."""
def __init__(self, *args): def __init__(self, *args):
GenericListWidget.__init__(*(self,) + args) super().__init__(*args)
def switch_prev_buffer(self): def switch_prev_buffer(self):
if self.currentRow() > 0: if self.currentRow() > 0:
@ -85,23 +83,23 @@ class BufferListWidget(GenericListWidget):
self.setCurrentRow(0) self.setCurrentRow(0)
class BufferWidget(QtGui.QWidget): class BufferWidget(QtWidgets.QWidget):
""" """
Widget with (from top to bottom): Widget with (from top to bottom):
title, chat + nicklist (optional) + prompt/input. title, chat + nicklist (optional) + prompt/input.
""" """
def __init__(self, display_nicklist=False): def __init__(self, display_nicklist=False):
QtGui.QWidget.__init__(self) super().__init__()
# title # title
self.title = QtGui.QLineEdit() self.title = QtWidgets.QLineEdit()
self.title.setFocusPolicy(QtCore.Qt.NoFocus) self.title.setFocusPolicy(QtCore.Qt.NoFocus)
# splitter with chat + nicklist # splitter with chat + nicklist
self.chat_nicklist = QtGui.QSplitter() self.chat_nicklist = QtWidgets.QSplitter()
self.chat_nicklist.setSizePolicy(QtGui.QSizePolicy.Expanding, self.chat_nicklist.setSizePolicy(QtWidgets.QSizePolicy.Expanding,
QtGui.QSizePolicy.Expanding) QtWidgets.QSizePolicy.Expanding)
self.chat = ChatTextEdit(debug=False) self.chat = ChatTextEdit(debug=False)
self.chat_nicklist.addWidget(self.chat) self.chat_nicklist.addWidget(self.chat)
self.nicklist = GenericListWidget() self.nicklist = GenericListWidget()
@ -110,16 +108,16 @@ class BufferWidget(QtGui.QWidget):
self.chat_nicklist.addWidget(self.nicklist) self.chat_nicklist.addWidget(self.nicklist)
# prompt + input # prompt + input
self.hbox_edit = QtGui.QHBoxLayout() self.hbox_edit = QtWidgets.QHBoxLayout()
self.hbox_edit.setContentsMargins(0, 0, 0, 0) self.hbox_edit.setContentsMargins(0, 0, 0, 0)
self.hbox_edit.setSpacing(0) self.hbox_edit.setSpacing(0)
self.input = InputLineEdit(self.chat) self.input = InputLineEdit(self.chat)
self.hbox_edit.addWidget(self.input) self.hbox_edit.addWidget(self.input)
prompt_input = QtGui.QWidget() prompt_input = QtWidgets.QWidget()
prompt_input.setLayout(self.hbox_edit) prompt_input.setLayout(self.hbox_edit)
# vbox with title + chat/nicklist + prompt/input # vbox with title + chat/nicklist + prompt/input
vbox = QtGui.QVBoxLayout() vbox = QtWidgets.QVBoxLayout()
vbox.setContentsMargins(0, 0, 0, 0) vbox.setContentsMargins(0, 0, 0, 0)
vbox.setSpacing(0) vbox.setSpacing(0)
vbox.addWidget(self.title) vbox.addWidget(self.title)
@ -139,7 +137,7 @@ class BufferWidget(QtGui.QWidget):
if self.hbox_edit.count() > 1: if self.hbox_edit.count() > 1:
self.hbox_edit.takeAt(0) self.hbox_edit.takeAt(0)
if prompt is not None: if prompt is not None:
label = QtGui.QLabel(prompt) label = QtWidgets.QLabel(prompt)
label.setContentsMargins(0, 0, 5, 0) label.setContentsMargins(0, 0, 5, 0)
self.hbox_edit.insertWidget(0, label) self.hbox_edit.insertWidget(0, label)
@ -147,7 +145,7 @@ class BufferWidget(QtGui.QWidget):
class Buffer(QtCore.QObject): class Buffer(QtCore.QObject):
"""A WeeChat buffer.""" """A WeeChat buffer."""
bufferInput = qt_compat.Signal(str, str) bufferInput = QtCore.Signal(str, str)
def __init__(self, data={}): def __init__(self, data={}):
QtCore.QObject.__init__(self) QtCore.QObject.__init__(self)
@ -167,15 +165,17 @@ class Buffer(QtCore.QObject):
"""Update title.""" """Update title."""
try: try:
self.widget.set_title( self.widget.set_title(
color.remove(self.data['title'].decode('utf-8'))) color.remove(self.data['title']))
except: # noqa: E722 except Exception: # noqa: E722
# TODO: Debug print the exception to be fixed.
# traceback.print_exc()
self.widget.set_title(None) self.widget.set_title(None)
def update_prompt(self): def update_prompt(self):
"""Update prompt.""" """Update prompt."""
try: try:
self.widget.set_prompt(self.data['local_variables']['nick']) self.widget.set_prompt(self.data['local_variables']['nick'])
except: # noqa: E722 except Exception: # noqa: E722
self.widget.set_prompt(None) self.widget.set_prompt(None)
def input_text_sent(self, text): def input_text_sent(self, text):
@ -243,6 +243,6 @@ class Buffer(QtCore.QObject):
pixmap = QtGui.QPixmap(8, 8) pixmap = QtGui.QPixmap(8, 8)
pixmap.fill() pixmap.fill()
icon = QtGui.QIcon(pixmap) icon = QtGui.QIcon(pixmap)
item = QtGui.QListWidgetItem(icon, nick['name']) item = QtWidgets.QListWidgetItem(icon, nick['name'])
self.widget.nicklist.addItem(item) self.widget.nicklist.addItem(item)
self.widget.nicklist.setVisible(True) self.widget.nicklist.setVisible(True)

@ -21,19 +21,18 @@
# #
import datetime import datetime
import qt_compat from qweechat import config
import config from qweechat.weechat import color
import weechat.color as color
QtCore = qt_compat.import_module('QtCore') from PySide6 import QtCore
QtGui = qt_compat.import_module('QtGui') from PySide6 import QtWidgets, QtGui
class ChatTextEdit(QtGui.QTextEdit): class ChatTextEdit(QtWidgets.QTextEdit):
"""Chat area.""" """Chat area."""
def __init__(self, debug, *args): def __init__(self, debug, *args):
QtGui.QTextEdit.__init__(*(self,) + args) QtWidgets.QTextEdit.__init__(*(self,) + args)
self.debug = debug self.debug = debug
self.readOnly = True self.readOnly = True
self.setFocusPolicy(QtCore.Qt.NoFocus) self.setFocusPolicy(QtCore.Qt.NoFocus)
@ -77,9 +76,9 @@ class ChatTextEdit(QtGui.QTextEdit):
prefix = '\x01(F%s)%s' % (forcecolor, prefix) prefix = '\x01(F%s)%s' % (forcecolor, prefix)
text = '\x01(F%s)%s' % (forcecolor, text) text = '\x01(F%s)%s' % (forcecolor, text)
if prefix: if prefix:
self._display_with_colors(str(prefix).decode('utf-8') + ' ') self._display_with_colors(prefix + ' ')
if text: if text:
self._display_with_colors(str(text).decode('utf-8')) self._display_with_colors(text)
if text[-1:] != '\n': if text[-1:] != '\n':
self.insertPlainText('\n') self.insertPlainText('\n')
else: else:

@ -20,7 +20,7 @@
# along with QWeeChat. If not, see <http://www.gnu.org/licenses/>. # along with QWeeChat. If not, see <http://www.gnu.org/licenses/>.
# #
import ConfigParser import configparser
import os import os
CONFIG_DIR = '%s/.qweechat' % os.getenv('HOME') CONFIG_DIR = '%s/.qweechat' % os.getenv('HOME')
@ -91,7 +91,7 @@ config_color_options = []
def read(): def read():
"""Read config file.""" """Read config file."""
global config_color_options global config_color_options
config = ConfigParser.RawConfigParser() config = configparser.RawConfigParser()
if os.path.isfile(CONFIG_FILENAME): if os.path.isfile(CONFIG_FILENAME):
config.read(CONFIG_FILENAME) config.read(CONFIG_FILENAME)
@ -123,7 +123,7 @@ def write(config):
"""Write config file.""" """Write config file."""
if not os.path.exists(CONFIG_DIR): if not os.path.exists(CONFIG_DIR):
os.mkdir(CONFIG_DIR, 0o0755) os.mkdir(CONFIG_DIR, 0o0755)
with open(CONFIG_FILENAME, 'wb') as cfg: with open(CONFIG_FILENAME, 'w') as cfg:
config.write(cfg) config.write(cfg)

@ -20,29 +20,27 @@
# along with QWeeChat. If not, see <http://www.gnu.org/licenses/>. # along with QWeeChat. If not, see <http://www.gnu.org/licenses/>.
# #
import qt_compat from PySide6 import QtGui, QtWidgets
QtGui = qt_compat.import_module('QtGui')
class ConnectionDialog(QtWidgets.QDialog):
class ConnectionDialog(QtGui.QDialog):
"""Connection window.""" """Connection window."""
def __init__(self, values, *args): def __init__(self, values, *args):
QtGui.QDialog.__init__(*(self,) + args) super().__init__(*args)
self.values = values self.values = values
self.setModal(True) self.setModal(True)
grid = QtGui.QGridLayout() grid = QtWidgets.QGridLayout()
grid.setSpacing(10) grid.setSpacing(10)
self.fields = {} self.fields = {}
for line, field in enumerate(('server', 'port', 'password', 'lines')): for line, field in enumerate(('server', 'port', 'password', 'lines')):
grid.addWidget(QtGui.QLabel(field.capitalize()), line, 0) grid.addWidget(QtWidgets.QLabel(field.capitalize()), line, 0)
line_edit = QtGui.QLineEdit() line_edit = QtWidgets.QLineEdit()
line_edit.setFixedWidth(200) line_edit.setFixedWidth(200)
if field == 'password': if field == 'password':
line_edit.setEchoMode(QtGui.QLineEdit.Password) line_edit.setEchoMode(QtWidgets.QLineEdit.Password)
if field == 'lines': if field == 'lines':
validator = QtGui.QIntValidator(0, 2147483647, self) validator = QtGui.QIntValidator(0, 2147483647, self)
line_edit.setValidator(validator) line_edit.setValidator(validator)
@ -51,14 +49,14 @@ class ConnectionDialog(QtGui.QDialog):
grid.addWidget(line_edit, line, 1) grid.addWidget(line_edit, line, 1)
self.fields[field] = line_edit self.fields[field] = line_edit
if field == 'port': if field == 'port':
ssl = QtGui.QCheckBox('SSL') ssl = QtWidgets.QCheckBox('SSL')
ssl.setChecked(self.values['ssl'] == 'on') ssl.setChecked(self.values['ssl'] == 'on')
grid.addWidget(ssl, line, 2) grid.addWidget(ssl, line, 2)
self.fields['ssl'] = ssl self.fields['ssl'] = ssl
self.dialog_buttons = QtGui.QDialogButtonBox() self.dialog_buttons = QtWidgets.QDialogButtonBox()
self.dialog_buttons.setStandardButtons( self.dialog_buttons.setStandardButtons(
QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel) QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Cancel)
self.dialog_buttons.rejected.connect(self.close) self.dialog_buttons.rejected.connect(self.close)
grid.addWidget(self.dialog_buttons, 4, 0, 1, 2) grid.addWidget(self.dialog_buttons, 4, 0, 1, 2)

@ -20,25 +20,24 @@
# along with QWeeChat. If not, see <http://www.gnu.org/licenses/>. # along with QWeeChat. If not, see <http://www.gnu.org/licenses/>.
# #
import qt_compat from qweechat.chat import ChatTextEdit
from chat import ChatTextEdit from qweechat.input import InputLineEdit
from input import InputLineEdit
QtGui = qt_compat.import_module('QtGui') from PySide6 import QtWidgets
class DebugDialog(QtGui.QDialog): class DebugDialog(QtWidgets.QDialog):
"""Debug dialog.""" """Debug dialog."""
def __init__(self, *args): def __init__(self, *args):
QtGui.QDialog.__init__(*(self,) + args) QtWidgets.QDialog.__init__(*(self,) + args)
self.resize(640, 480) self.resize(640, 480)
self.setWindowTitle('Debug console') self.setWindowTitle('Debug console')
self.chat = ChatTextEdit(debug=True) self.chat = ChatTextEdit(debug=True)
self.input = InputLineEdit(self.chat) self.input = InputLineEdit(self.chat)
vbox = QtGui.QVBoxLayout() vbox = QtWidgets.QVBoxLayout()
vbox.addWidget(self.chat) vbox.addWidget(self.chat)
vbox.addWidget(self.input) vbox.addWidget(self.input)

@ -20,21 +20,19 @@
# along with QWeeChat. If not, see <http://www.gnu.org/licenses/>. # along with QWeeChat. If not, see <http://www.gnu.org/licenses/>.
# #
import qt_compat from PySide6 import QtCore
from PySide6 import QtWidgets
QtCore = qt_compat.import_module('QtCore')
QtGui = qt_compat.import_module('QtGui')
class InputLineEdit(QtWidgets.QLineEdit):
class InputLineEdit(QtGui.QLineEdit):
"""Input line.""" """Input line."""
bufferSwitchPrev = qt_compat.Signal() bufferSwitchPrev = QtCore.Signal()
bufferSwitchNext = qt_compat.Signal() bufferSwitchNext = QtCore.Signal()
textSent = qt_compat.Signal(str) textSent = QtCore.Signal(str)
def __init__(self, scroll_widget): def __init__(self, scroll_widget):
QtGui.QLineEdit.__init__(self) super().__init__()
self.scroll_widget = scroll_widget self.scroll_widget = scroll_widget
self._history = [] self._history = []
self._history_index = -1 self._history_index = -1
@ -50,7 +48,7 @@ class InputLineEdit(QtGui.QLineEdit):
elif key == QtCore.Qt.Key_PageDown: elif key == QtCore.Qt.Key_PageDown:
self.bufferSwitchNext.emit() self.bufferSwitchNext.emit()
else: else:
QtGui.QLineEdit.keyPressEvent(self, event) QtWidgets.QLineEdit.keyPressEvent(self, event)
elif modifiers == QtCore.Qt.AltModifier: elif modifiers == QtCore.Qt.AltModifier:
if key in (QtCore.Qt.Key_Left, QtCore.Qt.Key_Up): if key in (QtCore.Qt.Key_Left, QtCore.Qt.Key_Up):
self.bufferSwitchPrev.emit() self.bufferSwitchPrev.emit()
@ -65,7 +63,7 @@ class InputLineEdit(QtGui.QLineEdit):
elif key == QtCore.Qt.Key_End: elif key == QtCore.Qt.Key_End:
bar.setValue(bar.maximum()) bar.setValue(bar.maximum())
else: else:
QtGui.QLineEdit.keyPressEvent(self, event) QtWidgets.QLineEdit.keyPressEvent(self, event)
elif key == QtCore.Qt.Key_PageUp: elif key == QtCore.Qt.Key_PageUp:
bar.setValue(bar.value() - bar.pageStep()) bar.setValue(bar.value() - bar.pageStep())
elif key == QtCore.Qt.Key_PageDown: elif key == QtCore.Qt.Key_PageDown:
@ -75,10 +73,10 @@ class InputLineEdit(QtGui.QLineEdit):
elif key == QtCore.Qt.Key_Down: elif key == QtCore.Qt.Key_Down:
self._history_navigate(1) self._history_navigate(1)
else: else:
QtGui.QLineEdit.keyPressEvent(self, event) QtWidgets.QLineEdit.keyPressEvent(self, event)
def _input_return_pressed(self): def _input_return_pressed(self):
self._history.append(self.text().encode('utf-8')) self._history.append(self.text())
self._history_index = len(self._history) self._history_index = len(self._history)
self.textSent.emit(self.text()) self.textSent.emit(self.text())
self.clear() self.clear()

@ -21,11 +21,11 @@
# #
import struct import struct
import qt_compat
import config
QtCore = qt_compat.import_module('QtCore') from PySide6 import QtCore, QtNetwork
QtNetwork = qt_compat.import_module('QtNetwork')
from qweechat import config
_PROTO_INIT_CMD = ['init password=%(password)s'] _PROTO_INIT_CMD = ['init password=%(password)s']
@ -47,11 +47,11 @@ _PROTO_SYNC_CMDS = [
class Network(QtCore.QObject): class Network(QtCore.QObject):
"""I/O with WeeChat/relay.""" """I/O with WeeChat/relay."""
statusChanged = qt_compat.Signal(str, str) statusChanged = QtCore.Signal(str, str)
messageFromWeechat = qt_compat.Signal(QtCore.QByteArray) messageFromWeechat = QtCore.Signal(QtCore.QByteArray)
def __init__(self, *args): def __init__(self, *args):
QtCore.QObject.__init__(*(self,) + args) super().__init__(*args)
self.status_disconnected = 'disconnected' self.status_disconnected = 'disconnected'
self.status_connecting = 'connecting...' self.status_connecting = 'connecting...'
self.status_connected = 'connected' self.status_connected = 'connected'
@ -63,7 +63,7 @@ class Network(QtCore.QObject):
self._buffer = QtCore.QByteArray() self._buffer = QtCore.QByteArray()
self._socket = QtNetwork.QSslSocket() self._socket = QtNetwork.QSslSocket()
self._socket.connected.connect(self._socket_connected) self._socket.connected.connect(self._socket_connected)
self._socket.error.connect(self._socket_error) # self._socket.error.connect(self._socket_error)
self._socket.readyRead.connect(self._socket_read) self._socket.readyRead.connect(self._socket_read)
self._socket.disconnected.connect(self._socket_disconnected) self._socket.disconnected.connect(self._socket_disconnected)
@ -87,7 +87,7 @@ class Network(QtCore.QObject):
self._buffer.append(data) self._buffer.append(data)
while len(self._buffer) >= 4: while len(self._buffer) >= 4:
remainder = None remainder = None
length = struct.unpack('>i', self._buffer[0:4])[0] length = struct.unpack('>i', self._buffer[0:4].data())[0]
if len(self._buffer) < length: if len(self._buffer) < length:
# partial message, just wait for end of message # partial message, just wait for end of message
break break
@ -108,7 +108,7 @@ class Network(QtCore.QObject):
self._server = None self._server = None
self._port = None self._port = None
self._ssl = None self._ssl = None
self._password = None self._password = ""
self.statusChanged.emit(self.status_disconnected, None) self.statusChanged.emit(self.status_disconnected, None)
def is_connected(self): def is_connected(self):
@ -136,11 +136,12 @@ class Network(QtCore.QObject):
return return
if self._socket.state() != QtNetwork.QAbstractSocket.UnconnectedState: if self._socket.state() != QtNetwork.QAbstractSocket.UnconnectedState:
self._socket.abort() self._socket.abort()
self._socket.connectToHost(self._server, self._port)
if self._ssl: if self._ssl:
self._socket.ignoreSslErrors() self._socket.ignoreSslErrors()
self._socket.startClientEncryption() self._socket.connectToHostEncrypted(self._server, self._port)
self.statusChanged.emit(self.status_connecting, None) else:
self._socket.connectToHost(self._server, self._port)
self.statusChanged.emit(self.status_connecting, "")
def disconnect_weechat(self): def disconnect_weechat(self):
"""Disconnect from WeeChat.""" """Disconnect from WeeChat."""

@ -1,55 +0,0 @@
# -*- coding: utf-8 -*-
#
# File downloaded from:
# https://github.com/epage/PythonUtils/blob/master/util/qt_compat.py
# Author: epage
# License: LGPL 2.1
#
from __future__ import with_statement
from __future__ import division
_TRY_PYSIDE = True
uses_pyside = False
try:
if not _TRY_PYSIDE:
raise ImportError()
import PySide.QtCore as _QtCore
QtCore = _QtCore
uses_pyside = True
except ImportError:
import sip
sip.setapi('QString', 2)
sip.setapi('QVariant', 2)
import PyQt4.QtCore as _QtCore
QtCore = _QtCore
uses_pyside = False
def _pyside_import_module(moduleName):
pyside = __import__('PySide', globals(), locals(), [moduleName], -1)
return getattr(pyside, moduleName)
def _pyqt4_import_module(moduleName):
pyside = __import__('PyQt4', globals(), locals(), [moduleName], -1)
return getattr(pyside, moduleName)
if uses_pyside:
import_module = _pyside_import_module
Signal = QtCore.Signal
Slot = QtCore.Slot
Property = QtCore.Property
else:
import_module = _pyqt4_import_module
Signal = QtCore.pyqtSignal
Slot = QtCore.pyqtSlot
Property = QtCore.pyqtProperty
if __name__ == "__main__":
pass

@ -36,18 +36,18 @@ It requires requires WeeChat 0.3.7 or newer, running on local or remote host.
import sys import sys
import traceback import traceback
from pkg_resources import resource_filename from pkg_resources import resource_filename
import qt_compat
import config from PySide6 import QtGui, QtWidgets, QtCore
import weechat.protocol as protocol
from network import Network from qweechat import config
from connection import ConnectionDialog from qweechat.weechat import protocol
from buffer import BufferListWidget, Buffer from qweechat.network import Network
from debug import DebugDialog from qweechat.connection import ConnectionDialog
from about import AboutDialog from qweechat.buffer import BufferListWidget, Buffer
from version import qweechat_version from qweechat.debug import DebugDialog
from qweechat.about import AboutDialog
QtCore = qt_compat.import_module('QtCore') from qweechat.version import qweechat_version
QtGui = qt_compat.import_module('QtGui')
NAME = 'QWeeChat' NAME = 'QWeeChat'
AUTHOR = 'Sébastien Helleu' AUTHOR = 'Sébastien Helleu'
@ -58,11 +58,11 @@ WEECHAT_SITE = 'https://weechat.org/'
DEBUG_NUM_LINES = 50 DEBUG_NUM_LINES = 50
class MainWindow(QtGui.QMainWindow): class MainWindow(QtWidgets.QMainWindow):
"""Main window.""" """Main window."""
def __init__(self, *args): def __init__(self, *args):
QtGui.QMainWindow.__init__(*(self,) + args) super().__init__()
self.config = config.read() self.config = config.read()
@ -87,11 +87,11 @@ class MainWindow(QtGui.QMainWindow):
# default buffer # default buffer
self.buffers = [Buffer()] self.buffers = [Buffer()]
self.stacked_buffers = QtGui.QStackedWidget() self.stacked_buffers = QtWidgets.QStackedWidget()
self.stacked_buffers.addWidget(self.buffers[0].widget) self.stacked_buffers.addWidget(self.buffers[0].widget)
# splitter with buffers + chat/input # splitter with buffers + chat/input
splitter = QtGui.QSplitter() splitter = QtWidgets.QSplitter()
splitter.addWidget(self.list_buffers) splitter.addWidget(self.list_buffers)
splitter.addWidget(self.stacked_buffers) splitter.addWidget(self.stacked_buffers)
@ -146,7 +146,7 @@ class MainWindow(QtGui.QMainWindow):
menu_window.addAction(self.actions['debug']) menu_window.addAction(self.actions['debug'])
menu_help = self.menu.addMenu('&Help') menu_help = self.menu.addMenu('&Help')
menu_help.addAction(self.actions['about']) menu_help.addAction(self.actions['about'])
self.network_status = QtGui.QLabel() self.network_status = QtWidgets.QLabel()
self.network_status.setFixedHeight(20) self.network_status.setFixedHeight(20)
self.network_status.setFixedWidth(200) self.network_status.setFixedWidth(200)
self.network_status.setContentsMargins(0, 0, 10, 0) self.network_status.setContentsMargins(0, 0, 10, 0)
@ -249,8 +249,7 @@ class MainWindow(QtGui.QMainWindow):
'&copy; 2011-2020 %s &lt;<a href="mailto:%s">%s</a>&gt;' '&copy; 2011-2020 %s &lt;<a href="mailto:%s">%s</a>&gt;'
% (AUTHOR, AUTHOR_MAIL, AUTHOR_MAIL), % (AUTHOR, AUTHOR_MAIL, AUTHOR_MAIL),
'', '',
'Running with %s' % ('PySide' if qt_compat.uses_pyside 'Running with PySide6',
else 'PyQt4'),
'', '',
'WeeChat site: <a href="%s">%s</a>' 'WeeChat site: <a href="%s">%s</a>'
% (WEECHAT_SITE, WEECHAT_SITE), % (WEECHAT_SITE, WEECHAT_SITE),
@ -315,11 +314,11 @@ class MainWindow(QtGui.QMainWindow):
self.debug_display(0, '==>', self.debug_display(0, '==>',
'message (%d bytes):\n%s' 'message (%d bytes):\n%s'
% (len(message), % (len(message),
protocol.hex_and_ascii(message, 20)), protocol.hex_and_ascii(message.data(), 20)),
forcecolor='#008800') forcecolor='#008800')
try: try:
proto = protocol.Protocol() proto = protocol.Protocol()
message = proto.decode(str(message)) message = proto.decode(message.data())
if message.uncompressed: if message.uncompressed:
self.debug_display( self.debug_display(
0, '==>', 0, '==>',
@ -329,7 +328,7 @@ class MainWindow(QtGui.QMainWindow):
forcecolor='#008800') forcecolor='#008800')
self.debug_display(0, '', 'Message: %s' % message) self.debug_display(0, '', 'Message: %s' % message)
self.parse_message(message) self.parse_message(message)
except: # noqa: E722 except Exception: # noqa: E722
print('Error while decoding message from WeeChat:\n%s' print('Error while decoding message from WeeChat:\n%s'
% traceback.format_exc()) % traceback.format_exc())
self.network.disconnect_weechat() self.network.disconnect_weechat()
@ -495,6 +494,8 @@ class MainWindow(QtGui.QMainWindow):
self.network.desync_weechat() self.network.desync_weechat()
elif message.msgid == '_upgrade_ended': elif message.msgid == '_upgrade_ended':
self.network.sync_weechat() self.network.sync_weechat()
else:
print(f"Unknown message with id {message.msgid}")
def create_buffer(self, item): def create_buffer(self, item):
"""Create a new buffer.""" """Create a new buffer."""
@ -509,9 +510,8 @@ class MainWindow(QtGui.QMainWindow):
def insert_buffer(self, index, buf): def insert_buffer(self, index, buf):
"""Insert a buffer in list.""" """Insert a buffer in list."""
self.buffers.insert(index, buf) self.buffers.insert(index, buf)
self.list_buffers.insertItem(index, '%d. %s' self.list_buffers.insertItem(index, '%s'
% (buf.data['number'], % (buf.data['local_variables']['name']))
buf.data['full_name'].decode('utf-8')))
self.stacked_buffers.insertWidget(index, buf.widget) self.stacked_buffers.insertWidget(index, buf.widget)
def remove_buffer(self, index): def remove_buffer(self, index):
@ -544,12 +544,18 @@ class MainWindow(QtGui.QMainWindow):
if self.debug_dialog: if self.debug_dialog:
self.debug_dialog.close() self.debug_dialog.close()
config.write(self.config) config.write(self.config)
QtGui.QMainWindow.closeEvent(self, event) QtWidgets.QMainWindow.closeEvent(self, event)
def main():
app = QtWidgets.QApplication(sys.argv)
app.setStyle(QtWidgets.QStyleFactory.create('Cleanlooks'))
app.setWindowIcon(QtGui.QIcon(
resource_filename(__name__, 'data/icons/weechat.png')))
main = MainWindow()
main.show()
sys.exit(app.exec_())
app = QtGui.QApplication(sys.argv) if __name__ == '__main__':
app.setStyle(QtGui.QStyleFactory.create('Cleanlooks')) main()
app.setWindowIcon(QtGui.QIcon(
resource_filename(__name__, 'data/icons/weechat.png')))
main = MainWindow()
sys.exit(app.exec_())

@ -21,6 +21,7 @@
# #
import re import re
import logging
RE_COLOR_ATTRS = r'[*!/_|]*' RE_COLOR_ATTRS = r'[*!/_|]*'
RE_COLOR_STD = r'(?:%s\d{2})' % RE_COLOR_ATTRS RE_COLOR_STD = r'(?:%s\d{2})' % RE_COLOR_ATTRS
@ -75,6 +76,9 @@ WEECHAT_BASIC_COLORS = (
('white', 0)) ('white', 0))
log = logging.getLogger(__name__)
class Color(): class Color():
def __init__(self, color_options, debug=False): def __init__(self, color_options, debug=False):
self.color_options = color_options self.color_options = color_options
@ -92,7 +96,7 @@ class Color():
index = int(color) index = int(color)
return '\x01(Fr%s)' % self.color_options[index] return '\x01(Fr%s)' % self.color_options[index]
except: # noqa: E722 except: # noqa: E722
print('Error decoding WeeChat color "%s"' % color) log.debug('Error decoding WeeChat color "%s"' % color)
return '' return ''
def _convert_terminal_color(self, fg_bg, attrs, color): def _convert_terminal_color(self, fg_bg, attrs, color):
@ -100,7 +104,7 @@ class Color():
index = int(color) index = int(color)
return '\x01(%s%s#%s)' % (fg_bg, attrs, self._rgb_color(index)) return '\x01(%s%s#%s)' % (fg_bg, attrs, self._rgb_color(index))
except: # noqa: E722 except: # noqa: E722
print('Error decoding terminal color "%s"' % color) log.debug('Error decoding terminal color "%s"' % color)
return '' return ''
def _convert_color_attr(self, fg_bg, color): def _convert_color_attr(self, fg_bg, color):
@ -123,7 +127,7 @@ class Color():
return self._convert_terminal_color(fg_bg, attrs, return self._convert_terminal_color(fg_bg, attrs,
WEECHAT_BASIC_COLORS[index][1]) WEECHAT_BASIC_COLORS[index][1])
except: # noqa: E722 except: # noqa: E722
print('Error decoding color "%s"' % color) log.debug('Error decoding color "%s"' % color)
return '' return ''
def _attrcode_to_char(self, code): def _attrcode_to_char(self, code):

@ -34,15 +34,11 @@ import collections
import struct import struct
import zlib import zlib
if hasattr(collections, 'OrderedDict'):
# python >= 2.7 class WeechatDict(collections.OrderedDict):
class WeechatDict(collections.OrderedDict): def __str__(self):
def __str__(self): return '{%s}' % ', '.join(
return '{%s}' % ', '.join( ['%s: %s' % (repr(key), repr(self[key])) for key in self])
['%s: %s' % (repr(key), repr(self[key])) for key in self])
else:
# python <= 2.6
WeechatDict = dict
class WeechatObject: class WeechatObject:
@ -151,7 +147,7 @@ class Protocol:
if len(self.data) < 3: if len(self.data) < 3:
self.data = '' self.data = ''
return '' return ''
objtype = str(self.data[0:3]) objtype = self.data[0:3].decode()
self.data = self.data[3:] self.data = self.data[3:]
return objtype return objtype
@ -196,14 +192,14 @@ class Protocol:
value = self._obj_len_data(1) value = self._obj_len_data(1)
if value is None: if value is None:
return None return None
return int(str(value)) return int(value)
def _obj_str(self): def _obj_str(self):
"""Read a string in data (length on 4 bytes + content).""" """Read a string in data (length on 4 bytes + content)."""
value = self._obj_len_data(4) value = self._obj_len_data(4)
if value is None: if value in ("", None):
return None return ""
return str(value) return value.decode()
def _obj_buffer(self): def _obj_buffer(self):
"""Read a buffer in data (length on 4 bytes + data).""" """Read a buffer in data (length on 4 bytes + data)."""
@ -214,14 +210,14 @@ class Protocol:
value = self._obj_len_data(1) value = self._obj_len_data(1)
if value is None: if value is None:
return None return None
return '0x%s' % str(value) return '0x%s' % value
def _obj_time(self): def _obj_time(self):
"""Read a time in data (length on 1 byte + value as string).""" """Read a time in data (length on 1 byte + value as string)."""
value = self._obj_len_data(1) value = self._obj_len_data(1)
if value is None: if value is None:
return None return None
return int(str(value)) return int(value)
def _obj_hashtable(self): def _obj_hashtable(self):
""" """
@ -314,8 +310,8 @@ class Protocol:
if compression: if compression:
uncompressed = zlib.decompress(self.data[5:]) uncompressed = zlib.decompress(self.data[5:])
size_uncompressed = len(uncompressed) + 5 size_uncompressed = len(uncompressed) + 5
uncompressed = '%s%s%s' % (struct.pack('>i', size_uncompressed), uncompressed = b'%s%s%s' % (struct.pack('>i', size_uncompressed),
struct.pack('b', 0), uncompressed) struct.pack('b', 0), uncompressed)
self.data = uncompressed self.data = uncompressed
else: else:
uncompressed = self.data[:] uncompressed = self.data[:]
@ -344,13 +340,20 @@ def hex_and_ascii(data, bytes_per_line=10):
for i in range(num_lines): for i in range(num_lines):
str_hex = [] str_hex = []
str_ascii = [] str_ascii = []
for char in data[i*bytes_per_line:(i*bytes_per_line)+bytes_per_line]: for j in range(bytes_per_line):
# We can't easily iterate over individual bytes, so we are going to
# do it this way.
index = (i*bytes_per_line) + j
char = data[index:index+1]
if not char:
char = b'x'
byte = struct.unpack('B', char)[0] byte = struct.unpack('B', char)[0]
str_hex.append('%02X' % int(byte)) str_hex.append(b'%02X' % int(byte))
if byte >= 32 and byte <= 127: if byte >= 32 and byte <= 127:
str_ascii.append(char) str_ascii.append(char)
else: else:
str_ascii.append('.') str_ascii.append(b'.')
fmt = '%%-%ds %%s' % ((bytes_per_line * 3) - 1) fmt = b'%%-%ds %%s' % ((bytes_per_line * 3) - 1)
lines.append(fmt % (' '.join(str_hex), ''.join(str_ascii))) lines.append(fmt % (b' '.join(str_hex),
return '\n'.join(lines) b''.join(str_ascii)))
return b'\n'.join(lines)

@ -24,8 +24,6 @@
Command-line program for testing WeeChat/relay protocol. Command-line program for testing WeeChat/relay protocol.
""" """
from __future__ import print_function
import argparse import argparse
import os import os
import select import select
@ -36,8 +34,9 @@ import sys
import time import time
import traceback import traceback
import protocol # WeeChat/relay protocol from qweechat.weechat import protocol
from .. version import qweechat_version
qweechat_version = '0.1'
NAME = 'qweechat-testproto' NAME = 'qweechat-testproto'
@ -61,12 +60,13 @@ class TestProto(object):
try: try:
self.sock = socket.socket(inet, socket.SOCK_STREAM) self.sock = socket.socket(inet, socket.SOCK_STREAM)
self.sock.connect((self.args.hostname, self.args.port)) self.sock.connect((self.args.hostname, self.args.port))
except: # noqa: E722 except Exception:
if self.sock: if self.sock:
self.sock.close() self.sock.close()
print('Failed to connect to', self.address) print('Failed to connect to', self.address)
return False return False
print('Connected to', self.address)
print(f'Connected to {self.address} socket {self.sock}')
return True return True
def send(self, messages): def send(self, messages):
@ -75,11 +75,12 @@ class TestProto(object):
Return True if OK, False if error. Return True if OK, False if error.
""" """
try: try:
for msg in messages.split('\n'): for msg in messages.split(b'\n'):
if msg == 'quit': if msg == b'quit':
self.has_quit = True self.has_quit = True
self.sock.sendall(msg + '\n') self.sock.sendall(msg + b'\n')
print('\x1b[33m<-- ' + msg + '\x1b[0m') sys.stdout.write(
(b'\x1b[33m<-- ' + msg + b'\x1b[0m\n').decode())
except: # noqa: E722 except: # noqa: E722
traceback.print_exc() traceback.print_exc()
print('Failed to send message') print('Failed to send message')
@ -94,7 +95,7 @@ class TestProto(object):
try: try:
proto = protocol.Protocol() proto = protocol.Protocol()
msgd = proto.decode(message, msgd = proto.decode(message,
separator='\n' if self.args.debug > 0 separator=b'\n' if self.args.debug > 0
else ', ') else ', ')
print('') print('')
if self.args.debug >= 2 and msgd.uncompressed: if self.args.debug >= 2 and msgd.uncompressed:
@ -122,7 +123,7 @@ class TestProto(object):
data = os.read(sys.stdin.fileno(), 4096) data = os.read(sys.stdin.fileno(), 4096)
if data: if data:
if not self.send(data.strip()): if not self.send(data.strip()):
# self.sock.close() self.sock.close()
return False return False
# open stdin to read user commands # open stdin to read user commands
sys.stdin = open('/dev/tty') sys.stdin = open('/dev/tty')
@ -136,10 +137,10 @@ class TestProto(object):
""" """
if self.has_quit: if self.has_quit:
return 0 return 0
message = '' message = b''
recvbuf = '' recvbuf = b''
prompt = '\x1b[36mrelay> \x1b[0m' prompt = b'\x1b[36mrelay> \x1b[0m'
sys.stdout.write(prompt) sys.stdout.write(prompt.decode())
sys.stdout.flush() sys.stdout.flush()
try: try:
while not self.has_quit: while not self.has_quit:
@ -149,13 +150,14 @@ class TestProto(object):
buf = os.read(_file.fileno(), 4096) buf = os.read(_file.fileno(), 4096)
if buf: if buf:
message += buf message += buf
if '\n' in message: if b'\n' in message:
messages = message.split('\n') messages = message.split(b'\n')
msgsent = '\n'.join(messages[:-1]) msgsent = b'\n'.join(messages[:-1])
if msgsent and not self.send(msgsent): if msgsent and not self.send(msgsent):
return 4 return 4
message = messages[-1] message = messages[-1]
sys.stdout.write(prompt + message) sys.stdout.write((prompt + message).decode())
# sys.stdout.write(prompt + message)
sys.stdout.flush() sys.stdout.flush()
else: else:
buf = _file.recv(4096) buf = _file.recv(4096)
@ -178,12 +180,12 @@ class TestProto(object):
if remainder: if remainder:
recvbuf = remainder recvbuf = remainder
else: else:
recvbuf = '' recvbuf = b''
sys.stdout.write(prompt + message) sys.stdout.write((prompt + message).decode())
sys.stdout.flush() sys.stdout.flush()
except: # noqa: E722 except: # noqa: E722
traceback.print_exc() traceback.print_exc()
self.send('quit') self.send(b'quit')
return 0 return 0
def __del__(self): def __del__(self):
@ -220,7 +222,7 @@ The script returns:
help='debug mode: long objects view ' help='debug mode: long objects view '
'(-dd: display raw messages)') '(-dd: display raw messages)')
parser.add_argument('-v', '--version', action='version', parser.add_argument('-v', '--version', action='version',
version=qweechat_version()) version=qweechat_version)
parser.add_argument('hostname', parser.add_argument('hostname',
help='hostname (or IP address) of machine running ' help='hostname (or IP address) of machine running '
'WeeChat/relay') 'WeeChat/relay')

@ -54,5 +54,8 @@ setup(
'console_scripts': [ 'console_scripts': [
'qweechat-testproto = qweechat.weechat.testproto:main', 'qweechat-testproto = qweechat.weechat.testproto:main',
] ]
} },
install_requires = [
'PySide6',
]
) )

Loading…
Cancel
Save