Allow user to set a base Qt window style #2251

Merged
PeterSurda merged 7 commits from gitea-91 into v0.6 2024-06-16 06:43:57 +02:00
7 changed files with 162 additions and 17 deletions

View File

@ -62,6 +62,9 @@ except ImportError:
get_plugins = False get_plugins = False
is_windows = sys.platform.startswith('win')
# TODO: rewrite # TODO: rewrite
def powQueueSize(): def powQueueSize():
"""Returns the size of queues.workerQueue including current unfinished work""" """Returns the size of queues.workerQueue including current unfinished work"""
@ -80,7 +83,7 @@ def openKeysFile():
keysfile = os.path.join(state.appdata, 'keys.dat') keysfile = os.path.join(state.appdata, 'keys.dat')
if 'linux' in sys.platform: if 'linux' in sys.platform:
subprocess.call(["xdg-open", keysfile]) subprocess.call(["xdg-open", keysfile])
elif sys.platform.startswith('win'): elif is_windows:
os.startfile(keysfile) # pylint: disable=no-member os.startfile(keysfile) # pylint: disable=no-member
@ -868,7 +871,7 @@ class MyForm(settingsmixin.SMainWindow):
""" """
startonlogon = config.safeGetBoolean( startonlogon = config.safeGetBoolean(
'bitmessagesettings', 'startonlogon') 'bitmessagesettings', 'startonlogon')
if sys.platform.startswith('win'): # Auto-startup for Windows if is_windows: # Auto-startup for Windows
RUN_PATH = "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run" RUN_PATH = "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run"
settings = QtCore.QSettings( settings = QtCore.QSettings(
RUN_PATH, QtCore.QSettings.NativeFormat) RUN_PATH, QtCore.QSettings.NativeFormat)
@ -4233,6 +4236,14 @@ class BitmessageQtApplication(QtGui.QApplication):
# Unique identifier for this application # Unique identifier for this application
uuid = '6ec0149b-96e1-4be1-93ab-1465fb3ebf7c' uuid = '6ec0149b-96e1-4be1-93ab-1465fb3ebf7c'
@staticmethod
def get_windowstyle():
"""Get window style set in config or default"""
return config.safeGet(
'bitmessagesettings', 'windowstyle',
'Windows' if is_windows else 'GTK+'
)
def __init__(self, *argv): def __init__(self, *argv):
super(BitmessageQtApplication, self).__init__(*argv) super(BitmessageQtApplication, self).__init__(*argv)
id = BitmessageQtApplication.uuid id = BitmessageQtApplication.uuid
@ -4241,6 +4252,14 @@ class BitmessageQtApplication(QtGui.QApplication):
QtCore.QCoreApplication.setOrganizationDomain("bitmessage.org") QtCore.QCoreApplication.setOrganizationDomain("bitmessage.org")
QtCore.QCoreApplication.setApplicationName("pybitmessageqt") QtCore.QCoreApplication.setApplicationName("pybitmessageqt")
self.setStyle(self.get_windowstyle())
font = config.safeGet('bitmessagesettings', 'font')
if font:
# family, size, weight = font.split(',')
family, size = font.split(',')
self.setFont(QtGui.QFont(family, int(size)))
self.server = None self.server = None
self.is_running = False self.is_running = False

View File

@ -61,7 +61,8 @@ class Ui_MainWindow(object):
self.tabWidget.setMinimumSize(QtCore.QSize(0, 0)) self.tabWidget.setMinimumSize(QtCore.QSize(0, 0))
self.tabWidget.setBaseSize(QtCore.QSize(0, 0)) self.tabWidget.setBaseSize(QtCore.QSize(0, 0))
font = QtGui.QFont() font = QtGui.QFont()
font.setPointSize(9) base_size = QtGui.QApplication.instance().font().pointSize()
font.setPointSize(int(base_size * 0.75))
self.tabWidget.setFont(font) self.tabWidget.setFont(font)
self.tabWidget.setTabPosition(QtGui.QTabWidget.North) self.tabWidget.setTabPosition(QtGui.QTabWidget.North)
self.tabWidget.setTabShape(QtGui.QTabWidget.Rounded) self.tabWidget.setTabShape(QtGui.QTabWidget.Rounded)

View File

