Compare commits
24 Commits
3b0947c9ef
...
master
Author | SHA1 | Date | |
---|---|---|---|
fcee502eb9 | |||
1e4097880a | |||
55e7da35f4 | |||
5e99216cf2 | |||
1555e20459 | |||
9390c9bb91 | |||
1a71f78521 | |||
a23eb74aa8 | |||
38d82dfc82 | |||
60c48bcb6a | |||
c7580c3a35 | |||
e29185c293 | |||
acb9e83c29 | |||
b54c0d1716 | |||
b1f0ad4bd8 | |||
ba73b0ac36 | |||
82e0d92056 | |||
001244e9ab | |||
99fe78515c | |||
2a814055fe | |||
0dffebcf24 | |||
0feac51b53 | |||
ae648fd7f6 | |||
c05ab0e534 |
1
.github/workflows/ci.yml
vendored
@ -14,6 +14,7 @@ jobs:
|
|||||||
- "3.7"
|
- "3.7"
|
||||||
- "3.8"
|
- "3.8"
|
||||||
- "3.9"
|
- "3.9"
|
||||||
|
- "3.10"
|
||||||
|
|
||||||
name: Python ${{ matrix.python-version }}
|
name: Python ${{ matrix.python-version }}
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
|
7
.rsync.sh
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# find * -name \*.py | xargs grep -l '[ ]*$' | xargs sed -i -e 's/[ ]*$//'
|
||||||
|
rsync "$@" -vax --include \*.py --exclude \*.log --exclude \*.out \
|
||||||
|
--exclude \*.egg-info --exclude libs --exclude dist --exclude build \
|
||||||
|
--exclude \*.pyc --exclude .pyl\* --exclude \*~ --exclude \*.so \
|
||||||
|
./ ../qweechat.git/|grep -v /$
|
4
AUTHORS
@ -20,5 +20,5 @@ Alphabetically:
|
|||||||
|
|
||||||
Developers are connected to IRC:
|
Developers are connected to IRC:
|
||||||
|
|
||||||
* server: 'irc.freenode.net'
|
* server: irc.libera.chat
|
||||||
* channels: '#weechat' (English) and '#weechat-fr' (French)
|
* channels: #weechat (English) and #weechat-fr (French)
|
||||||
|
43
Makefile
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (C) 2021 Sébastien Helleu <flashcode@flashtux.org>
|
# Copyright (C) 2021-2022 Sébastien Helleu <flashcode@flashtux.org>
|
||||||
#
|
#
|
||||||
# This file is part of QWeeChat, a Qt remote GUI for WeeChat.
|
# This file is part of QWeeChat, a Qt remote GUI for WeeChat.
|
||||||
#
|
#
|
||||||
@ -16,12 +16,23 @@
|
|||||||
# 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/>.
|
||||||
#
|
#
|
||||||
|
# to run the tests, run make PASS=controllerpassword test
|
||||||
|
|
||||||
|
PREFIX=/usr/local
|
||||||
|
PYTHON_EXE_MSYS=${PREFIX}/bin/python3.sh
|
||||||
|
PIP_EXE_MSYS=${PREFIX}/bin/pip3.sh
|
||||||
|
LOCAL_DOCTEST=${PREFIX}/bin/toxcore_run_doctest3.bash
|
||||||
|
DOCTEST=${LOCAL_DOCTEST}
|
||||||
|
PYTHON_MINOR=`python3 --version 2>&1 | sed -e 's@^.* @@' -e 's@\.[0-9]*$$@@'`
|
||||||
|
MOD=qweechat
|
||||||
|
|
||||||
all: check
|
all: check
|
||||||
|
|
||||||
check: lint test
|
check::
|
||||||
|
sh ${PYTHON_EXE_MSYS} -c "import ${MOD}"
|
||||||
|
sh ${PYTHON_EXE_MSYS} -c "from ${MOD} import config"
|
||||||
|
|
||||||
lint: flake8 pylint bandit
|
lint:: flake8 pylint bandit
|
||||||
|
|
||||||
flake8:
|
flake8:
|
||||||
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
|
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
|
||||||
@ -32,3 +43,29 @@ pylint:
|
|||||||
|
|
||||||
bandit:
|
bandit:
|
||||||
bandit -r qweechat
|
bandit -r qweechat
|
||||||
|
|
||||||
|
rsync::
|
||||||
|
bash .rsync.sh
|
||||||
|
|
||||||
|
install:: install-pip
|
||||||
|
|
||||||
|
install-setup::
|
||||||
|
# deprecated
|
||||||
|
${PYTHON_EXE_MSYS} -W ignore::DeprecationWarning \
|
||||||
|
setup.py install \
|
||||||
|
--prefix ${PREFIX}
|
||||||
|
|
||||||
|
install-pip::
|
||||||
|
# we install --nodeps because pip is installing stuff we already have in the OS
|
||||||
|
${PIP_EXE_MSYS} --python ${PYTHON_EXE_MSYS} install \
|
||||||
|
--no-deps \
|
||||||
|
--target ${PREFIX}/lib/python${PYTHON_MINOR}/site-packages/ \
|
||||||
|
--upgrade .
|
||||||
|
sed -i -e "1s@/usr/bin/python${PYTHON_MINOR}@${PYTHON_EXE_MSYS}@" \
|
||||||
|
${PREFIX}/lib/python${PYTHON_MINOR}/site-packages/bin/*
|
||||||
|
|
||||||
|
veryclean:: clean
|
||||||
|
rm -rf build dist $(MOD).egg-info
|
||||||
|
|
||||||
|
clean::
|
||||||
|
find . -type f -name \*~ -delete
|
||||||
|
38
README.md
@ -20,29 +20,41 @@ Homepage: https://weechat.org/
|
|||||||
QWeeChat requires:
|
QWeeChat requires:
|
||||||
|
|
||||||
- Python ≥ 3.7
|
- Python ≥ 3.7
|
||||||
- [PySide6](https://pypi.org/project/PySide6/)
|
- PyQt5 or PyQt5 and maybe PySide2 and PySide2 using qtpy
|
||||||
- [WeeChat](https://weechat.org) ≥ 0.3.7, on local or remote machine, with relay plugin enabled and listening on a port with protocol "weechat"
|
- dev-python/qtconsole; the requirements.txt file says >= 5.5.1 but earlier versions may work just fine
|
||||||
|
- [WeeChat](https://weechat.org) ≥ 0.3.7, on local or remote machine, with relay plugin enabled and listening on a port with protocol "weechat" Tested with 4.1.2
|
||||||
|
|
||||||
|
|
||||||
### Install via source distribution
|
### Install via source distribution
|
||||||
|
|
||||||
|
Look at the Makefile and customize the variables; then
|
||||||
```
|
```
|
||||||
$ pip install .
|
$ make install
|
||||||
```
|
```
|
||||||
|
|
||||||
## WeeChat setup
|
## WeeChat setup
|
||||||
|
|
||||||
You have to add a relay port in WeeChat, for example on port 1234:
|
You have to add a relay port in WeeChat.
|
||||||
|
Follow the normal instructions for adding a ```relay``` to
|
||||||
|
[weechat](https://github.com/weechat/weechat)
|
||||||
```
|
```
|
||||||
/set relay.network.password "mypass"
|
/relay add weechat 9000
|
||||||
/relay add weechat 1234
|
/relay start weechat
|
||||||
|
```
|
||||||
|
or
|
||||||
|
```
|
||||||
|
weechat -r '/relay add weechat 9000;/relay start weechat'
|
||||||
|
```
|
||||||
|
and run qweechat either under toxygen, or standalone
|
||||||
|
```
|
||||||
|
python3 -m qweechat
|
||||||
```
|
```
|
||||||
|
|
||||||
## Connect to WeeChat
|
## Connect to WeeChat
|
||||||
|
|
||||||
In QWeeChat, click on connect and enter fields:
|
In QWeeChat, click on connect and enter fields:
|
||||||
|
|
||||||
- `server`: the IP address or hostname of your machine with WeeChat running
|
- `hostname`: the IP address or hostname of your machine with WeeChat running
|
||||||
- `port`: the relay port (defined in WeeChat)
|
- `port`: the relay port (defined in WeeChat)
|
||||||
- `password`: the relay password (defined in WeeChat)
|
- `password`: the relay password (defined in WeeChat)
|
||||||
- `totp`: the Time-Based One-Time Password (optional, to set if required by WeeChat)
|
- `totp`: the Time-Based One-Time Password (optional, to set if required by WeeChat)
|
||||||
@ -51,7 +63,7 @@ Options can be changed in file `~/.config/qweechat/qweechat.conf`.
|
|||||||
|
|
||||||
## Copyright
|
## Copyright
|
||||||
|
|
||||||
Copyright © 2011-2021 [Sébastien Helleu](https://github.com/flashcode)
|
Copyright © 2011-2022 [Sébastien Helleu](https://github.com/flashcode)
|
||||||
|
|
||||||
This file is part of QWeeChat, a Qt remote GUI for WeeChat.
|
This file is part of QWeeChat, a Qt remote GUI for WeeChat.
|
||||||
|
|
||||||
@ -67,3 +79,11 @@ GNU General Public License for more details.
|
|||||||
|
|
||||||
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 <https://www.gnu.org/licenses/>.
|
along with QWeeChat. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
## Updates
|
||||||
|
|
||||||
|
Up-to-date code is on https://git.plastiras.org/emdee/qweechat
|
||||||
|
You can also run this qweechat under toxygen
|
||||||
|
https://git.macaw.me/emdee/toxygen
|
||||||
|
|
||||||
|
For tox in weechat, see https://git.macaw.me/emdee/tox-weechat
|
||||||
|
52
pyproject.toml
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
[project]
|
||||||
|
name = 'qweechat'
|
||||||
|
requires-python = ">= 3.7"
|
||||||
|
description = "qtpy channel for qweechat"
|
||||||
|
keywords = ["qt", "console", "weechat"]
|
||||||
|
classifiers = [
|
||||||
|
# How mature is this project? Common values are
|
||||||
|
# 3 - Alpha
|
||||||
|
# 4 - Beta
|
||||||
|
# 5 - Production/Stable
|
||||||
|
"Development Status :: 4 - Beta",
|
||||||
|
|
||||||
|
# Indicate who your project is intended for
|
||||||
|
"Intended Audience :: Developers",
|
||||||
|
|
||||||
|
# Specify the Python versions you support here.
|
||||||
|
"Programming Language :: Python :: 3",
|
||||||
|
"License :: OSI Approved",
|
||||||
|
"Operating System :: POSIX :: BSD :: FreeBSD",
|
||||||
|
"Operating System :: POSIX :: Linux",
|
||||||
|
"Programming Language :: Python :: 3 :: Only",
|
||||||
|
"Programming Language :: Python :: 3.7",
|
||||||
|
"Programming Language :: Python :: 3.8",
|
||||||
|
"Programming Language :: Python :: 3.9",
|
||||||
|
"Programming Language :: Python :: 3.10",
|
||||||
|
"Programming Language :: Python :: 3.11",
|
||||||
|
"Programming Language :: Python :: Implementation :: CPython",
|
||||||
|
]
|
||||||
|
dynamic = ["version", "readme", "dependencies"] # cannot be dynamic ['license']
|
||||||
|
|
||||||
|
[project.license]
|
||||||
|
file = "COPYING"
|
||||||
|
|
||||||
|
[project.urls]
|
||||||
|
repository = "https://git.plastiras.org/emdee/qweechat"
|
||||||
|
homepage = "https://git.plastiras.org/emdee/qweechat"
|
||||||
|
|
||||||
|
[build-system]
|
||||||
|
requires = ["setuptools>=40.8.0", "wheel"]
|
||||||
|
build-backend = "setuptools.build_meta"
|
||||||
|
# backend = "setuptools.build_meta:__legacy__"
|
||||||
|
|
||||||
|
[tool.setuptools.dynamic]
|
||||||
|
version = {attr = "qweechat.version.VERSION"}
|
||||||
|
readme = {file = ["README.md"]}
|
||||||
|
dependencies = {file = ["requirements.txt"]}
|
||||||
|
|
||||||
|
#[tool.setuptools]
|
||||||
|
#packages = ["qweechat", "qweechat.weechat"]
|
||||||
|
|
||||||
|
#[tool.setuptools.packages.find]
|
||||||
|
#where=src
|
@ -1,57 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
# preferences.py - preferences dialog box
|
|
||||||
#
|
|
||||||
# Copyright (C) 2011-2021 Sébastien Helleu <flashcode@flashtux.org>
|
|
||||||
#
|
|
||||||
# This file is part of QWeeChat, a Qt remote GUI for WeeChat.
|
|
||||||
#
|
|
||||||
# QWeeChat 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.
|
|
||||||
#
|
|
||||||
# QWeeChat 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 QWeeChat. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
#
|
|
||||||
|
|
||||||
"""Preferences dialog box."""
|
|
||||||
|
|
||||||
from PySide6 import QtCore, QtWidgets as QtGui
|
|
||||||
|
|
||||||
|
|
||||||
class PreferencesDialog(QtGui.QDialog):
|
|
||||||
"""Preferences dialog."""
|
|
||||||
|
|
||||||
def __init__(self, *args):
|
|
||||||
QtGui.QDialog.__init__(*(self,) + args)
|
|
||||||
self.setModal(True)
|
|
||||||
self.setWindowTitle('Preferences')
|
|
||||||
|
|
||||||
close_button = QtGui.QPushButton('Close')
|
|
||||||
close_button.pressed.connect(self.close)
|
|
||||||
|
|
||||||
hbox = QtGui.QHBoxLayout()
|
|
||||||
hbox.addStretch(1)
|
|
||||||
hbox.addWidget(close_button)
|
|
||||||
hbox.addStretch(1)
|
|
||||||
|
|
||||||
vbox = QtGui.QVBoxLayout()
|
|
||||||
|
|
||||||
label = QtGui.QLabel('Not yet implemented!')
|
|
||||||
label.setAlignment(QtCore.Qt.AlignHCenter)
|
|
||||||
vbox.addWidget(label)
|
|
||||||
|
|
||||||
label = QtGui.QLabel('')
|
|
||||||
label.setAlignment(QtCore.Qt.AlignHCenter)
|
|
||||||
vbox.addWidget(label)
|
|
||||||
|
|
||||||
vbox.addLayout(hbox)
|
|
||||||
|
|
||||||
self.setLayout(vbox)
|
|
||||||
self.show()
|
|
@ -1 +1,4 @@
|
|||||||
PySide6
|
# works with PyQt5 and PyQt6 and maybe PySide2 and PySide6 as well
|
||||||
|
PyQt5
|
||||||
|
# earlier versions may work just fine
|
||||||
|
pyqtconsole >= 5.5.1
|
||||||
|
62
setup.cfg
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
[metadata]
|
||||||
|
classifiers =
|
||||||
|
License :: OSI Approved
|
||||||
|
License :: OSI Approved :: BSD 1-clause
|
||||||
|
Intended Audience :: Web Developers
|
||||||
|
Operating System :: Microsoft :: Windows
|
||||||
|
Operating System :: POSIX :: BSD :: FreeBSD
|
||||||
|
Operating System :: POSIX :: Linux
|
||||||
|
Programming Language :: Python :: 3 :: Only
|
||||||
|
Programming Language :: Python :: 3.6
|
||||||
|
Programming Language :: Python :: 3.7
|
||||||
|
Programming Language :: Python :: 3.8
|
||||||
|
Programming Language :: Python :: 3.9
|
||||||
|
Programming Language :: Python :: 3.10
|
||||||
|
Programming Language :: Python :: 3.11
|
||||||
|
Programming Language :: Python :: Implementation :: CPython
|
||||||
|
|
||||||
|
[options]
|
||||||
|
zip_safe = false
|
||||||
|
python_requires = >= 3.7
|
||||||
|
package_dir=
|
||||||
|
=src
|
||||||
|
# find:
|
||||||
|
packages =
|
||||||
|
qweechat
|
||||||
|
qweechat.weechat
|
||||||
|
|
||||||
|
[options.package_data]
|
||||||
|
qweechat =
|
||||||
|
data/icons/*.png
|
||||||
|
|
||||||
|
[options.entry_points]
|
||||||
|
gui_scripts =
|
||||||
|
qweechat = "qweechat:iMain"
|
||||||
|
|
||||||
|
[easy_install]
|
||||||
|
zip_ok = false
|
||||||
|
|
||||||
|
[flake8]
|
||||||
|
jobs = 1
|
||||||
|
max-line-length = 88
|
||||||
|
ignore =
|
||||||
|
E111
|
||||||
|
E114
|
||||||
|
E128
|
||||||
|
E225
|
||||||
|
E261
|
||||||
|
E302
|
||||||
|
E305
|
||||||
|
E402
|
||||||
|
E501
|
||||||
|
E502
|
||||||
|
E541
|
||||||
|
E701
|
||||||
|
E702
|
||||||
|
E704
|
||||||
|
E722
|
||||||
|
E741
|
||||||
|
F508
|
||||||
|
F541
|
||||||
|
W503
|
||||||
|
W601
|
10
setup.py
@ -1,6 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 2011-2021 Sébastien Helleu <flashcode@flashtux.org>
|
# Copyright (C) 2011-2022 Sébastien Helleu <flashcode@flashtux.org>
|
||||||
#
|
#
|
||||||
# This file is part of QWeeChat, a Qt remote GUI for WeeChat.
|
# This file is part of QWeeChat, a Qt remote GUI for WeeChat.
|
||||||
#
|
#
|
||||||
@ -44,18 +44,20 @@ setup(
|
|||||||
'Programming Language :: Python',
|
'Programming Language :: Python',
|
||||||
'Topic :: Communications :: Chat',
|
'Topic :: Communications :: Chat',
|
||||||
],
|
],
|
||||||
packages=['qweechat', 'qweechat.weechat'],
|
packages=['qweechat', 'qweechat.weechat', 'qweechat.data.icons'],
|
||||||
include_package_data=True,
|
include_package_data=True,
|
||||||
package_data={'qweechat': ['data/icons/*.png']},
|
package_data={'qweechat': ['data/icons/*.png']},
|
||||||
entry_points={
|
entry_points={
|
||||||
'gui_scripts': [
|
'gui_scripts': [
|
||||||
'qweechat = qweechat.qweechat:main',
|
'qweechat = qweechat.qweechat:iMain',
|
||||||
],
|
],
|
||||||
'console_scripts': [
|
'console_scripts': [
|
||||||
'qweechat-testproto = qweechat.weechat.testproto:main',
|
'qweechat-testproto = qweechat.weechat.testproto:main',
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
install_requires=[
|
install_requires=[
|
||||||
'PySide6',
|
'PyQt5',
|
||||||
|
'qtconsole',
|
||||||
],
|
],
|
||||||
|
zip_safe = False,
|
||||||
)
|
)
|
||||||
|
63
setup.py.dst
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# Copyright (C) 2011-2022 Sébastien Helleu <flashcode@flashtux.org>
|
||||||
|
#
|
||||||
|
# This file is part of QWeeChat, a Qt remote GUI for WeeChat.
|
||||||
|
#
|
||||||
|
# QWeeChat 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.
|
||||||
|
#
|
||||||
|
# QWeeChat 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 QWeeChat. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
|
||||||
|
from setuptools import setup
|
||||||
|
from qweechat.version import qweechat_version
|
||||||
|
|
||||||
|
DESCRIPTION = 'Qt remote GUI for WeeChat'
|
||||||
|
|
||||||
|
setup(
|
||||||
|
name='qweechat',
|
||||||
|
version=qweechat_version(),
|
||||||
|
description=DESCRIPTION,
|
||||||
|
long_description=DESCRIPTION,
|
||||||
|
author='Sébastien Helleu',
|
||||||
|
author_email='flashcode@flashtux.org',
|
||||||
|
url='https://weechat.org/',
|
||||||
|
license='GPL3',
|
||||||
|
keywords='weechat qt gui',
|
||||||
|
classifiers=[
|
||||||
|
'Development Status :: 4 - Beta',
|
||||||
|
'Environment :: X11 Applications :: Qt',
|
||||||
|
'Intended Audience :: End Users/Desktop',
|
||||||
|
'License :: OSI Approved :: GNU General Public License v3 '
|
||||||
|
'or later (GPLv3+)',
|
||||||
|
'Natural Language :: English',
|
||||||
|
'Operating System :: OS Independent',
|
||||||
|
'Programming Language :: Python',
|
||||||
|
'Topic :: Communications :: Chat',
|
||||||
|
],
|
||||||
|
packages=['qweechat', 'qweechat.weechat', 'qweechat.data.icons'],
|
||||||
|
include_package_data=True,
|
||||||
|
package_data={'qweechat': ['data/icons/*.png']},
|
||||||
|
entry_points={
|
||||||
|
'gui_scripts': [
|
||||||
|
'qweechat = qweechat.qweechat:iMain',
|
||||||
|
],
|
||||||
|
'console_scripts': [
|
||||||
|
'qweechat-testproto = qweechat.weechat.testproto:main',
|
||||||
|
]
|
||||||
|
},
|
||||||
|
install_requires=[
|
||||||
|
'PyQt5',
|
||||||
|
'qtconsole',
|
||||||
|
],
|
||||||
|
zip_safe = False,
|
||||||
|
)
|
0
src/__init__.py
Normal file
@ -1,6 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 2011-2021 Sébastien Helleu <flashcode@flashtux.org>
|
# Copyright (C) 2011-2022 Sébastien Helleu <flashcode@flashtux.org>
|
||||||
#
|
#
|
||||||
# This file is part of QWeeChat, a Qt remote GUI for WeeChat.
|
# This file is part of QWeeChat, a Qt remote GUI for WeeChat.
|
||||||
#
|
#
|
122
src/qweechat/__main__.py
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import traceback
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from qtpy import QtWidgets, QtGui, QtCore
|
||||||
|
from qtpy.QtWidgets import QApplication
|
||||||
|
|
||||||
|
global LOG
|
||||||
|
LOG = logging.getLogger('qweechat')
|
||||||
|
|
||||||
|
def iMain(lArgs=None):
|
||||||
|
try:
|
||||||
|
from qweechat import qweechat
|
||||||
|
from qweechat.config import write
|
||||||
|
LOG.info("Loading WeechatConsole")
|
||||||
|
except ImportError as e:
|
||||||
|
LOG.error(f"ImportError Loading import qweechat {e} {sys.path}")
|
||||||
|
LOG.debug(traceback.print_exc())
|
||||||
|
return 1
|
||||||
|
|
||||||
|
from qtpy.QtWidgets import QApplication
|
||||||
|
_app = QApplication([])
|
||||||
|
|
||||||
|
# is this still needed?
|
||||||
|
if sys.platform == 'Linux' and \
|
||||||
|
hasattr(QtCore.Qt, 'AA_X11InitThreads'):
|
||||||
|
QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_X11InitThreads)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# WeeChat backported from PySide6 to PyQt5
|
||||||
|
LOG.info("Adding WeechatConsole")
|
||||||
|
class WeechatConsole(qweechat.MainWindow):
|
||||||
|
def __init__(self, *args):
|
||||||
|
qweechat.MainWindow.__init__(self, *args)
|
||||||
|
|
||||||
|
def closeEvent(self, event):
|
||||||
|
"""Called when QWeeChat window is closed."""
|
||||||
|
self.network.disconnect_weechat()
|
||||||
|
if self.network.debug_dialog:
|
||||||
|
self.network.debug_dialog.close()
|
||||||
|
write(self.config)
|
||||||
|
except Exception as e:
|
||||||
|
LOG.exception(f"ERROR WeechatConsole {e}")
|
||||||
|
MainWindow = None
|
||||||
|
return 2
|
||||||
|
size = 12
|
||||||
|
font_name = "Courier New"
|
||||||
|
font_name = "DejaVu Sans Mono"
|
||||||
|
|
||||||
|
try:
|
||||||
|
LOG.info("Creating WeechatConsole")
|
||||||
|
_we = WeechatConsole()
|
||||||
|
_we.show()
|
||||||
|
_we.setWindowTitle('File/Connect to 127.0.0.1:9000')
|
||||||
|
# Fix the pyconsole geometry
|
||||||
|
try:
|
||||||
|
font = _we.buffers[0].widget.chat.defaultFont()
|
||||||
|
font.setFamily(font_name)
|
||||||
|
font.setBold(True)
|
||||||
|
if font_width is None:
|
||||||
|
font_width = QFontMetrics(font).width('M')
|
||||||
|
_we.setFont(font)
|
||||||
|
except Exception as e:
|
||||||
|
# LOG.debug(e)
|
||||||
|
font_width = size
|
||||||
|
geometry = _we.geometry()
|
||||||
|
# make this configable?
|
||||||
|
geometry.setWidth(int(font_width*70))
|
||||||
|
geometry.setHeight(int(font_width*(2+24)*11/8))
|
||||||
|
_we.setGeometry(geometry)
|
||||||
|
#? QtCore.QSize()
|
||||||
|
_we.resize(int(font_width*80+20), int(font_width*(2+24)*11/8))
|
||||||
|
|
||||||
|
_we.list_buffers.setSizePolicy(QtWidgets.QSizePolicy.Preferred,
|
||||||
|
QtWidgets.QSizePolicy.Preferred)
|
||||||
|
_we.stacked_buffers.setSizePolicy(QtWidgets.QSizePolicy.Expanding,
|
||||||
|
QtWidgets.QSizePolicy.Expanding)
|
||||||
|
|
||||||
|
LOG.info("Showing WeechatConsole")
|
||||||
|
_we.show()
|
||||||
|
# or _we.eval_in_thread()
|
||||||
|
#? return 0
|
||||||
|
except Exception as e:
|
||||||
|
LOG.exception(f"Error creating WeechatConsole {e}")
|
||||||
|
return 4
|
||||||
|
|
||||||
|
LOG.info("_execute_app")
|
||||||
|
|
||||||
|
# self._app.lastWindowClosed.connect(self._app.quit)
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
_app.exec_()
|
||||||
|
except Exception as ex:
|
||||||
|
LOG.error('Unhandled exception: ' + str(ex))
|
||||||
|
return 5
|
||||||
|
else:
|
||||||
|
pass # break
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
iRet = 0
|
||||||
|
try:
|
||||||
|
iRet = iMain(sys.argv[1:])
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
iRet = 0
|
||||||
|
except SystemExit as e:
|
||||||
|
iRet = e
|
||||||
|
except Exception as e:
|
||||||
|
import traceback
|
||||||
|
sys.stderr.write(f"Exception from main {e}" \
|
||||||
|
+'\n' + traceback.format_exc() +'\n' )
|
||||||
|
iRet = 1
|
||||||
|
|
||||||
|
# Exception ignored in: <module 'threading' from '/usr/lib/python3.9/threading.py'>
|
||||||
|
# File "/usr/lib/python3.9/threading.py", line 1428, in _shutdown
|
||||||
|
# lock.acquire()
|
||||||
|
# gevent.exceptions.LoopExit as e:
|
||||||
|
# This operation would block forever
|
||||||
|
# sys.stderr.write('Calling sys.exit' +'\n')
|
||||||
|
# sys.exit(iRet)
|
||||||
|
|
@ -2,7 +2,7 @@
|
|||||||
#
|
#
|
||||||
# about.py - about dialog box
|
# about.py - about dialog box
|
||||||
#
|
#
|
||||||
# Copyright (C) 2011-2021 Sébastien Helleu <flashcode@flashtux.org>
|
# Copyright (C) 2011-2022 Sébastien Helleu <flashcode@flashtux.org>
|
||||||
#
|
#
|
||||||
# This file is part of QWeeChat, a Qt remote GUI for WeeChat.
|
# This file is part of QWeeChat, a Qt remote GUI for WeeChat.
|
||||||
#
|
#
|
||||||
@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
"""About dialog box."""
|
"""About dialog box."""
|
||||||
|
|
||||||
from PySide6 import QtCore, QtWidgets as QtGui
|
from qtpy import QtCore, QtWidgets as QtGui
|
||||||
|
|
||||||
from qweechat.version import qweechat_version
|
from qweechat.version import qweechat_version
|
||||||
|
|
||||||
@ -46,7 +46,7 @@ class AboutDialog(QtGui.QDialog):
|
|||||||
vbox = QtGui.QVBoxLayout()
|
vbox = QtGui.QVBoxLayout()
|
||||||
messages = [
|
messages = [
|
||||||
f'<b>{app_name}</b> {qweechat_version()}',
|
f'<b>{app_name}</b> {qweechat_version()}',
|
||||||
f'© 2011-2021 {author}',
|
f'© 2011-2022 {author}',
|
||||||
'',
|
'',
|
||||||
f'<a href="{weechat_site}">{weechat_site}</a>',
|
f'<a href="{weechat_site}">{weechat_site}</a>',
|
||||||
'',
|
'',
|
@ -2,7 +2,7 @@
|
|||||||
#
|
#
|
||||||
# buffer.py - management of WeeChat buffers/nicklist
|
# buffer.py - management of WeeChat buffers/nicklist
|
||||||
#
|
#
|
||||||
# Copyright (C) 2011-2021 Sébastien Helleu <flashcode@flashtux.org>
|
# Copyright (C) 2011-2022 Sébastien Helleu <flashcode@flashtux.org>
|
||||||
#
|
#
|
||||||
# This file is part of QWeeChat, a Qt remote GUI for WeeChat.
|
# This file is part of QWeeChat, a Qt remote GUI for WeeChat.
|
||||||
#
|
#
|
||||||
@ -24,13 +24,13 @@
|
|||||||
|
|
||||||
from pkg_resources import resource_filename
|
from pkg_resources import resource_filename
|
||||||
|
|
||||||
from PySide6 import QtCore, QtGui, QtWidgets
|
from qtpy import QtCore, QtGui, QtWidgets
|
||||||
|
from qtpy.QtCore import Signal
|
||||||
|
|
||||||
from qweechat.chat import ChatTextEdit
|
from qweechat.chat import ChatTextEdit
|
||||||
from qweechat.input import InputLineEdit
|
from qweechat.input import InputLineEdit
|
||||||
from qweechat.weechat import color
|
from qweechat.weechat import color
|
||||||
|
|
||||||
|
|
||||||
class GenericListWidget(QtWidgets.QListWidget):
|
class GenericListWidget(QtWidgets.QListWidget):
|
||||||
"""Generic QListWidget with dynamic size."""
|
"""Generic QListWidget with dynamic size."""
|
||||||
|
|
||||||
@ -145,7 +145,7 @@ class BufferWidget(QtWidgets.QWidget):
|
|||||||
class Buffer(QtCore.QObject):
|
class Buffer(QtCore.QObject):
|
||||||
"""A WeeChat buffer."""
|
"""A WeeChat buffer."""
|
||||||
|
|
||||||
bufferInput = QtCore.Signal(str, str)
|
bufferInput = Signal(str, str)
|
||||||
|
|
||||||
def __init__(self, data=None):
|
def __init__(self, data=None):
|
||||||
QtCore.QObject.__init__(self)
|
QtCore.QObject.__init__(self)
|
@ -2,7 +2,7 @@
|
|||||||
#
|
#
|
||||||
# chat.py - chat area
|
# chat.py - chat area
|
||||||
#
|
#
|
||||||
# Copyright (C) 2011-2021 Sébastien Helleu <flashcode@flashtux.org>
|
# Copyright (C) 2011-2022 Sébastien Helleu <flashcode@flashtux.org>
|
||||||
#
|
#
|
||||||
# This file is part of QWeeChat, a Qt remote GUI for WeeChat.
|
# This file is part of QWeeChat, a Qt remote GUI for WeeChat.
|
||||||
#
|
#
|
||||||
@ -24,9 +24,9 @@
|
|||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
from PySide6 import QtCore, QtWidgets, QtGui
|
from qtpy import QtCore, QtWidgets, QtGui
|
||||||
|
|
||||||
from qweechat import config
|
from qweechat.config import color_options
|
||||||
from qweechat.weechat import color
|
from qweechat.weechat import color
|
||||||
|
|
||||||
|
|
||||||
@ -62,7 +62,7 @@ class ChatTextEdit(QtWidgets.QTextEdit):
|
|||||||
'/': True
|
'/': True
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self._color = color.Color(config.color_options(), self.debug)
|
self._color = color.Color(color_options(), self.debug)
|
||||||
|
|
||||||
def display(self, time, prefix, text, forcecolor=None):
|
def display(self, time, prefix, text, forcecolor=None):
|
||||||
if time == 0:
|
if time == 0:
|
@ -2,7 +2,7 @@
|
|||||||
#
|
#
|
||||||
# config.py - configuration for QWeeChat
|
# config.py - configuration for QWeeChat
|
||||||
#
|
#
|
||||||
# Copyright (C) 2011-2021 Sébastien Helleu <flashcode@flashtux.org>
|
# Copyright (C) 2011-2022 Sébastien Helleu <flashcode@flashtux.org>
|
||||||
#
|
#
|
||||||
# This file is part of QWeeChat, a Qt remote GUI for WeeChat.
|
# This file is part of QWeeChat, a Qt remote GUI for WeeChat.
|
||||||
#
|
#
|
||||||
@ -33,14 +33,15 @@ CONFIG_FILENAME = '%s/qweechat.conf' % CONFIG_DIR
|
|||||||
CONFIG_DEFAULT_RELAY_LINES = 50
|
CONFIG_DEFAULT_RELAY_LINES = 50
|
||||||
|
|
||||||
CONFIG_DEFAULT_SECTIONS = ('relay', 'look', 'color')
|
CONFIG_DEFAULT_SECTIONS = ('relay', 'look', 'color')
|
||||||
CONFIG_DEFAULT_OPTIONS = (('relay.server', ''),
|
CONFIG_DEFAULT_OPTIONS = (('relay.hostname', '127.0.0.1'),
|
||||||
('relay.port', ''),
|
('relay.port', '9000'),
|
||||||
('relay.ssl', 'off'),
|
('relay.ssl', 'off'),
|
||||||
('relay.password', ''),
|
('relay.password', ''),
|
||||||
('relay.autoconnect', 'off'),
|
('relay.autoconnect', 'off'),
|
||||||
('relay.lines', str(CONFIG_DEFAULT_RELAY_LINES)),
|
('relay.lines', str(CONFIG_DEFAULT_RELAY_LINES)),
|
||||||
('look.debug', 'off'),
|
('look.debug', 'off'),
|
||||||
('look.statusbar', 'off'))
|
('look.statusbar', 'on'))
|
||||||
|
CONFIG_DEFAULT_NOTIFICATION_OPTIONS = tuple
|
||||||
|
|
||||||
# Default colors for WeeChat color options (option name, #rgb value)
|
# Default colors for WeeChat color options (option name, #rgb value)
|
||||||
CONFIG_DEFAULT_COLOR_OPTIONS = (
|
CONFIG_DEFAULT_COLOR_OPTIONS = (
|
@ -2,7 +2,7 @@
|
|||||||
#
|
#
|
||||||
# connection.py - connection window
|
# connection.py - connection window
|
||||||
#
|
#
|
||||||
# Copyright (C) 2011-2021 Sébastien Helleu <flashcode@flashtux.org>
|
# Copyright (C) 2011-2022 Sébastien Helleu <flashcode@flashtux.org>
|
||||||
#
|
#
|
||||||
# This file is part of QWeeChat, a Qt remote GUI for WeeChat.
|
# This file is part of QWeeChat, a Qt remote GUI for WeeChat.
|
||||||
#
|
#
|
||||||
@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
"""Connection window."""
|
"""Connection window."""
|
||||||
|
|
||||||
from PySide6 import QtGui, QtWidgets
|
from qtpy import QtGui, QtWidgets
|
||||||
|
|
||||||
|
|
||||||
class ConnectionDialog(QtWidgets.QDialog):
|
class ConnectionDialog(QtWidgets.QDialog):
|
||||||
@ -40,22 +40,30 @@ class ConnectionDialog(QtWidgets.QDialog):
|
|||||||
self.fields = {}
|
self.fields = {}
|
||||||
focus = None
|
focus = None
|
||||||
|
|
||||||
# server
|
# hostname
|
||||||
grid.addWidget(QtWidgets.QLabel('<b>Server</b>'), 0, 0)
|
grid.addWidget(QtWidgets.QLabel('<b>Hostname</b>'), 0, 0)
|
||||||
line_edit = QtWidgets.QLineEdit()
|
line_edit = QtWidgets.QLineEdit()
|
||||||
line_edit.setFixedWidth(200)
|
line_edit.setFixedWidth(200)
|
||||||
value = self.values.get('server', '')
|
value = self.values.get('hostname', '')
|
||||||
|
if value in ['None', None]:
|
||||||
|
value = '0'
|
||||||
|
elif type(value) == int:
|
||||||
|
value = str(value)
|
||||||
line_edit.insert(value)
|
line_edit.insert(value)
|
||||||
grid.addWidget(line_edit, 0, 1)
|
grid.addWidget(line_edit, 0, 1)
|
||||||
self.fields['server'] = line_edit
|
self.fields['hostname'] = line_edit
|
||||||
if not focus and not value:
|
if not focus and not value:
|
||||||
focus = 'server'
|
focus = 'hostname'
|
||||||
|
|
||||||
# port / SSL
|
# port / SSL
|
||||||
grid.addWidget(QtWidgets.QLabel('<b>Port</b>'), 1, 0)
|
grid.addWidget(QtWidgets.QLabel('<b>Port</b>'), 1, 0)
|
||||||
line_edit = QtWidgets.QLineEdit()
|
line_edit = QtWidgets.QLineEdit()
|
||||||
line_edit.setFixedWidth(200)
|
line_edit.setFixedWidth(200)
|
||||||
value = self.values.get('port', '')
|
value = self.values.get('port', '')
|
||||||
|
if value in ['None', None]:
|
||||||
|
value = '0'
|
||||||
|
elif type(value) == int:
|
||||||
|
value = str(value)
|
||||||
line_edit.insert(value)
|
line_edit.insert(value)
|
||||||
grid.addWidget(line_edit, 1, 1)
|
grid.addWidget(line_edit, 1, 1)
|
||||||
self.fields['port'] = line_edit
|
self.fields['port'] = line_edit
|
||||||
@ -73,6 +81,10 @@ class ConnectionDialog(QtWidgets.QDialog):
|
|||||||
line_edit.setFixedWidth(200)
|
line_edit.setFixedWidth(200)
|
||||||
line_edit.setEchoMode(QtWidgets.QLineEdit.Password)
|
line_edit.setEchoMode(QtWidgets.QLineEdit.Password)
|
||||||
value = self.values.get('password', '')
|
value = self.values.get('password', '')
|
||||||
|
if value in ['None', None]:
|
||||||
|
value = '0'
|
||||||
|
elif type(value) == int:
|
||||||
|
value = str(value)
|
||||||
line_edit.insert(value)
|
line_edit.insert(value)
|
||||||
grid.addWidget(line_edit, 2, 1)
|
grid.addWidget(line_edit, 2, 1)
|
||||||
self.fields['password'] = line_edit
|
self.fields['password'] = line_edit
|
@ -4,7 +4,7 @@ Copyright and license for images
|
|||||||
|
|
||||||
Files: weechat.png, bullet_green_8x8.png, bullet_yellow_8x8.png
|
Files: weechat.png, bullet_green_8x8.png, bullet_yellow_8x8.png
|
||||||
|
|
||||||
Copyright (C) 2011-2021 Sébastien Helleu <flashcode@flashtux.org>
|
Copyright (C) 2011-2022 Sébastien Helleu <flashcode@flashtux.org>
|
||||||
Released under GPLv3.
|
Released under GPLv3.
|
||||||
|
|
||||||
|
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 384 B After Width: | Height: | Size: 384 B |
Before Width: | Height: | Size: 375 B After Width: | Height: | Size: 375 B |
Before Width: | Height: | Size: 813 B After Width: | Height: | Size: 813 B |
Before Width: | Height: | Size: 597 B After Width: | Height: | Size: 597 B |
Before Width: | Height: | Size: 713 B After Width: | Height: | Size: 713 B |
Before Width: | Height: | Size: 596 B After Width: | Height: | Size: 596 B |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
@ -2,7 +2,7 @@
|
|||||||
#
|
#
|
||||||
# debug.py - debug window
|
# debug.py - debug window
|
||||||
#
|
#
|
||||||
# Copyright (C) 2011-2021 Sébastien Helleu <flashcode@flashtux.org>
|
# Copyright (C) 2011-2022 Sébastien Helleu <flashcode@flashtux.org>
|
||||||
#
|
#
|
||||||
# This file is part of QWeeChat, a Qt remote GUI for WeeChat.
|
# This file is part of QWeeChat, a Qt remote GUI for WeeChat.
|
||||||
#
|
#
|
||||||
@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
"""Debug window."""
|
"""Debug window."""
|
||||||
|
|
||||||
from PySide6 import QtWidgets
|
from qtpy import QtWidgets
|
||||||
|
|
||||||
from qweechat.chat import ChatTextEdit
|
from qweechat.chat import ChatTextEdit
|
||||||
from qweechat.input import InputLineEdit
|
from qweechat.input import InputLineEdit
|
||||||
@ -33,7 +33,7 @@ class DebugDialog(QtWidgets.QDialog):
|
|||||||
|
|
||||||
def __init__(self, *args):
|
def __init__(self, *args):
|
||||||
QtWidgets.QDialog.__init__(*(self,) + args)
|
QtWidgets.QDialog.__init__(*(self,) + args)
|
||||||
self.resize(1024, 768)
|
self.resize(800, 600)
|
||||||
self.setWindowTitle('Debug console')
|
self.setWindowTitle('Debug console')
|
||||||
|
|
||||||
self.chat = ChatTextEdit(debug=True)
|
self.chat = ChatTextEdit(debug=True)
|
@ -2,7 +2,7 @@
|
|||||||
#
|
#
|
||||||
# input.py - input line for chat and debug window
|
# input.py - input line for chat and debug window
|
||||||
#
|
#
|
||||||
# Copyright (C) 2011-2021 Sébastien Helleu <flashcode@flashtux.org>
|
# Copyright (C) 2011-2022 Sébastien Helleu <flashcode@flashtux.org>
|
||||||
#
|
#
|
||||||
# This file is part of QWeeChat, a Qt remote GUI for WeeChat.
|
# This file is part of QWeeChat, a Qt remote GUI for WeeChat.
|
||||||
#
|
#
|
||||||
@ -22,15 +22,15 @@
|
|||||||
|
|
||||||
"""Input line for chat and debug window."""
|
"""Input line for chat and debug window."""
|
||||||
|
|
||||||
from PySide6 import QtCore, QtWidgets
|
from qtpy import QtCore, QtWidgets
|
||||||
|
from qtpy.QtCore import Signal
|
||||||
|
|
||||||
class InputLineEdit(QtWidgets.QLineEdit):
|
class InputLineEdit(QtWidgets.QLineEdit):
|
||||||
"""Input line."""
|
"""Input line."""
|
||||||
|
|
||||||
bufferSwitchPrev = QtCore.Signal()
|
bufferSwitchPrev = Signal()
|
||||||
bufferSwitchNext = QtCore.Signal()
|
bufferSwitchNext = Signal()
|
||||||
textSent = QtCore.Signal(str)
|
textSent = Signal(str)
|
||||||
|
|
||||||
def __init__(self, scroll_widget):
|
def __init__(self, scroll_widget):
|
||||||
super().__init__()
|
super().__init__()
|
@ -2,7 +2,7 @@
|
|||||||
#
|
#
|
||||||
# network.py - I/O with WeeChat/relay
|
# network.py - I/O with WeeChat/relay
|
||||||
#
|
#
|
||||||
# Copyright (C) 2011-2021 Sébastien Helleu <flashcode@flashtux.org>
|
# Copyright (C) 2011-2022 Sébastien Helleu <flashcode@flashtux.org>
|
||||||
#
|
#
|
||||||
# This file is part of QWeeChat, a Qt remote GUI for WeeChat.
|
# This file is part of QWeeChat, a Qt remote GUI for WeeChat.
|
||||||
#
|
#
|
||||||
@ -26,7 +26,9 @@ import hashlib
|
|||||||
import secrets
|
import secrets
|
||||||
import struct
|
import struct
|
||||||
|
|
||||||
from PySide6 import QtCore, QtNetwork
|
from qtpy import QtCore, QtNetwork
|
||||||
|
# from PyQt5.QtCore import pyqtSignal as Signal
|
||||||
|
from qtpy.QtCore import Signal
|
||||||
|
|
||||||
from qweechat import config
|
from qweechat import config
|
||||||
from qweechat.debug import DebugDialog
|
from qweechat.debug import DebugDialog
|
||||||
@ -47,13 +49,13 @@ _HASH_ALGOS = ':'.join(_HASH_ALGOS_LIST)
|
|||||||
_PROTO_HANDSHAKE = f'(handshake) handshake password_hash_algo={_HASH_ALGOS}\n'
|
_PROTO_HANDSHAKE = f'(handshake) handshake password_hash_algo={_HASH_ALGOS}\n'
|
||||||
|
|
||||||
# initialize with the password (plain text)
|
# initialize with the password (plain text)
|
||||||
_PROTO_INIT_PWD = 'init password=%(password)s%(totp)s\n'
|
_PROTO_INIT_PWD = 'init password=%(password)s%(totp)s\n' # nosec
|
||||||
|
|
||||||
# initialize with the hashed password
|
# initialize with the hashed password
|
||||||
_PROTO_INIT_HASH = ('init password_hash='
|
_PROTO_INIT_HASH = ('init password_hash='
|
||||||
'%(algo)s:%(salt)s%(iter)s:%(hash)s%(totp)s\n')
|
'%(algo)s:%(salt)s%(iter)s:%(hash)s%(totp)s\n')
|
||||||
|
|
||||||
_PROTO_SYNC = [
|
_PROTO_SYNC_CMDS = [
|
||||||
# get buffers
|
# get buffers
|
||||||
'(listbuffers) hdata buffer:gui_buffers(*) number,full_name,short_name,'
|
'(listbuffers) hdata buffer:gui_buffers(*) number,full_name,short_name,'
|
||||||
'type,nicklist,title,local_variables',
|
'type,nicklist,title,local_variables',
|
||||||
@ -98,8 +100,8 @@ NETWORK_STATUS = {
|
|||||||
class Network(QtCore.QObject):
|
class Network(QtCore.QObject):
|
||||||
"""I/O with WeeChat/relay."""
|
"""I/O with WeeChat/relay."""
|
||||||
|
|
||||||
statusChanged = QtCore.Signal(str, str)
|
statusChanged = Signal(str, str)
|
||||||
messageFromWeechat = QtCore.Signal(QtCore.QByteArray)
|
messageFromWeechat = Signal(QtCore.QByteArray)
|
||||||
|
|
||||||
def __init__(self, *args):
|
def __init__(self, *args):
|
||||||
super().__init__(*args)
|
super().__init__(*args)
|
||||||
@ -115,7 +117,7 @@ class Network(QtCore.QObject):
|
|||||||
|
|
||||||
def _init_connection(self):
|
def _init_connection(self):
|
||||||
self.status = STATUS_DISCONNECTED
|
self.status = STATUS_DISCONNECTED
|
||||||
self._server = None
|
self._hostname = None
|
||||||
self._port = None
|
self._port = None
|
||||||
self._ssl = None
|
self._ssl = None
|
||||||
self._password = None
|
self._password = None
|
||||||
@ -179,7 +181,7 @@ class Network(QtCore.QObject):
|
|||||||
|
|
||||||
def _build_sync_command(self):
|
def _build_sync_command(self):
|
||||||
"""Build the sync commands to send to WeeChat."""
|
"""Build the sync commands to send to WeeChat."""
|
||||||
cmd = '\n'.join(_PROTO_SYNC) + '\n'
|
cmd = '\n'.join(_PROTO_SYNC_CMDS) + '\n'
|
||||||
return cmd % {'lines': self._lines}
|
return cmd % {'lines': self._lines}
|
||||||
|
|
||||||
def handshake_timer_expired(self):
|
def handshake_timer_expired(self):
|
||||||
@ -230,15 +232,27 @@ class Network(QtCore.QObject):
|
|||||||
|
|
||||||
def is_connected(self):
|
def is_connected(self):
|
||||||
"""Return True if the socket is connected, False otherwise."""
|
"""Return True if the socket is connected, False otherwise."""
|
||||||
return self._socket.state() == QtNetwork.QAbstractSocket.ConnectedState
|
return self.is_state(at='ConnectedState')
|
||||||
|
|
||||||
|
def is_state(self, at='ConnectedState'):
|
||||||
|
"""Return True if the socket is connected, False otherwise."""
|
||||||
|
if hasattr(QtNetwork.QAbstractSocket, 'ConnectedState'):
|
||||||
|
if self._socket.state() == getattr(QtNetwork.QAbstractSocket, at):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
if hasattr(QtNetwork.QAbstractSocket, 'SocketState'):
|
||||||
|
if self._socket.state() == getattr(QtNetwork.QAbstractSocket.SocketState, at):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
return False
|
||||||
|
|
||||||
def is_ssl(self):
|
def is_ssl(self):
|
||||||
"""Return True if SSL is used, False otherwise."""
|
"""Return True if SSL is used, False otherwise."""
|
||||||
return self._ssl
|
return self._ssl
|
||||||
|
|
||||||
def connect_weechat(self, server, port, ssl, password, totp, lines):
|
def connect_weechat(self, hostname, port, ssl, password, totp, lines):
|
||||||
"""Connect to WeeChat."""
|
"""Connect to WeeChat."""
|
||||||
self._server = server
|
self._hostname = hostname
|
||||||
try:
|
try:
|
||||||
self._port = int(port)
|
self._port = int(port)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
@ -250,23 +264,24 @@ class Network(QtCore.QObject):
|
|||||||
self._lines = int(lines)
|
self._lines = int(lines)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
self._lines = config.CONFIG_DEFAULT_RELAY_LINES
|
self._lines = config.CONFIG_DEFAULT_RELAY_LINES
|
||||||
if self._socket.state() == QtNetwork.QAbstractSocket.ConnectedState:
|
# AttributeError: type object 'QAbstractSocket' has no attribute 'ConnectedState'
|
||||||
|
if self.is_connected():
|
||||||
return
|
return
|
||||||
if self._socket.state() != QtNetwork.QAbstractSocket.UnconnectedState:
|
if not self.is_state('UnconnectedState'):
|
||||||
self._socket.abort()
|
self._socket.abort()
|
||||||
if self._ssl:
|
if self._ssl:
|
||||||
self._socket.ignoreSslErrors()
|
self._socket.ignoreSslErrors()
|
||||||
self._socket.connectToHostEncrypted(self._server, self._port)
|
self._socket.connectToHostEncrypted(self._hostname, self._port)
|
||||||
else:
|
else:
|
||||||
self._socket.connectToHost(self._server, self._port)
|
self._socket.connectToHost(self._hostname, self._port)
|
||||||
self.set_status(STATUS_CONNECTING)
|
self.set_status(STATUS_CONNECTING)
|
||||||
|
|
||||||
def disconnect_weechat(self):
|
def disconnect_weechat(self):
|
||||||
"""Disconnect from WeeChat."""
|
"""Disconnect from WeeChat."""
|
||||||
if self._socket.state() == QtNetwork.QAbstractSocket.UnconnectedState:
|
if self.is_state('UnconnectedState'):
|
||||||
self.set_status(STATUS_DISCONNECTED)
|
self.set_status(STATUS_DISCONNECTED)
|
||||||
return
|
return
|
||||||
if self._socket.state() == QtNetwork.QAbstractSocket.ConnectedState:
|
if self.is_state('ConnectedState'):
|
||||||
self.send_to_weechat('quit\n')
|
self.send_to_weechat('quit\n')
|
||||||
self._socket.waitForBytesWritten(1000)
|
self._socket.waitForBytesWritten(1000)
|
||||||
else:
|
else:
|
||||||
@ -316,7 +331,7 @@ class Network(QtCore.QObject):
|
|||||||
def get_options(self):
|
def get_options(self):
|
||||||
"""Get connection options."""
|
"""Get connection options."""
|
||||||
return {
|
return {
|
||||||
'server': self._server,
|
'hostname': self._hostname,
|
||||||
'port': self._port,
|
'port': self._port,
|
||||||
'ssl': 'on' if self._ssl else 'off',
|
'ssl': 'on' if self._ssl else 'off',
|
||||||
'password': self._password,
|
'password': self._password,
|
509
src/qweechat/preferences.py
Normal file
@ -0,0 +1,509 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# preferences.py - preferences dialog box
|
||||||
|
#
|
||||||
|
# Copyright (C) 2011-2022 Sébastien Helleu <flashcode@flashtux.org>
|
||||||
|
#
|
||||||
|
# This file is part of QWeeChat, a Qt remote GUI for WeeChat.
|
||||||
|
#
|
||||||
|
# QWeeChat 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.
|
||||||
|
#
|
||||||
|
# QWeeChat 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 QWeeChat. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
|
||||||
|
"""Preferences dialog box."""
|
||||||
|
|
||||||
|
from qtpy import QtCore, QtWidgets, QtGui
|
||||||
|
qicon_from_theme = QtGui.QIcon.fromTheme
|
||||||
|
|
||||||
|
class PreferencesDialog(QtWidgets.QDialog):
|
||||||
|
"""Preferences dialog."""
|
||||||
|
|
||||||
|
def __init__(self, *args):
|
||||||
|
QtWidgets.QDialog.__init__(*(self,) + args)
|
||||||
|
self.setModal(True)
|
||||||
|
self.setWindowTitle('Preferences')
|
||||||
|
|
||||||
|
close_button = QtWidgets.QPushButton('Close')
|
||||||
|
close_button.pressed.connect(self.close)
|
||||||
|
|
||||||
|
hbox = QtWidgets.QHBoxLayout()
|
||||||
|
hbox.addStretch(1)
|
||||||
|
hbox.addWidget(close_button)
|
||||||
|
hbox.addStretch(1)
|
||||||
|
|
||||||
|
vbox = QtWidgets.QVBoxLayout()
|
||||||
|
|
||||||
|
label = QtWidgets.QLabel('Not yet implemented!')
|
||||||
|
label.setAlignment(QtCore.Qt.AlignHCenter)
|
||||||
|
vbox.addWidget(label)
|
||||||
|
|
||||||
|
label = QtWidgets.QLabel('')
|
||||||
|
label.setAlignment(QtCore.Qt.AlignHCenter)
|
||||||
|
vbox.addWidget(label)
|
||||||
|
|
||||||
|
vbox.addLayout(hbox)
|
||||||
|
|
||||||
|
self.setLayout(vbox)
|
||||||
|
self.show()
|
||||||
|
|
||||||
|
# prepare for https://github.com/weechat/qweechat/pull/8
|
||||||
|
|
||||||
|
def _pane_switch(self, item):
|
||||||
|
"""Switch the visible preference pane."""
|
||||||
|
index = self.list_panes.indexOfTopLevelItem(item)
|
||||||
|
if index >= 0:
|
||||||
|
self.stacked_panes.setCurrentIndex(index)
|
||||||
|
|
||||||
|
def _save_and_close(self):
|
||||||
|
for widget in (self.stacked_panes.widget(i)
|
||||||
|
for i in range(self.stacked_panes.count())):
|
||||||
|
for key, field in widget.fields.items():
|
||||||
|
if isinstance(field, QtWidgets.QComboBox):
|
||||||
|
text = field.itemText(field.currentIndex())
|
||||||
|
data = field.itemData(field.currentIndex())
|
||||||
|
text = data if data else text
|
||||||
|
elif isinstance(field, QtWidgets.QCheckBox):
|
||||||
|
text = "on" if field.isChecked() else "off"
|
||||||
|
else:
|
||||||
|
text = field.text()
|
||||||
|
self.config.set(widget.section_name, key, str(text))
|
||||||
|
write(self.config)
|
||||||
|
self.parent.apply_preferences()
|
||||||
|
self.close()
|
||||||
|
|
||||||
|
|
||||||
|
class PreferencesNotificationBlock(QtWidgets.QVBoxLayout):
|
||||||
|
"""Display notification settings with drill down to configure."""
|
||||||
|
def __init__(self, pane, *args):
|
||||||
|
QtWidgets.QVBoxLayout.__init__(*(self,) + args)
|
||||||
|
self.section = "notifications"
|
||||||
|
self.config = QtWidgets.QApplication.instance().config
|
||||||
|
self.pane = pane
|
||||||
|
self.stack = QtWidgets.QStackedWidget()
|
||||||
|
|
||||||
|
self.table = QtWidgets.QTableWidget()
|
||||||
|
fg_color = self.table.palette().text().color().name()
|
||||||
|
self.action_labels = {
|
||||||
|
"sound": "Play a sound",
|
||||||
|
"message": "Show a message in a popup",
|
||||||
|
"file": "Log to a file",
|
||||||
|
"taskbar": "Mark taskbar entry",
|
||||||
|
"tray": "Mark systray/indicator",
|
||||||
|
"command": "Run a command"}
|
||||||
|
self.action_icons = {
|
||||||
|
"sound": qicon_from_theme("media-playback-start"),
|
||||||
|
"message": qicon_from_theme("dialog-information"),
|
||||||
|
"file": qicon_from_theme("document-export"),
|
||||||
|
"taskbar": qicon_from_theme("weechat"),
|
||||||
|
"tray": utils.qicon_tint("ic_hot", fg_color),
|
||||||
|
"command": qicon_from_theme("system-run")}
|
||||||
|
self.icon_widget_qss = "padding:0;min-height:10px;min-width:16px;"
|
||||||
|
self.table.resizeColumnsToContents()
|
||||||
|
self.table.setColumnCount(2)
|
||||||
|
self.table.resizeRowsToContents()
|
||||||
|
self.table.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
|
||||||
|
self.table.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection)
|
||||||
|
self.table.setHorizontalHeaderLabels(["State", "Type"])
|
||||||
|
self.table.horizontalHeader().setStretchLastSection(True)
|
||||||
|
self.table.horizontalHeader().setHighlightSections(False)
|
||||||
|
self.table.verticalHeader().setVisible(False)
|
||||||
|
self.table.setShowGrid(False)
|
||||||
|
self.table.itemSelectionChanged.connect(self._table_row_changed)
|
||||||
|
|
||||||
|
self.buftypes = {}
|
||||||
|
for key, value in CONFIG_DEFAULT_NOTIFICATION_OPTIONS:
|
||||||
|
buftype, optkey = key.split(".")
|
||||||
|
if buftype not in self.buftypes:
|
||||||
|
self.buftypes[buftype] = {}
|
||||||
|
self.buftypes[buftype][optkey] = self.config.get(self.section, key)
|
||||||
|
for buftype, optkey in self.buftypes.items():
|
||||||
|
self._insert_type(buftype)
|
||||||
|
self.update_icons()
|
||||||
|
self.resize_table()
|
||||||
|
self.addWidget(self.table)
|
||||||
|
self.addWidget(self.stack)
|
||||||
|
self.table.selectRow(0)
|
||||||
|
|
||||||
|
def _insert_type(self, buftype):
|
||||||
|
row = self.table.rowCount()
|
||||||
|
self.table.insertRow(row)
|
||||||
|
buftype_item = QtWidgets.QTableWidgetItem(buftype)
|
||||||
|
buftype_item.setTextAlignment(QtCore.Qt.AlignCenter)
|
||||||
|
self.table.setItem(row, 0, QtWidgets.QTableWidgetItem())
|
||||||
|
self.table.setItem(row, 1, buftype_item)
|
||||||
|
subgrid = QtWidgets.QGridLayout()
|
||||||
|
subgrid.setColumnStretch(2, 1)
|
||||||
|
subgrid.setSpacing(10)
|
||||||
|
|
||||||
|
for key, qicon in self.action_icons.items():
|
||||||
|
value = self.buftypes[buftype][key]
|
||||||
|
line = subgrid.rowCount()
|
||||||
|
label = IconTextLabel(self.action_labels[key], qicon, 16)
|
||||||
|
|
||||||
|
checkbox = QtWidgets.QCheckBox()
|
||||||
|
span = 1
|
||||||
|
edit = None
|
||||||
|
if key in ("message", "taskbar", "tray"):
|
||||||
|
checkbox.setChecked(value == "on")
|
||||||
|
span = 2
|
||||||
|
elif key == "sound":
|
||||||
|
edit = PreferencesFileEdit(
|
||||||
|
checkbox=checkbox, caption='Select a sound file',
|
||||||
|
filter='Audio Files (*.wav *.mp3 *.ogg)')
|
||||||
|
elif key == "file":
|
||||||
|
edit = PreferencesFileEdit(checkbox=checkbox, mode="save")
|
||||||
|
else:
|
||||||
|
edit = PreferencesFileEdit(checkbox=checkbox)
|
||||||
|
if edit:
|
||||||
|
edit.insert(value)
|
||||||
|
subgrid.addWidget(edit, line, 2)
|
||||||
|
else:
|
||||||
|
edit = checkbox
|
||||||
|
subgrid.addWidget(label, line, 1, 1, span)
|
||||||
|
subgrid.addWidget(checkbox, line, 0)
|
||||||
|
self.pane.fields[buftype + "." + key] = edit
|
||||||
|
subpane = QtWidgets.QWidget()
|
||||||
|
subpane.setLayout(subgrid)
|
||||||
|
subpane.setMaximumHeight(subgrid.totalMinimumSize().height())
|
||||||
|
self.stack.addWidget(subpane)
|
||||||
|
|
||||||
|
def resize_table(self):
|
||||||
|
"""Fit the table height to contents."""
|
||||||
|
height = self.table.horizontalHeader().height()
|
||||||
|
height = height * (self.table.rowCount() + 1)
|
||||||
|
height += self.table.contentsMargins().top()
|
||||||
|
height += self.table.contentsMargins().bottom()
|
||||||
|
self.table.setMaximumHeight(height)
|
||||||
|
self.table.setMinimumHeight(height)
|
||||||
|
self.table.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
|
||||||
|
|
||||||
|
def update_icons(self):
|
||||||
|
"""Draw the correct icons in the left col."""
|
||||||
|
for i in range(self.table.rowCount()):
|
||||||
|
hbox = QtWidgets.QHBoxLayout()
|
||||||
|
iconset = QtWidgets.QWidget()
|
||||||
|
buftype = self.table.item(i, 1).text()
|
||||||
|
for key, qicon in self.action_icons.items():
|
||||||
|
field = self.pane.fields[buftype + "." + key]
|
||||||
|
if isinstance(field, QtWidgets.QCheckBox):
|
||||||
|
val = "on" if field.isChecked() else "off"
|
||||||
|
else:
|
||||||
|
val = field.text()
|
||||||
|
iconbtn = QtWidgets.QPushButton()
|
||||||
|
iconbtn.setContentsMargins(0, 0, 0, 0)
|
||||||
|
iconbtn.setFlat(True)
|
||||||
|
iconbtn.setFocusPolicy(QtCore.Qt.NoFocus)
|
||||||
|
if val and val != "off":
|
||||||
|
iconbtn.setIcon(qicon)
|
||||||
|
iconbtn.setStyleSheet(self.icon_widget_qss)
|
||||||
|
iconbtn.setToolTip(key)
|
||||||
|
iconbtn.clicked.connect(lambda i=i: self.table.selectRow(i))
|
||||||
|
hbox.addWidget(iconbtn)
|
||||||
|
iconset.setLayout(hbox)
|
||||||
|
self.table.setCellWidget(i, 0, iconset)
|
||||||
|
|
||||||
|
def _table_row_changed(self):
|
||||||
|
row = self.table.selectionModel().selectedRows()[0].row()
|
||||||
|
self.stack.setCurrentIndex(row)
|
||||||
|
|
||||||
|
|
||||||
|
class PreferencesTreeWidget(QtWidgets.QTreeWidget):
|
||||||
|
"""Widget with tree list of preferences."""
|
||||||
|
def __init__(self, header_label, *args):
|
||||||
|
QtWidgets.QTreeWidget.__init__(*(self,) + args)
|
||||||
|
self.setHeaderLabel(header_label)
|
||||||
|
self.setRootIsDecorated(False)
|
||||||
|
self.setMaximumWidth(180)
|
||||||
|
self.setTextElideMode(QtCore.Qt.ElideNone)
|
||||||
|
self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
|
||||||
|
self.setFocusPolicy(QtCore.Qt.NoFocus)
|
||||||
|
|
||||||
|
|
||||||
|
class PreferencesSliderEdit(QtWidgets.QSlider):
|
||||||
|
"""Percentage slider."""
|
||||||
|
def __init__(self, *args):
|
||||||
|
QtWidgets.QSlider.__init__(*(self,) + args)
|
||||||
|
self.setMinimum(0)
|
||||||
|
self.setMaximum(100)
|
||||||
|
self.setTickPosition(QtWidgets.QSlider.TicksBelow)
|
||||||
|
self.setTickInterval(5)
|
||||||
|
|
||||||
|
def insert(self, percent):
|
||||||
|
self.setValue(int(percent[:-1]))
|
||||||
|
|
||||||
|
def text(self):
|
||||||
|
return str(self.value()) + "%"
|
||||||
|
|
||||||
|
|
||||||
|
class PreferencesColorEdit(QtWidgets.QPushButton):
|
||||||
|
"""Simple color square that changes based on the color selected."""
|
||||||
|
def __init__(self, *args):
|
||||||
|
QtWidgets.QPushButton.__init__(*(self,) + args)
|
||||||
|
self.color = "#000000"
|
||||||
|
self.clicked.connect(self._color_picker)
|
||||||
|
# Some of the configured colors use a astrisk prefix.
|
||||||
|
# Toggle this on right click.
|
||||||
|
self.star = False
|
||||||
|
self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
|
||||||
|
self.customContextMenuRequested.connect(self._color_star)
|
||||||
|
|
||||||
|
def insert(self, color):
|
||||||
|
"""Insert the desired color for the widget."""
|
||||||
|
if color[:1] == "*":
|
||||||
|
self.star = True
|
||||||
|
color = color[1:]
|
||||||
|
self.setText("*" if self.star else "")
|
||||||
|
self.color = color
|
||||||
|
self.setStyleSheet("background-color: " + color)
|
||||||
|
|
||||||
|
def text(self):
|
||||||
|
"""Returns the hex value of the color."""
|
||||||
|
return ("*" if self.star else "") + self.color
|
||||||
|
|
||||||
|
def _color_picker(self):
|
||||||
|
color = QtWidgets.QColorDialog.getColor(self.color)
|
||||||
|
self.insert(color.name())
|
||||||
|
|
||||||
|
def _color_star(self):
|
||||||
|
self.star = not self.star
|
||||||
|
self.insert(self.text())
|
||||||
|
|
||||||
|
|
||||||
|
class PreferencesFontEdit(QtWidgets.QWidget):
|
||||||
|
"""Font entry and selection."""
|
||||||
|
def __init__(self, *args):
|
||||||
|
QtWidgets.QWidget.__init__(*(self,) + args)
|
||||||
|
layout = QtWidgets.QHBoxLayout()
|
||||||
|
self.checkbox = QtWidgets.QCheckBox()
|
||||||
|
self.edit = QtWidgets.QLineEdit()
|
||||||
|
self.font = ""
|
||||||
|
self.qfont = None
|
||||||
|
self.button = QtWidgets.QPushButton("C&hoose")
|
||||||
|
self.button.clicked.connect(self._font_picker)
|
||||||
|
self.checkbox.toggled.connect(
|
||||||
|
lambda: self._checkbox_toggled(self.checkbox))
|
||||||
|
layout.addWidget(self.checkbox)
|
||||||
|
layout.addWidget(self.edit)
|
||||||
|
layout.addWidget(self.button)
|
||||||
|
layout.setContentsMargins(0, 0, 0, 0)
|
||||||
|
self.setLayout(layout)
|
||||||
|
|
||||||
|
def insert(self, font_str):
|
||||||
|
"""Insert the font described by the string."""
|
||||||
|
self.font = font_str
|
||||||
|
self.edit.insert(font_str)
|
||||||
|
if font_str:
|
||||||
|
self.qfont = utils.Font.str_to_qfont(font_str)
|
||||||
|
self.edit.setFont(self.qfont)
|
||||||
|
self.checkbox.setChecked(True)
|
||||||
|
self._checkbox_toggled(self.checkbox)
|
||||||
|
else:
|
||||||
|
self.checkbox.setChecked(False)
|
||||||
|
self.qfont = None
|
||||||
|
self._checkbox_toggled(self.checkbox)
|
||||||
|
|
||||||
|
def text(self):
|
||||||
|
"""Returns the human readable font string."""
|
||||||
|
return self.font
|
||||||
|
|
||||||
|
def _font_picker(self):
|
||||||
|
font, ok = QtWidgets.QFontDialog.getFont(self.qfont)
|
||||||
|
if ok:
|
||||||
|
self.insert(utils.Font.qfont_to_str(font))
|
||||||
|
|
||||||
|
def _checkbox_toggled(self, button):
|
||||||
|
if button.isChecked() is False and not self.font == "":
|
||||||
|
self.insert("")
|
||||||
|
self.edit.setEnabled(button.isChecked())
|
||||||
|
self.button.setEnabled(button.isChecked())
|
||||||
|
|
||||||
|
|
||||||
|
class PreferencesFileEdit(QtWidgets.QWidget):
|
||||||
|
"""File entry and selection."""
|
||||||
|
def __init__(self, checkbox=None, caption="Select a file", filter=None,
|
||||||
|
mode="open", *args):
|
||||||
|
QtWidgets.QWidget.__init__(*(self,) + args)
|
||||||
|
layout = QtWidgets.QHBoxLayout()
|
||||||
|
self.caption = caption
|
||||||
|
self.filter = filter
|
||||||
|
self.edit = QtWidgets.QLineEdit()
|
||||||
|
self.file_str = ""
|
||||||
|
self.mode = mode
|
||||||
|
self.button = QtWidgets.QPushButton("B&rowse")
|
||||||
|
self.button.clicked.connect(self._file_picker)
|
||||||
|
if checkbox:
|
||||||
|
self.checkbox = checkbox
|
||||||
|
else:
|
||||||
|
self.checkbox = QtWidgets.QCheckBox()
|
||||||
|
layout.addWidget(self.checkbox)
|
||||||
|
self.checkbox.toggled.connect(
|
||||||
|
lambda: self._checkbox_toggled(self.checkbox))
|
||||||
|
layout.addWidget(self.edit)
|
||||||
|
layout.addWidget(self.button)
|
||||||
|
layout.setContentsMargins(0, 0, 0, 0)
|
||||||
|
self.setLayout(layout)
|
||||||
|
|
||||||
|
def insert(self, file_str):
|
||||||
|
"""Insert the file."""
|
||||||
|
self.file_str = file_str
|
||||||
|
self.edit.insert(file_str)
|
||||||
|
if file_str:
|
||||||
|
self.checkbox.setChecked(True)
|
||||||
|
self._checkbox_toggled(self.checkbox)
|
||||||
|
else:
|
||||||
|
self.checkbox.setChecked(False)
|
||||||
|
self._checkbox_toggled(self.checkbox)
|
||||||
|
|
||||||
|
def text(self):
|
||||||
|
"""Returns the human readable font string."""
|
||||||
|
return self.file_str
|
||||||
|
|
||||||
|
def _file_picker(self):
|
||||||
|
path = ""
|
||||||
|
if self.mode == "save":
|
||||||
|
fn = QtWidgets.QFileDialog.getSaveFileName
|
||||||
|
else:
|
||||||
|
fn = QtWidgets.QFileDialog.getOpenFileName
|
||||||
|
filename, fil = fn(self, self.caption, path, self.filter, self.filter)
|
||||||
|
if filename:
|
||||||
|
self.insert(filename)
|
||||||
|
|
||||||
|
def _checkbox_toggled(self, button):
|
||||||
|
if button.isChecked() is False and not self.file_str == "":
|
||||||
|
self.insert("")
|
||||||
|
self.edit.setEnabled(button.isChecked())
|
||||||
|
self.button.setEnabled(button.isChecked())
|
||||||
|
|
||||||
|
|
||||||
|
class PreferencesPaneWidget(QtWidgets.QWidget):
|
||||||
|
"""
|
||||||
|
Widget with (from top to bottom):
|
||||||
|
title, chat + nicklist (optional) + prompt/input.
|
||||||
|
"""
|
||||||
|
|
||||||
|
disabled_fields = ["show_hostnames", "hide_nick_changes",
|
||||||
|
"hide_join_and_part"]
|
||||||
|
|
||||||
|
def __init__(self, section, section_name):
|
||||||
|
QtWidgets.QWidget.__init__(self)
|
||||||
|
self.grid = QtWidgets.QGridLayout()
|
||||||
|
self.grid.setAlignment(QtCore.Qt.AlignTop)
|
||||||
|
self.section = section
|
||||||
|
self.section_name = section_name
|
||||||
|
self.fields = {}
|
||||||
|
self.setLayout(self.grid)
|
||||||
|
self.grid.setColumnStretch(2, 1)
|
||||||
|
self.grid.setSpacing(10)
|
||||||
|
self.int_validator = QtWidgets.QIntValidator(0, 2147483647, self)
|
||||||
|
toolbar_icons = [
|
||||||
|
('ToolButtonFollowStyle', 'Default'),
|
||||||
|
('ToolButtonIconOnly', 'Icon Only'),
|
||||||
|
('ToolButtonTextOnly', 'Text Only'),
|
||||||
|
('ToolButtonTextBesideIcon', 'Text Alongside Icons'),
|
||||||
|
('ToolButtonTextUnderIcon', 'Text Under Icons')]
|
||||||
|
tray_options = [
|
||||||
|
('always', 'Always'),
|
||||||
|
('unread', 'On Unread Messages'),
|
||||||
|
('never', 'Never'),
|
||||||
|
]
|
||||||
|
list_positions = [
|
||||||
|
('left', 'Left'),
|
||||||
|
('right', 'Right'),
|
||||||
|
]
|
||||||
|
sort_options = ['A-Z Ranked', 'A-Z', 'Z-A Ranked', 'Z-A']
|
||||||
|
focus_opts = ["requested", "always", "never"]
|
||||||
|
self.comboboxes = {"style": QtWidgets.QStyleFactory.keys(),
|
||||||
|
"position": list_positions,
|
||||||
|
"toolbar_icons": toolbar_icons,
|
||||||
|
"focus_new_tabs": focus_opts,
|
||||||
|
"tray_icon": tray_options,
|
||||||
|
"sort": sort_options}
|
||||||
|
|
||||||
|
def addItem(self, key, value, default):
|
||||||
|
"""Add a key-value pair."""
|
||||||
|
line = len(self.fields)
|
||||||
|
name = key.split(".")[-1:][0].capitalize().replace("_", " ")
|
||||||
|
label = QtWidgets.QLabel(name)
|
||||||
|
start = 0
|
||||||
|
|
||||||
|
if self.section == "color":
|
||||||
|
start = 2 * (line % 2)
|
||||||
|
line = line // 2
|
||||||
|
edit = PreferencesColorEdit()
|
||||||
|
edit.setFixedWidth(edit.sizeHint().height())
|
||||||
|
edit.insert(value)
|
||||||
|
label.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
|
||||||
|
elif key == "custom_stylesheet":
|
||||||
|
edit = PreferencesFileEdit(caption='Select QStyleSheet File',
|
||||||
|
filter='*.qss')
|
||||||
|
edit.insert(value)
|
||||||
|
elif name.lower()[-5:] == "sound":
|
||||||
|
edit = PreferencesFileEdit(
|
||||||
|
caption='Select a sound file',
|
||||||
|
filter='Audio Files (*.wav *.mp3 *.ogg)')
|
||||||
|
edit.insert(value)
|
||||||
|
elif name.lower()[-4:] == "font":
|
||||||
|
edit = PreferencesFontEdit()
|
||||||
|
edit.setFixedWidth(200)
|
||||||
|
edit.insert(value)
|
||||||
|
elif key in self.comboboxes.keys():
|
||||||
|
edit = QtWidgets.QComboBox()
|
||||||
|
if len(self.comboboxes[key][0]) == 2:
|
||||||
|
for keyvalue in self.comboboxes[key]:
|
||||||
|
edit.addItem(keyvalue[1], keyvalue[0])
|
||||||
|
# if self.section == "nicks" and key == "position":
|
||||||
|
# edit.addItem("below", "Below Buffer List")
|
||||||
|
# edit.addItem("above", "Above Buffer List")
|
||||||
|
edit.setCurrentIndex(edit.findData(value))
|
||||||
|
else:
|
||||||
|
edit.addItems(self.comboboxes[key])
|
||||||
|
edit.setCurrentIndex(edit.findText(value))
|
||||||
|
edit.setFixedWidth(200)
|
||||||
|
elif default in ["on", "off"]:
|
||||||
|
edit = QtWidgets.QCheckBox()
|
||||||
|
edit.setChecked(value == "on")
|
||||||
|
elif default[-1:] == "%":
|
||||||
|
edit = PreferencesSliderEdit(QtCore.Qt.Horizontal)
|
||||||
|
edit.setFixedWidth(200)
|
||||||
|
edit.insert(value)
|
||||||
|
else:
|
||||||
|
edit = QtWidgets.QLineEdit()
|
||||||
|
edit.setFixedWidth(200)
|
||||||
|
edit.insert(value)
|
||||||
|
if default.isdigit() or key == "port":
|
||||||
|
edit.setValidator(self.int_validator)
|
||||||
|
if key == 'password':
|
||||||
|
edit.setEchoMode(QtWidgets.QLineEdit.Password)
|
||||||
|
if key in self.disabled_fields:
|
||||||
|
edit.setDisabled(True)
|
||||||
|
self.grid.addWidget(label, line, start + 0)
|
||||||
|
self.grid.addWidget(edit, line, start + 1)
|
||||||
|
|
||||||
|
self.fields[key] = edit
|
||||||
|
|
||||||
|
|
||||||
|
class IconTextLabel(QtWidgets.QWidget):
|
||||||
|
"""An icon next to text."""
|
||||||
|
def __init__(self, text=None, icon=None, extent=None):
|
||||||
|
QtWidgets.QWidget.__init__(self)
|
||||||
|
text_label = QtWidgets.QLabel(text)
|
||||||
|
if not extent:
|
||||||
|
extent = text_label.height()
|
||||||
|
icon_label = QtWidgets.QLabel()
|
||||||
|
pixmap = icon.pixmap(extent, QtWidgets.QIcon.Normal, QtWidgets.QIcon.On)
|
||||||
|
icon_label.setPixmap(pixmap)
|
||||||
|
label_layout = QtWidgets.QHBoxLayout()
|
||||||
|
label_layout.addWidget(icon_label)
|
||||||
|
label_layout.addWidget(text_label)
|
||||||
|
label_layout.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
|
||||||
|
self.setLayout(label_layout)
|
555
src/qweechat/preferences.py.new
Normal file
@ -0,0 +1,555 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# preferences.py - preferences dialog box
|
||||||
|
#
|
||||||
|
# Copyright (C) 2016 Ricky Brent <ricky@rickybrent.com>
|
||||||
|
#
|
||||||
|
# This file is part of QWeeChat, a Qt remote GUI for WeeChat.
|
||||||
|
#
|
||||||
|
# QWeeChat 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.
|
||||||
|
#
|
||||||
|
# QWeeChat 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 QWeeChat. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
|
||||||
|
from qweechat.config import (CONFIG_DEFAULT_SECTIONS,
|
||||||
|
CONFIG_DEFAULT_NOTIFICATION_OPTIONS,
|
||||||
|
CONFIG_DEFAULT_OPTIONS, write)
|
||||||
|
import utils
|
||||||
|
|
||||||
|
from qtpy import QtCore, QtGui
|
||||||
|
qicon_from_theme = QtGui.QIcon.fromTheme
|
||||||
|
|
||||||
|
class PreferencesDialog(QtGui.QDialog):
|
||||||
|
"""Preferences dialog."""
|
||||||
|
|
||||||
|
custom_sections = {
|
||||||
|
"look": "Look",
|
||||||
|
"input": "Input Box",
|
||||||
|
"nicks": "Nick List",
|
||||||
|
"buffers": "Buffer List",
|
||||||
|
"buffer_flags": False,
|
||||||
|
"notifications": "Notifications",
|
||||||
|
"color": "Colors",
|
||||||
|
"relay": "Relay/Connection"
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self, name, parent, *args):
|
||||||
|
QtGui.QDialog.__init__(*(self,) + args)
|
||||||
|
self.setModal(True)
|
||||||
|
self.setWindowTitle(name)
|
||||||
|
self.parent = parent
|
||||||
|
self.config = parent.config
|
||||||
|
self.stacked_panes = QtGui.QStackedWidget()
|
||||||
|
self.list_panes = PreferencesTreeWidget("Settings")
|
||||||
|
|
||||||
|
splitter = QtGui.QSplitter()
|
||||||
|
splitter.addWidget(self.list_panes)
|
||||||
|
splitter.addWidget(self.stacked_panes)
|
||||||
|
|
||||||
|
# Follow same order as defaults:
|
||||||
|
section_panes = {}
|
||||||
|
for section in CONFIG_DEFAULT_SECTIONS:
|
||||||
|
item = QtGui.QTreeWidgetItem(section)
|
||||||
|
name = section
|
||||||
|
item.setText(0, section.title())
|
||||||
|
if section in self.custom_sections:
|
||||||
|
if not self.custom_sections[section]:
|
||||||
|
continue
|
||||||
|
item.setText(0, self.custom_sections[section])
|
||||||
|
section_panes[section] = PreferencesPaneWidget(section, name)
|
||||||
|
self.list_panes.addTopLevelItem(item)
|
||||||
|
self.stacked_panes.addWidget(section_panes[section])
|
||||||
|
|
||||||
|
for setting, default in CONFIG_DEFAULT_OPTIONS:
|
||||||
|
section_key = setting.split(".")
|
||||||
|
section = section_key[0]
|
||||||
|
key = ".".join(section_key[1:])
|
||||||
|
section_panes[section].addItem(
|
||||||
|
key, self.config.get(section, key), default)
|
||||||
|
for key, value in self.config.items("color"):
|
||||||
|
section_panes["color"].addItem(key, value, False)
|
||||||
|
notification_field_count = len(section_panes["notifications"].fields)
|
||||||
|
notification = PreferencesNotificationBlock(
|
||||||
|
section_panes["notifications"])
|
||||||
|
section_panes["notifications"].grid.addLayout(
|
||||||
|
notification, notification_field_count, 0, 1, -1)
|
||||||
|
|
||||||
|
self.list_panes.currentItemChanged.connect(self._pane_switch)
|
||||||
|
self.list_panes.setCurrentItem(self.list_panes.topLevelItem(0))
|
||||||
|
|
||||||
|
hbox = QtGui.QHBoxLayout()
|
||||||
|
self.dialog_buttons = QtGui.QDialogButtonBox()
|
||||||
|
self.dialog_buttons.setStandardButtons(
|
||||||
|
QtGui.QDialogButtonBox.Save | QtGui.QDialogButtonBox.Cancel)
|
||||||
|
self.dialog_buttons.rejected.connect(self.close)
|
||||||
|
self.dialog_buttons.accepted.connect(self._save_and_close)
|
||||||
|
|
||||||
|
hbox.addStretch(1)
|
||||||
|
hbox.addWidget(self.dialog_buttons)
|
||||||
|
hbox.addStretch(1)
|
||||||
|
|
||||||
|
vbox = QtGui.QVBoxLayout()
|
||||||
|
vbox.addWidget(splitter)
|
||||||
|
vbox.addLayout(hbox)
|
||||||
|
|
||||||
|
self.setLayout(vbox)
|
||||||
|
self.show()
|
||||||
|
|
||||||
|
def _pane_switch(self, item):
|
||||||
|
"""Switch the visible preference pane."""
|
||||||
|
index = self.list_panes.indexOfTopLevelItem(item)
|
||||||
|
if index >= 0:
|
||||||
|
self.stacked_panes.setCurrentIndex(index)
|
||||||
|
|
||||||
|
def _save_and_close(self):
|
||||||
|
for widget in (self.stacked_panes.widget(i)
|
||||||
|
for i in range(self.stacked_panes.count())):
|
||||||
|
for key, field in widget.fields.items():
|
||||||
|
if isinstance(field, QtGui.QComboBox):
|
||||||
|
text = field.itemText(field.currentIndex())
|
||||||
|
data = field.itemData(field.currentIndex())
|
||||||
|
text = data if data else text
|
||||||
|
elif isinstance(field, QtGui.QCheckBox):
|
||||||
|
text = "on" if field.isChecked() else "off"
|
||||||
|
else:
|
||||||
|
text = field.text()
|
||||||
|
self.config.set(widget.section_name, key, str(text))
|
||||||
|
write(self.config)
|
||||||
|
self.parent.apply_preferences()
|
||||||
|
self.close()
|
||||||
|
|
||||||
|
|
||||||
|
class PreferencesNotificationBlock(QtGui.QVBoxLayout):
|
||||||
|
"""Display notification settings with drill down to configure."""
|
||||||
|
def __init__(self, pane, *args):
|
||||||
|
QtGui.QVBoxLayout.__init__(*(self,) + args)
|
||||||
|
self.section = "notifications"
|
||||||
|
self.config = QtGui.QApplication.instance().config
|
||||||
|
self.pane = pane
|
||||||
|
self.stack = QtGui.QStackedWidget()
|
||||||
|
|
||||||
|
self.table = QtGui.QTableWidget()
|
||||||
|
fg_color = self.table.palette().text().color().name()
|
||||||
|
self.action_labels = {
|
||||||
|
"sound": "Play a sound",
|
||||||
|
"message": "Show a message in a popup",
|
||||||
|
"file": "Log to a file",
|
||||||
|
"taskbar": "Mark taskbar entry",
|
||||||
|
"tray": "Mark systray/indicator",
|
||||||
|
"command": "Run a command"}
|
||||||
|
self.action_icons = {
|
||||||
|
"sound": qicon_from_theme("media-playback-start"),
|
||||||
|
"message": qicon_from_theme("dialog-information"),
|
||||||
|
"file": qicon_from_theme("document-export"),
|
||||||
|
"taskbar": qicon_from_theme("weechat"),
|
||||||
|
"tray": utils.qicon_tint("ic_hot", fg_color),
|
||||||
|
"command": qicon_from_theme("system-run")}
|
||||||
|
self.icon_widget_qss = "padding:0;min-height:10px;min-width:16px;"
|
||||||
|
self.table.resizeColumnsToContents()
|
||||||
|
self.table.setColumnCount(2)
|
||||||
|
self.table.resizeRowsToContents()
|
||||||
|
self.table.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
|
||||||
|
self.table.setSelectionMode(QtGui.QAbstractItemView.SingleSelection)
|
||||||
|
self.table.setHorizontalHeaderLabels(["State", "Type"])
|
||||||
|
self.table.horizontalHeader().setStretchLastSection(True)
|
||||||
|
self.table.horizontalHeader().setHighlightSections(False)
|
||||||
|
self.table.verticalHeader().setVisible(False)
|
||||||
|
self.table.setShowGrid(False)
|
||||||
|
self.table.itemSelectionChanged.connect(self._table_row_changed)
|
||||||
|
|
||||||
|
self.buftypes = {}
|
||||||
|
for key, value in CONFIG_DEFAULT_NOTIFICATION_OPTIONS:
|
||||||
|
buftype, optkey = key.split(".")
|
||||||
|
if buftype not in self.buftypes:
|
||||||
|
self.buftypes[buftype] = {}
|
||||||
|
self.buftypes[buftype][optkey] = self.config.get(self.section, key)
|
||||||
|
for buftype, optkey in self.buftypes.items():
|
||||||
|
self._insert_type(buftype)
|
||||||
|
self.update_icons()
|
||||||
|
self.resize_table()
|
||||||
|
self.addWidget(self.table)
|
||||||
|
self.addWidget(self.stack)
|
||||||
|
self.table.selectRow(0)
|
||||||
|
|
||||||
|
def _insert_type(self, buftype):
|
||||||
|
row = self.table.rowCount()
|
||||||
|
self.table.insertRow(row)
|
||||||
|
buftype_item = QtGui.QTableWidgetItem(buftype)
|
||||||
|
buftype_item.setTextAlignment(QtCore.Qt.AlignCenter)
|
||||||
|
self.table.setItem(row, 0, QtGui.QTableWidgetItem())
|
||||||
|
self.table.setItem(row, 1, buftype_item)
|
||||||
|
subgrid = QtGui.QGridLayout()
|
||||||
|
subgrid.setColumnStretch(2, 1)
|
||||||
|
subgrid.setSpacing(10)
|
||||||
|
|
||||||
|
for key, qicon in self.action_icons.items():
|
||||||
|
value = self.buftypes[buftype][key]
|
||||||
|
line = subgrid.rowCount()
|
||||||
|
label = IconTextLabel(self.action_labels[key], qicon, 16)
|
||||||
|
|
||||||
|
checkbox = QtGui.QCheckBox()
|
||||||
|
span = 1
|
||||||
|
edit = None
|
||||||
|
if key in ("message", "taskbar", "tray"):
|
||||||
|
checkbox.setChecked(value == "on")
|
||||||
|
span = 2
|
||||||
|
elif key == "sound":
|
||||||
|
edit = PreferencesFileEdit(
|
||||||
|
checkbox=checkbox, caption='Select a sound file',
|
||||||
|
filter='Audio Files (*.wav *.mp3 *.ogg)')
|
||||||
|
elif key == "file":
|
||||||
|
edit = PreferencesFileEdit(checkbox=checkbox, mode="save")
|
||||||
|
else:
|
||||||
|
edit = PreferencesFileEdit(checkbox=checkbox)
|
||||||
|
if edit:
|
||||||
|
edit.insert(value)
|
||||||
|
subgrid.addWidget(edit, line, 2)
|
||||||
|
else:
|
||||||
|
edit = checkbox
|
||||||
|
subgrid.addWidget(label, line, 1, 1, span)
|
||||||
|
subgrid.addWidget(checkbox, line, 0)
|
||||||
|
self.pane.fields[buftype + "." + key] = edit
|
||||||
|
subpane = QtGui.QWidget()
|
||||||
|
subpane.setLayout(subgrid)
|
||||||
|
subpane.setMaximumHeight(subgrid.totalMinimumSize().height())
|
||||||
|
self.stack.addWidget(subpane)
|
||||||
|
|
||||||
|
def resize_table(self):
|
||||||
|
"""Fit the table height to contents."""
|
||||||
|
height = self.table.horizontalHeader().height()
|
||||||
|
height = height * (self.table.rowCount() + 1)
|
||||||
|
height += self.table.contentsMargins().top()
|
||||||
|
height += self.table.contentsMargins().bottom()
|
||||||
|
self.table.setMaximumHeight(height)
|
||||||
|
self.table.setMinimumHeight(height)
|
||||||
|
self.table.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
|
||||||
|
|
||||||
|
def update_icons(self):
|
||||||
|
"""Draw the correct icons in the left col."""
|
||||||
|
for i in range(self.table.rowCount()):
|
||||||
|
hbox = QtGui.QHBoxLayout()
|
||||||
|
iconset = QtGui.QWidget()
|
||||||
|
buftype = self.table.item(i, 1).text()
|
||||||
|
for key, qicon in self.action_icons.items():
|
||||||
|
field = self.pane.fields[buftype + "." + key]
|
||||||
|
if isinstance(field, QtGui.QCheckBox):
|
||||||
|
val = "on" if field.isChecked() else "off"
|
||||||
|
else:
|
||||||
|
val = field.text()
|
||||||
|
iconbtn = QtGui.QPushButton()
|
||||||
|
iconbtn.setContentsMargins(0, 0, 0, 0)
|
||||||
|
iconbtn.setFlat(True)
|
||||||
|
iconbtn.setFocusPolicy(QtCore.Qt.NoFocus)
|
||||||
|
if val and val != "off":
|
||||||
|
iconbtn.setIcon(qicon)
|
||||||
|
iconbtn.setStyleSheet(self.icon_widget_qss)
|
||||||
|
iconbtn.setToolTip(key)
|
||||||
|
iconbtn.clicked.connect(lambda i=i: self.table.selectRow(i))
|
||||||
|
hbox.addWidget(iconbtn)
|
||||||
|
iconset.setLayout(hbox)
|
||||||
|
self.table.setCellWidget(i, 0, iconset)
|
||||||
|
|
||||||
|
def _table_row_changed(self):
|
||||||
|
row = self.table.selectionModel().selectedRows()[0].row()
|
||||||
|
self.stack.setCurrentIndex(row)
|
||||||
|
|
||||||
|
|
||||||
|
class PreferencesTreeWidget(QtGui.QTreeWidget):
|
||||||
|
"""Widget with tree list of preferences."""
|
||||||
|
def __init__(self, header_label, *args):
|
||||||
|
QtGui.QTreeWidget.__init__(*(self,) + args)
|
||||||
|
self.setHeaderLabel(header_label)
|
||||||
|
self.setRootIsDecorated(False)
|
||||||
|
self.setMaximumWidth(180)
|
||||||
|
self.setTextElideMode(QtCore.Qt.ElideNone)
|
||||||
|
self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
|
||||||
|
self.setFocusPolicy(QtCore.Qt.NoFocus)
|
||||||
|
|
||||||
|
|
||||||
|
class PreferencesSliderEdit(QtGui.QSlider):
|
||||||
|
"""Percentage slider."""
|
||||||
|
def __init__(self, *args):
|
||||||
|
QtGui.QSlider.__init__(*(self,) + args)
|
||||||
|
self.setMinimum(0)
|
||||||
|
self.setMaximum(100)
|
||||||
|
self.setTickPosition(QtGui.QSlider.TicksBelow)
|
||||||
|
self.setTickInterval(5)
|
||||||
|
|
||||||
|
def insert(self, percent):
|
||||||
|
self.setValue(int(percent[:-1]))
|
||||||
|
|
||||||
|
def text(self):
|
||||||
|
return str(self.value()) + "%"
|
||||||
|
|
||||||
|
|
||||||
|
class PreferencesColorEdit(QtGui.QPushButton):
|
||||||
|
"""Simple color square that changes based on the color selected."""
|
||||||
|
def __init__(self, *args):
|
||||||
|
QtGui.QPushButton.__init__(*(self,) + args)
|
||||||
|
self.color = "#000000"
|
||||||
|
self.clicked.connect(self._color_picker)
|
||||||
|
# Some of the configured colors use a astrisk prefix.
|
||||||
|
# Toggle this on right click.
|
||||||
|
self.star = False
|
||||||
|
self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
|
||||||
|
self.customContextMenuRequested.connect(self._color_star)
|
||||||
|
|
||||||
|
def insert(self, color):
|
||||||
|
"""Insert the desired color for the widget."""
|
||||||
|
if color[:1] == "*":
|
||||||
|
self.star = True
|
||||||
|
color = color[1:]
|
||||||
|
self.setText("*" if self.star else "")
|
||||||
|
self.color = color
|
||||||
|
self.setStyleSheet("background-color: " + color)
|
||||||
|
|
||||||
|
def text(self):
|
||||||
|
"""Returns the hex value of the color."""
|
||||||
|
return ("*" if self.star else "") + self.color
|
||||||
|
|
||||||
|
def _color_picker(self):
|
||||||
|
color = QtGui.QColorDialog.getColor(self.color)
|
||||||
|
self.insert(color.name())
|
||||||
|
|
||||||
|
def _color_star(self):
|
||||||
|
self.star = not self.star
|
||||||
|
self.insert(self.text())
|
||||||
|
|
||||||
|
|
||||||
|
class PreferencesFontEdit(QtGui.QWidget):
|
||||||
|
"""Font entry and selection."""
|
||||||
|
def __init__(self, *args):
|
||||||
|
QtGui.QWidget.__init__(*(self,) + args)
|
||||||
|
layout = QtGui.QHBoxLayout()
|
||||||
|
self.checkbox = QtGui.QCheckBox()
|
||||||
|
self.edit = QtGui.QLineEdit()
|
||||||
|
self.font = ""
|
||||||
|
self.qfont = None
|
||||||
|
self.button = QtGui.QPushButton("C&hoose")
|
||||||
|
self.button.clicked.connect(self._font_picker)
|
||||||
|
self.checkbox.toggled.connect(
|
||||||
|
lambda: self._checkbox_toggled(self.checkbox))
|
||||||
|
layout.addWidget(self.checkbox)
|
||||||
|
layout.addWidget(self.edit)
|
||||||
|
layout.addWidget(self.button)
|
||||||
|
layout.setContentsMargins(0, 0, 0, 0)
|
||||||
|
self.setLayout(layout)
|
||||||
|
|
||||||
|
def insert(self, font_str):
|
||||||
|
"""Insert the font described by the string."""
|
||||||
|
self.font = font_str
|
||||||
|
self.edit.insert(font_str)
|
||||||
|
if font_str:
|
||||||
|
self.qfont = utils.Font.str_to_qfont(font_str)
|
||||||
|
self.edit.setFont(self.qfont)
|
||||||
|
self.checkbox.setChecked(True)
|
||||||
|
self._checkbox_toggled(self.checkbox)
|
||||||
|
else:
|
||||||
|
self.checkbox.setChecked(False)
|
||||||
|
self.qfont = None
|
||||||
|
self._checkbox_toggled(self.checkbox)
|
||||||
|
|
||||||
|
def text(self):
|
||||||
|
"""Returns the human readable font string."""
|
||||||
|
return self.font
|
||||||
|
|
||||||
|
def _font_picker(self):
|
||||||
|
font, ok = QtGui.QFontDialog.getFont(self.qfont)
|
||||||
|
if ok:
|
||||||
|
self.insert(utils.Font.qfont_to_str(font))
|
||||||
|
|
||||||
|
def _checkbox_toggled(self, button):
|
||||||
|
if button.isChecked() is False and not self.font == "":
|
||||||
|
self.insert("")
|
||||||
|
self.edit.setEnabled(button.isChecked())
|
||||||
|
self.button.setEnabled(button.isChecked())
|
||||||
|
|
||||||
|
|
||||||
|
class PreferencesFileEdit(QtGui.QWidget):
|
||||||
|
"""File entry and selection."""
|
||||||
|
def __init__(self, checkbox=None, caption="Select a file", filter=None,
|
||||||
|
mode="open", *args):
|
||||||
|
QtGui.QWidget.__init__(*(self,) + args)
|
||||||
|
layout = QtGui.QHBoxLayout()
|
||||||
|
self.caption = caption
|
||||||
|
self.filter = filter
|
||||||
|
self.edit = QtGui.QLineEdit()
|
||||||
|
self.file_str = ""
|
||||||
|
self.mode = mode
|
||||||
|
self.button = QtGui.QPushButton("B&rowse")
|
||||||
|
self.button.clicked.connect(self._file_picker)
|
||||||
|
if checkbox:
|
||||||
|
self.checkbox = checkbox
|
||||||
|
else:
|
||||||
|
self.checkbox = QtGui.QCheckBox()
|
||||||
|
layout.addWidget(self.checkbox)
|
||||||
|
self.checkbox.toggled.connect(
|
||||||
|
lambda: self._checkbox_toggled(self.checkbox))
|
||||||
|
layout.addWidget(self.edit)
|
||||||
|
layout.addWidget(self.button)
|
||||||
|
layout.setContentsMargins(0, 0, 0, 0)
|
||||||
|
self.setLayout(layout)
|
||||||
|
|
||||||
|
def insert(self, file_str):
|
||||||
|
"""Insert the file."""
|
||||||
|
self.file_str = file_str
|
||||||
|
self.edit.insert(file_str)
|
||||||
|
if file_str:
|
||||||
|
self.checkbox.setChecked(True)
|
||||||
|
self._checkbox_toggled(self.checkbox)
|
||||||
|
else:
|
||||||
|
self.checkbox.setChecked(False)
|
||||||
|
self._checkbox_toggled(self.checkbox)
|
||||||
|
|
||||||
|
def text(self):
|
||||||
|
"""Returns the human readable font string."""
|
||||||
|
return self.file_str
|
||||||
|
|
||||||
|
def _file_picker(self):
|
||||||
|
path = ""
|
||||||
|
if self.mode == "save":
|
||||||
|
fn = QtGui.QFileDialog.getSaveFileName
|
||||||
|
else:
|
||||||
|
fn = QtGui.QFileDialog.getOpenFileName
|
||||||
|
filename, fil = fn(self, self.caption, path, self.filter, self.filter)
|
||||||
|
if filename:
|
||||||
|
self.insert(filename)
|
||||||
|
|
||||||
|
def _checkbox_toggled(self, button):
|
||||||
|
if button.isChecked() is False and not self.file_str == "":
|
||||||
|
self.insert("")
|
||||||
|
self.edit.setEnabled(button.isChecked())
|
||||||
|
self.button.setEnabled(button.isChecked())
|
||||||
|
|
||||||
|
|
||||||
|
class PreferencesPaneWidget(QtGui.QWidget):
|
||||||
|
"""
|
||||||
|
Widget with (from top to bottom):
|
||||||
|
title, chat + nicklist (optional) + prompt/input.
|
||||||
|
"""
|
||||||
|
|
||||||
|
disabled_fields = ["show_hostnames", "hide_nick_changes",
|
||||||
|
"hide_join_and_part"]
|
||||||
|
|
||||||
|
def __init__(self, section, section_name):
|
||||||
|
QtGui.QWidget.__init__(self)
|
||||||
|
self.grid = QtGui.QGridLayout()
|
||||||
|
self.grid.setAlignment(QtCore.Qt.AlignTop)
|
||||||
|
self.section = section
|
||||||
|
self.section_name = section_name
|
||||||
|
self.fields = {}
|
||||||
|
self.setLayout(self.grid)
|
||||||
|
self.grid.setColumnStretch(2, 1)
|
||||||
|
self.grid.setSpacing(10)
|
||||||
|
self.int_validator = QtGui.QIntValidator(0, 2147483647, self)
|
||||||
|
toolbar_icons = [
|
||||||
|
('ToolButtonFollowStyle', 'Default'),
|
||||||
|
('ToolButtonIconOnly', 'Icon Only'),
|
||||||
|
('ToolButtonTextOnly', 'Text Only'),
|
||||||
|
('ToolButtonTextBesideIcon', 'Text Alongside Icons'),
|
||||||
|
('ToolButtonTextUnderIcon', 'Text Under Icons')]
|
||||||
|
tray_options = [
|
||||||
|
('always', 'Always'),
|
||||||
|
('unread', 'On Unread Messages'),
|
||||||
|
('never', 'Never'),
|
||||||
|
]
|
||||||
|
list_positions = [
|
||||||
|
('left', 'Left'),
|
||||||
|
('right', 'Right'),
|
||||||
|
]
|
||||||
|
sort_options = ['A-Z Ranked', 'A-Z', 'Z-A Ranked', 'Z-A']
|
||||||
|
focus_opts = ["requested", "always", "never"]
|
||||||
|
self.comboboxes = {"style": QtGui.QStyleFactory.keys(),
|
||||||
|
"position": list_positions,
|
||||||
|
"toolbar_icons": toolbar_icons,
|
||||||
|
"focus_new_tabs": focus_opts,
|
||||||
|
"tray_icon": tray_options,
|
||||||
|
"sort": sort_options}
|
||||||
|
|
||||||
|
def addItem(self, key, value, default):
|
||||||
|
"""Add a key-value pair."""
|
||||||
|
line = len(self.fields)
|
||||||
|
name = key.split(".")[-1:][0].capitalize().replace("_", " ")
|
||||||
|
label = QtGui.QLabel(name)
|
||||||
|
start = 0
|
||||||
|
|
||||||
|
if self.section == "color":
|
||||||
|
start = 2 * (line % 2)
|
||||||
|
line = line // 2
|
||||||
|
edit = PreferencesColorEdit()
|
||||||
|
edit.setFixedWidth(edit.sizeHint().height())
|
||||||
|
edit.insert(value)
|
||||||
|
label.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
|
||||||
|
elif key == "custom_stylesheet":
|
||||||
|
edit = PreferencesFileEdit(caption='Select QStyleSheet File',
|
||||||
|
filter='*.qss')
|
||||||
|
edit.insert(value)
|
||||||
|
elif name.lower()[-5:] == "sound":
|
||||||
|
edit = PreferencesFileEdit(
|
||||||
|
caption='Select a sound file',
|
||||||
|
filter='Audio Files (*.wav *.mp3 *.ogg)')
|
||||||
|
edit.insert(value)
|
||||||
|
elif name.lower()[-4:] == "font":
|
||||||
|
edit = PreferencesFontEdit()
|
||||||
|
edit.setFixedWidth(200)
|
||||||
|
edit.insert(value)
|
||||||
|
elif key in self.comboboxes.keys():
|
||||||
|
edit = QtGui.QComboBox()
|
||||||
|
if len(self.comboboxes[key][0]) == 2:
|
||||||
|
for keyvalue in self.comboboxes[key]:
|
||||||
|
edit.addItem(keyvalue[1], keyvalue[0])
|
||||||
|
# if self.section == "nicks" and key == "position":
|
||||||
|
# edit.addItem("below", "Below Buffer List")
|
||||||
|
# edit.addItem("above", "Above Buffer List")
|
||||||
|
edit.setCurrentIndex(edit.findData(value))
|
||||||
|
else:
|
||||||
|
edit.addItems(self.comboboxes[key])
|
||||||
|
edit.setCurrentIndex(edit.findText(value))
|
||||||
|
edit.setFixedWidth(200)
|
||||||
|
elif default in ["on", "off"]:
|
||||||
|
edit = QtGui.QCheckBox()
|
||||||
|
edit.setChecked(value == "on")
|
||||||
|
elif default[-1:] == "%":
|
||||||
|
edit = PreferencesSliderEdit(QtCore.Qt.Horizontal)
|
||||||
|
edit.setFixedWidth(200)
|
||||||
|
edit.insert(value)
|
||||||
|
else:
|
||||||
|
edit = QtGui.QLineEdit()
|
||||||
|
edit.setFixedWidth(200)
|
||||||
|
edit.insert(value)
|
||||||
|
if default.isdigit() or key == "port":
|
||||||
|
edit.setValidator(self.int_validator)
|
||||||
|
if key == 'password':
|
||||||
|
edit.setEchoMode(QtGui.QLineEdit.Password)
|
||||||
|
if key in self.disabled_fields:
|
||||||
|
edit.setDisabled(True)
|
||||||
|
self.grid.addWidget(label, line, start + 0)
|
||||||
|
self.grid.addWidget(edit, line, start + 1)
|
||||||
|
|
||||||
|
self.fields[key] = edit
|
||||||
|
|
||||||
|
|
||||||
|
class IconTextLabel(QtGui.QWidget):
|
||||||
|
"""An icon next to text."""
|
||||||
|
def __init__(self, text=None, icon=None, extent=None):
|
||||||
|
QtGui.QWidget.__init__(self)
|
||||||
|
text_label = QtGui.QLabel(text)
|
||||||
|
if not extent:
|
||||||
|
extent = text_label.height()
|
||||||
|
icon_label = QtGui.QLabel()
|
||||||
|
pixmap = icon.pixmap(extent, QtGui.QIcon.Normal, QtGui.QIcon.On)
|
||||||
|
icon_label.setPixmap(pixmap)
|
||||||
|
label_layout = QtGui.QHBoxLayout()
|
||||||
|
label_layout.addWidget(icon_label)
|
||||||
|
label_layout.addWidget(text_label)
|
||||||
|
label_layout.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
|
||||||
|
self.setLayout(label_layout)
|
@ -2,7 +2,7 @@
|
|||||||
#
|
#
|
||||||
# qweechat.py - WeeChat remote GUI using Qt toolkit
|
# qweechat.py - WeeChat remote GUI using Qt toolkit
|
||||||
#
|
#
|
||||||
# Copyright (C) 2011-2021 Sébastien Helleu <flashcode@flashtux.org>
|
# Copyright (C) 2011-2022 Sébastien Helleu <flashcode@flashtux.org>
|
||||||
#
|
#
|
||||||
# This file is part of QWeeChat, a Qt remote GUI for WeeChat.
|
# This file is part of QWeeChat, a Qt remote GUI for WeeChat.
|
||||||
#
|
#
|
||||||
@ -35,15 +35,16 @@ It requires requires WeeChat 0.3.7 or newer, running on local or remote host.
|
|||||||
|
|
||||||
import sys
|
import sys
|
||||||
import traceback
|
import traceback
|
||||||
|
import signal
|
||||||
from pkg_resources import resource_filename
|
from pkg_resources import resource_filename
|
||||||
|
|
||||||
from PySide6 import QtCore, QtGui, QtWidgets
|
from qtpy import QtCore, QtGui, QtWidgets
|
||||||
|
|
||||||
from qweechat import config
|
from qweechat.config import read, write
|
||||||
from qweechat.about import AboutDialog
|
from qweechat.about import AboutDialog
|
||||||
from qweechat.buffer import BufferListWidget, Buffer
|
from qweechat.buffer import BufferListWidget, Buffer
|
||||||
from qweechat.connection import ConnectionDialog
|
from qweechat.connection import ConnectionDialog
|
||||||
from qweechat.network import Network, STATUS_DISCONNECTED, NETWORK_STATUS
|
from qweechat.network import Network, STATUS_DISCONNECTED
|
||||||
from qweechat.preferences import PreferencesDialog
|
from qweechat.preferences import PreferencesDialog
|
||||||
from qweechat.weechat import protocol
|
from qweechat.weechat import protocol
|
||||||
|
|
||||||
@ -52,16 +53,16 @@ APP_NAME = 'QWeeChat'
|
|||||||
AUTHOR = 'Sébastien Helleu'
|
AUTHOR = 'Sébastien Helleu'
|
||||||
WEECHAT_SITE = 'https://weechat.org/'
|
WEECHAT_SITE = 'https://weechat.org/'
|
||||||
|
|
||||||
|
# not QFrame
|
||||||
class MainWindow(QtWidgets.QMainWindow):
|
class MainWindow(QtWidgets.QMainWindow):
|
||||||
"""Main window."""
|
"""Main window."""
|
||||||
|
|
||||||
def __init__(self, *args):
|
def __init__(self, *args):
|
||||||
super().__init__(*args)
|
super().__init__(*args)
|
||||||
|
|
||||||
self.config = config.read()
|
self.config = read()
|
||||||
|
|
||||||
self.resize(1000, 600)
|
self.resize(800, 600)
|
||||||
self.setWindowTitle(APP_NAME)
|
self.setWindowTitle(APP_NAME)
|
||||||
|
|
||||||
self.about_dialog = None
|
self.about_dialog = None
|
||||||
@ -87,10 +88,16 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||||||
splitter.addWidget(self.list_buffers)
|
splitter.addWidget(self.list_buffers)
|
||||||
splitter.addWidget(self.stacked_buffers)
|
splitter.addWidget(self.stacked_buffers)
|
||||||
|
|
||||||
|
self.list_buffers.setSizePolicy(QtWidgets.QSizePolicy.Preferred,
|
||||||
|
QtWidgets.QSizePolicy.Preferred)
|
||||||
|
self.stacked_buffers.setSizePolicy(QtWidgets.QSizePolicy.Expanding,
|
||||||
|
QtWidgets.QSizePolicy.Expanding)
|
||||||
|
# MainWindow
|
||||||
self.setCentralWidget(splitter)
|
self.setCentralWidget(splitter)
|
||||||
|
|
||||||
if self.config.getboolean('look', 'statusbar'):
|
if self.config.getboolean('look', 'statusbar'):
|
||||||
self.statusBar().visible = True
|
self.statusBar().visible = True
|
||||||
|
self.statusBar().visible = True
|
||||||
|
|
||||||
# actions for menu and toolbar
|
# actions for menu and toolbar
|
||||||
actions_def = {
|
actions_def = {
|
||||||
@ -139,7 +146,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||||||
}
|
}
|
||||||
self.actions = {}
|
self.actions = {}
|
||||||
for name, action in list(actions_def.items()):
|
for name, action in list(actions_def.items()):
|
||||||
self.actions[name] = QtGui.QAction(
|
self.actions[name] = QtWidgets.QAction(
|
||||||
QtGui.QIcon(
|
QtGui.QIcon(
|
||||||
resource_filename(__name__, 'data/icons/%s' % action[0])),
|
resource_filename(__name__, 'data/icons/%s' % action[0])),
|
||||||
name.capitalize(), self)
|
name.capitalize(), self)
|
||||||
@ -164,20 +171,22 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||||||
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)
|
||||||
self.network_status.setAlignment(QtCore.Qt.AlignRight)
|
self.network_status.setAlignment(QtCore.Qt.AlignRight)
|
||||||
if hasattr(self.menu, 'setCornerWidget'):
|
if hasattr(self, 'menuBar'):
|
||||||
self.menu.setCornerWidget(self.network_status,
|
if hasattr(self.menu, 'setCornerWidget'):
|
||||||
QtCore.Qt.TopRightCorner)
|
self.menu.setCornerWidget(self.network_status,
|
||||||
|
QtCore.Qt.TopRightCorner)
|
||||||
self.network_status_set(STATUS_DISCONNECTED)
|
self.network_status_set(STATUS_DISCONNECTED)
|
||||||
|
|
||||||
# toolbar
|
# toolbar
|
||||||
toolbar = self.addToolBar('toolBar')
|
if hasattr(self, 'addToolBar'):
|
||||||
toolbar.setToolButtonStyle(QtCore.Qt.ToolButtonTextUnderIcon)
|
toolbar = self.addToolBar('toolBar')
|
||||||
toolbar.addActions([self.actions['connect'],
|
toolbar.setToolButtonStyle(QtCore.Qt.ToolButtonTextUnderIcon)
|
||||||
self.actions['disconnect'],
|
toolbar.addActions([self.actions['connect'],
|
||||||
self.actions['debug'],
|
self.actions['disconnect'],
|
||||||
self.actions['preferences'],
|
self.actions['debug'],
|
||||||
self.actions['about'],
|
self.actions['preferences'],
|
||||||
self.actions['quit']])
|
self.actions['about'],
|
||||||
|
self.actions['quit']])
|
||||||
|
|
||||||
self.buffers[0].widget.input.setFocus()
|
self.buffers[0].widget.input.setFocus()
|
||||||
|
|
||||||
@ -188,12 +197,12 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||||||
# auto-connect to relay
|
# auto-connect to relay
|
||||||
if self.config.getboolean('relay', 'autoconnect'):
|
if self.config.getboolean('relay', 'autoconnect'):
|
||||||
self.network.connect_weechat(
|
self.network.connect_weechat(
|
||||||
server=self.config.get('relay', 'server'),
|
hostname=self.config.get('relay', 'hostname', fallback='127.0.0.1'),
|
||||||
port=self.config.get('relay', 'port'),
|
port=self.config.get('relay', 'port', fallback='9000'),
|
||||||
ssl=self.config.getboolean('relay', 'ssl'),
|
ssl=self.config.getboolean('relay', 'ssl', fallback=''),
|
||||||
password=self.config.get('relay', 'password'),
|
password=self.config.get('relay', 'password', fallback=''),
|
||||||
totp=None,
|
totp=None,
|
||||||
lines=self.config.get('relay', 'lines'),
|
lines=self.config.get('relay', 'lines', fallback=''),
|
||||||
)
|
)
|
||||||
|
|
||||||
self.show()
|
self.show()
|
||||||
@ -230,8 +239,8 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||||||
def open_connection_dialog(self):
|
def open_connection_dialog(self):
|
||||||
"""Open a dialog with connection settings."""
|
"""Open a dialog with connection settings."""
|
||||||
values = {}
|
values = {}
|
||||||
for option in ('server', 'port', 'ssl', 'password', 'lines'):
|
for option in ('hostname', 'port', 'ssl', 'password', 'lines'):
|
||||||
values[option] = self.config.get('relay', option)
|
values[option] = self.config.get('relay', option, fallback='')
|
||||||
self.connection_dialog = ConnectionDialog(values, self)
|
self.connection_dialog = ConnectionDialog(values, self)
|
||||||
self.connection_dialog.dialog_buttons.accepted.connect(
|
self.connection_dialog.dialog_buttons.accepted.connect(
|
||||||
self.connect_weechat)
|
self.connect_weechat)
|
||||||
@ -239,7 +248,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||||||
def connect_weechat(self):
|
def connect_weechat(self):
|
||||||
"""Connect to WeeChat."""
|
"""Connect to WeeChat."""
|
||||||
self.network.connect_weechat(
|
self.network.connect_weechat(
|
||||||
server=self.connection_dialog.fields['server'].text(),
|
hostname=self.connection_dialog.fields['hostname'].text(),
|
||||||
port=self.connection_dialog.fields['port'].text(),
|
port=self.connection_dialog.fields['port'].text(),
|
||||||
ssl=self.connection_dialog.fields['ssl'].isChecked(),
|
ssl=self.connection_dialog.fields['ssl'].isChecked(),
|
||||||
password=self.connection_dialog.fields['password'].text(),
|
password=self.connection_dialog.fields['password'].text(),
|
||||||
@ -258,8 +267,12 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||||||
def network_status_set(self, status):
|
def network_status_set(self, status):
|
||||||
"""Set the network status."""
|
"""Set the network status."""
|
||||||
pal = self.network_status.palette()
|
pal = self.network_status.palette()
|
||||||
pal.setColor(self.network_status.foregroundRole(),
|
try:
|
||||||
|
pal.setColor(self.network_status.foregroundRole(),
|
||||||
self.network.status_color(status))
|
self.network.status_color(status))
|
||||||
|
except:
|
||||||
|
# dunno
|
||||||
|
pass
|
||||||
ssl = ' (SSL)' if status != STATUS_DISCONNECTED \
|
ssl = ' (SSL)' if status != STATUS_DISCONNECTED \
|
||||||
and self.network.is_ssl() else ''
|
and self.network.is_ssl() else ''
|
||||||
self.network_status.setPalette(pal)
|
self.network_status.setPalette(pal)
|
||||||
@ -511,8 +524,10 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||||||
else:
|
else:
|
||||||
index = [i for i, b in enumerate(self.buffers)
|
index = [i for i, b in enumerate(self.buffers)
|
||||||
if b.pointer() == next_buffer]
|
if b.pointer() == next_buffer]
|
||||||
if index:
|
if len(index):
|
||||||
index = index[0]
|
index = index[0]
|
||||||
|
else:
|
||||||
|
index = -1
|
||||||
if index < 0:
|
if index < 0:
|
||||||
print('Warning: unable to find position for buffer, using end of '
|
print('Warning: unable to find position for buffer, using end of '
|
||||||
'list by default')
|
'list by default')
|
||||||
@ -524,19 +539,30 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||||||
self.network.disconnect_weechat()
|
self.network.disconnect_weechat()
|
||||||
if self.network.debug_dialog:
|
if self.network.debug_dialog:
|
||||||
self.network.debug_dialog.close()
|
self.network.debug_dialog.close()
|
||||||
config.write(self.config)
|
write(self.config)
|
||||||
QtWidgets.QMainWindow.closeEvent(self, event)
|
QtWidgets.QFrame.closeEvent(self, event)
|
||||||
|
|
||||||
|
def iMain(lArgs=None):
|
||||||
def main():
|
if lArgs is None and len(sys.argv) > 1:
|
||||||
app = QtWidgets.QApplication(sys.argv)
|
lArgs = sys.argv[1:]
|
||||||
|
elif lArgs is None:
|
||||||
|
lArgs = []
|
||||||
|
app = QtWidgets.QApplication(lArgs)
|
||||||
app.setStyle(QtWidgets.QStyleFactory.create('Cleanlooks'))
|
app.setStyle(QtWidgets.QStyleFactory.create('Cleanlooks'))
|
||||||
app.setWindowIcon(QtGui.QIcon(
|
app.setWindowIcon(QtGui.QIcon(
|
||||||
resource_filename(__name__, 'data/icons/weechat.png')))
|
resource_filename(__name__, 'data/icons/weechat.png')))
|
||||||
main_win = MainWindow()
|
main_win = MainWindow()
|
||||||
main_win.show()
|
main_win.show()
|
||||||
sys.exit(app.exec_())
|
i = app.exec_()
|
||||||
|
return i
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
signal.signal(signal.SIGINT, signal.SIG_DFL)
|
||||||
|
try:
|
||||||
|
i = iMain()
|
||||||
|
except KeyboardInterrupt as e:
|
||||||
|
i = 0
|
||||||
|
except Exception as e:
|
||||||
|
LOG.exception(f"Exception {e}")
|
||||||
|
i = 1
|
||||||
|
sys.exit(i)
|
@ -2,7 +2,7 @@
|
|||||||
#
|
#
|
||||||
# version.py - version of QWeeChat
|
# version.py - version of QWeeChat
|
||||||
#
|
#
|
||||||
# Copyright (C) 2011-2021 Sébastien Helleu <flashcode@flashtux.org>
|
# Copyright (C) 2011-2022 Sébastien Helleu <flashcode@flashtux.org>
|
||||||
#
|
#
|
||||||
# This file is part of QWeeChat, a Qt remote GUI for WeeChat.
|
# This file is part of QWeeChat, a Qt remote GUI for WeeChat.
|
||||||
#
|
#
|
||||||
@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
"""Version of QWeeChat."""
|
"""Version of QWeeChat."""
|
||||||
|
|
||||||
VERSION = '0.0.1-dev'
|
VERSION = '1.0.0'
|
||||||
|
|
||||||
|
|
||||||
def qweechat_version():
|
def qweechat_version():
|
@ -1,6 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 2011-2021 Sébastien Helleu <flashcode@flashtux.org>
|
# Copyright (C) 2011-2022 Sébastien Helleu <flashcode@flashtux.org>
|
||||||
#
|
#
|
||||||
# This file is part of QWeeChat, a Qt remote GUI for WeeChat.
|
# This file is part of QWeeChat, a Qt remote GUI for WeeChat.
|
||||||
#
|
#
|
@ -2,7 +2,7 @@
|
|||||||
#
|
#
|
||||||
# color.py - remove/replace colors in WeeChat strings
|
# color.py - remove/replace colors in WeeChat strings
|
||||||
#
|
#
|
||||||
# Copyright (C) 2011-2021 Sébastien Helleu <flashcode@flashtux.org>
|
# Copyright (C) 2011-2022 Sébastien Helleu <flashcode@flashtux.org>
|
||||||
#
|
#
|
||||||
# This file is part of QWeeChat, a Qt remote GUI for WeeChat.
|
# This file is part of QWeeChat, a Qt remote GUI for WeeChat.
|
||||||
#
|
#
|
@ -2,7 +2,7 @@
|
|||||||
#
|
#
|
||||||
# protocol.py - decode binary messages received from WeeChat/relay
|
# protocol.py - decode binary messages received from WeeChat/relay
|
||||||
#
|
#
|
||||||
# Copyright (C) 2011-2021 Sébastien Helleu <flashcode@flashtux.org>
|
# Copyright (C) 2011-2022 Sébastien Helleu <flashcode@flashtux.org>
|
||||||
#
|
#
|
||||||
# This file is part of QWeeChat, a Qt remote GUI for WeeChat.
|
# This file is part of QWeeChat, a Qt remote GUI for WeeChat.
|
||||||
#
|
#
|
@ -2,7 +2,7 @@
|
|||||||
#
|
#
|
||||||
# testproto.py - command-line program for testing WeeChat/relay protocol
|
# testproto.py - command-line program for testing WeeChat/relay protocol
|
||||||
#
|
#
|
||||||
# Copyright (C) 2013-2021 Sébastien Helleu <flashcode@flashtux.org>
|
# Copyright (C) 2013-2022 Sébastien Helleu <flashcode@flashtux.org>
|
||||||
#
|
#
|
||||||
# This file is part of QWeeChat, a Qt remote GUI for WeeChat.
|
# This file is part of QWeeChat, a Qt remote GUI for WeeChat.
|
||||||
#
|
#
|