@ -41,14 +41,17 @@ def getSOCKSProxyType(config):
class SettingsDialog(QtGui.QDialog): class SettingsDialog(QtGui.QDialog):
"""The "Settings" dialog""" """The "Settings" dialog"""
# pylint: disable=too-many-instance-attributes
def __init__(self, parent=None, firstrun=False): def __init__(self, parent=None, firstrun=False):
super(SettingsDialog, self).__init__(parent) super(SettingsDialog, self).__init__(parent)
widgets.load('settings.ui', self) widgets.load('settings.ui', self)
self.app = QtGui.QApplication.instance()
self.parent = parent self.parent = parent
self.firstrun = firstrun self.firstrun = firstrun
self.config = config_obj self.config = config_obj
self.net_restart_needed = False self.net_restart_needed = False
self.font_setting = None
self.timer = QtCore.QTimer() self.timer = QtCore.QTimer()
if self.config.safeGetBoolean('bitmessagesettings', 'dontconnect'): if self.config.safeGetBoolean('bitmessagesettings', 'dontconnect'):
@ -85,6 +88,15 @@ class SettingsDialog(QtGui.QDialog):
def adjust_from_config(self, config): def adjust_from_config(self, config):
"""Adjust all widgets state according to config settings""" """Adjust all widgets state according to config settings"""
# pylint: disable=too-many-branches,too-many-statements # pylint: disable=too-many-branches,too-many-statements
current_style = self.app.get_windowstyle()
for i, sk in enumerate(QtGui.QStyleFactory.keys()):
self.comboBoxStyle.addItem(sk)
if sk == current_style:
self.comboBoxStyle.setCurrentIndex(i)
self.save_font_setting(self.app.font())
if not self.parent.tray.isSystemTrayAvailable(): if not self.parent.tray.isSystemTrayAvailable():
self.groupBoxTray.setEnabled(False) self.groupBoxTray.setEnabled(False)
self.groupBoxTray.setTitle(_translate( self.groupBoxTray.setTitle(_translate(
@ -137,7 +149,7 @@ class SettingsDialog(QtGui.QDialog):
"MainWindow", "MainWindow",
"Tray notifications not yet supported on your OS.")) "Tray notifications not yet supported on your OS."))
if 'win' not in sys.platform and not self.parent.desktop: if not sys.platform.startswith('win') and not self.parent.desktop:
self.checkBoxStartOnLogon.setDisabled(True) self.checkBoxStartOnLogon.setDisabled(True)
self.checkBoxStartOnLogon.setText(_translate( self.checkBoxStartOnLogon.setText(_translate(
"MainWindow", "Start-on-login not yet supported on your OS.")) "MainWindow", "Start-on-login not yet supported on your OS."))
@ -322,6 +334,18 @@ class SettingsDialog(QtGui.QDialog):
if status == 'success': if status == 'success':
self.parent.namecoin = nc self.parent.namecoin = nc
def save_font_setting(self, font):
"""Save user font setting and set the buttonFont text"""
font_setting = (font.family(), font.pointSize())
self.buttonFont.setText('{} {}'.format(*font_setting))
self.font_setting = '{},{}'.format(*font_setting)
def choose_font(self):
"""Show the font selection dialog"""
font, valid = QtGui.QFontDialog.getFont()
if valid:
self.save_font_setting(font)
def accept(self): def accept(self):
"""A callback for accepted event of buttonBox (OK button pressed)""" """A callback for accepted event of buttonBox (OK button pressed)"""
# pylint: disable=too-many-branches,too-many-statements # pylint: disable=too-many-branches,too-many-statements
@ -348,6 +372,20 @@ class SettingsDialog(QtGui.QDialog):
self.config.set('bitmessagesettings', 'replybelow', str( self.config.set('bitmessagesettings', 'replybelow', str(
self.checkBoxReplyBelow.isChecked())) self.checkBoxReplyBelow.isChecked()))
window_style = str(self.comboBoxStyle.currentText())
if self.app.get_windowstyle() != window_style or self.config.safeGet(
'bitmessagesettings', 'font'
) != self.font_setting:
self.config.set('bitmessagesettings', 'windowstyle', window_style)
self.config.set('bitmessagesettings', 'font', self.font_setting)
queues.UISignalQueue.put((
'updateStatusBar', (
_translate(
"MainWindow",
"You need to restart the application to apply"
" the window style or default font."), 1)
))
lang = str(self.languageComboBox.itemData( lang = str(self.languageComboBox.itemData(
self.languageComboBox.currentIndex()).toString()) self.languageComboBox.currentIndex()).toString())
self.config.set('bitmessagesettings', 'userlocale', lang) self.config.set('bitmessagesettings', 'userlocale', lang)

View File

@ -147,6 +147,32 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="9" column="0">
<widget class="QGroupBox" name="groupBoxStyle">
<property name="title">
<string>Custom Style</string>
</property>
<layout class="QHBoxLayout">
<item>
<widget class="QComboBox" name="comboBoxStyle">
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonFont">
<property name="text">
<string>Font</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="9" column="1"> <item row="9" column="1">
<widget class="QGroupBox" name="groupBox"> <widget class="QGroupBox" name="groupBox">
<property name="title"> <property name="title">
@ -1202,5 +1228,11 @@
</hint> </hint>
</hints> </hints>
</connection> </connection>
<connection>
<sender>buttonFont</sender>
<signal>clicked()</signal>
<receiver>settingsDialog</receiver>
<slot>choose_font</slot>
</connection>
</connections> </connections>
</ui> </ui>

View File

@ -1,9 +1,9 @@
"""bitmessageqt tests""" """bitmessageqt tests"""
from addressbook import TestAddressbook from .addressbook import TestAddressbook
from main import TestMain, TestUISignaler from .main import TestMain, TestUISignaler
from settings import TestSettings from .settings import TestSettings
from support import TestSupport from .support import TestSupport
__all__ = [ __all__ = [
"TestAddressbook", "TestMain", "TestSettings", "TestSupport", "TestAddressbook", "TestMain", "TestSettings", "TestSupport",

View File

@ -1,19 +1,23 @@
"""Common definitions for bitmessageqt tests""" """Common definitions for bitmessageqt tests"""
import Queue
import sys import sys
import unittest import unittest
from PyQt4 import QtCore, QtGui from PyQt4 import QtCore, QtGui
from six.moves import queue
import bitmessageqt import bitmessageqt
import queues from bitmessageqt import _translate, config, queues
from tr import _translate
class TestBase(unittest.TestCase): class TestBase(unittest.TestCase):
"""Base class for bitmessageqt test case""" """Base class for bitmessageqt test case"""
@classmethod
def setUpClass(cls):
"""Provide the UI test cases with common settings"""
cls.config = config
def setUp(self): def setUp(self):
self.app = ( self.app = (
QtGui.QApplication.instance() QtGui.QApplication.instance()
@ -24,14 +28,21 @@ class TestBase(unittest.TestCase):
self.window.appIndicatorInit(self.app) self.window.appIndicatorInit(self.app)
def tearDown(self): def tearDown(self):
"""Search for exceptions in closures called by timer and fail if any"""
# self.app.deleteLater() # self.app.deleteLater()
concerning = []
while True: while True:
try: try:
thread, exc = queues.excQueue.get(block=False) thread, exc = queues.excQueue.get(block=False)
except Queue.Empty: except queue.Empty:
return break
if thread == 'tests': if thread == 'tests':
self.fail('Exception in the main thread: %s' % exc) concerning.append(exc)
if concerning:
self.fail(
'Exceptions found in the main thread:\n%s' % '\n'.join((
str(e) for e in concerning
)))
class TestMain(unittest.TestCase): class TestMain(unittest.TestCase):

View File

@ -1,10 +1,14 @@
"""Tests for PyBitmessage settings"""
import threading import threading
import time import time
from main import TestBase from PyQt4 import QtCore, QtGui, QtTest
from bmconfigparser import config from bmconfigparser import config
from bitmessageqt import settings from bitmessageqt import settings
from .main import TestBase
class TestSettings(TestBase): class TestSettings(TestBase):
"""A test case for the "Settings" dialog""" """A test case for the "Settings" dialog"""
@ -14,8 +18,7 @@ class TestSettings(TestBase):
def test_udp(self): def test_udp(self):
"""Test the effect of checkBoxUDP""" """Test the effect of checkBoxUDP"""
udp_setting = config.safeGetBoolean( udp_setting = config.safeGetBoolean('bitmessagesettings', 'udp')
'bitmessagesettings', 'udp')
self.assertEqual(udp_setting, self.dialog.checkBoxUDP.isChecked()) self.assertEqual(udp_setting, self.dialog.checkBoxUDP.isChecked())
self.dialog.checkBoxUDP.setChecked(not udp_setting) self.dialog.checkBoxUDP.setChecked(not udp_setting)
self.dialog.accept() self.dialog.accept()
@ -32,3 +35,44 @@ class TestSettings(TestBase):
else: else:
if not udp_setting: if not udp_setting:
self.fail('No Announcer thread found while udp set to True') self.fail('No Announcer thread found while udp set to True')
def test_styling(self):
"""Test custom windows style and font"""
style_setting = config.safeGet('bitmessagesettings', 'windowstyle')
font_setting = config.safeGet('bitmessagesettings', 'font')
self.assertIs(style_setting, None)
self.assertIs(font_setting, None)
style_control = self.dialog.comboBoxStyle
self.assertEqual(
style_control.currentText(), self.app.get_windowstyle())
def call_font_dialog():
"""A function to get the open font dialog and accept it"""
font_dialog = QtGui.QApplication.activeModalWidget()
self.assertTrue(isinstance(font_dialog, QtGui.QFontDialog))
selected_font = font_dialog.currentFont()
self.assertEqual(
config.safeGet('bitmessagesettings', 'font'), '{},{}'.format(
selected_font.family(), selected_font.pointSize()))
font_dialog.accept()
self.dialog.accept()
self.assertEqual(
config.safeGet('bitmessagesettings', 'windowstyle'),
style_control.currentText())
def click_font_button():
"""Use QtTest to click the button"""
QtTest.QTest.mouseClick(
self.dialog.buttonFont, QtCore.Qt.LeftButton)
style_count = style_control.count()
self.assertGreater(style_count, 1)
for i in range(style_count):
if i != style_control.currentIndex():
style_control.setCurrentIndex(i)
break
QtCore.QTimer.singleShot(30, click_font_button)
QtCore.QTimer.singleShot(60, call_font_dialog)
time.sleep(2)