Refactor: Migrate to QTableView for user servers, and added search box

Signed-off-by: LorenEteval <loren.eteval@proton.me>
This commit is contained in:
LorenEteval
2026-06-21 15:56:17 +08:00
committed by Loren Eteval
parent 9bd24aec4c
commit bc9cd5026e
5 changed files with 120 additions and 97 deletions
+53 -37
View File
@@ -2,7 +2,7 @@ TRANSLATION = {
"Delete": {
"source": [
"Furious.Qt.QtWidgets",
"Furious.Widget.UserServersQTableWidget",
"Furious.Widget.UserServersQTableView",
"Furious.Widget.UserSubsQTableWidget",
"Furious.Widget.XrayAssetViewerQListWidget",
"Furious.Window.UserSubsWindow"
@@ -338,7 +338,7 @@ TRANSLATION = {
},
"Select All": {
"source": [
"Furious.Widget.UserServersQTableWidget",
"Furious.Widget.UserServersQTableView",
"Furious.Window.LogViewerWindow",
"Furious.Window.TextEditorWindow"
],
@@ -440,7 +440,7 @@ TRANSLATION = {
"Furious.Widget.GuiTrojan",
"Furious.Widget.GuiVLESS",
"Furious.Widget.GuiVMess",
"Furious.Widget.UserServersQTableWidget",
"Furious.Widget.UserServersQTableView",
"Furious.Widget.UserSubsQTableWidget"
],
"RU": "Примечание",
@@ -450,7 +450,7 @@ TRANSLATION = {
"Protocol": {
"source": [
"Furious.Widget.GuiHysteria1",
"Furious.Widget.UserServersQTableWidget"
"Furious.Widget.UserServersQTableView"
],
"RU": "Протокол",
"ZH": "协议",
@@ -462,7 +462,7 @@ TRANSLATION = {
"Furious.Widget.GuiTrojan",
"Furious.Widget.GuiVLESS",
"Furious.Widget.GuiVMess",
"Furious.Widget.UserServersQTableWidget"
"Furious.Widget.UserServersQTableView"
],
"RU": "Адрес",
"ZH": "地址",
@@ -474,7 +474,7 @@ TRANSLATION = {
"Furious.Widget.GuiTrojan",
"Furious.Widget.GuiVLESS",
"Furious.Widget.GuiVMess",
"Furious.Widget.UserServersQTableWidget"
"Furious.Widget.UserServersQTableView"
],
"RU": "Порт",
"ZH": "端口",
@@ -483,7 +483,7 @@ TRANSLATION = {
"Transport": {
"source": [
"Furious.Widget.GuiVTransport",
"Furious.Widget.UserServersQTableWidget"
"Furious.Widget.UserServersQTableView"
],
"RU": "Способ передачи",
"ZH": "传输方式",
@@ -491,7 +491,7 @@ TRANSLATION = {
},
"TLS": {
"source": [
"Furious.Widget.UserServersQTableWidget"
"Furious.Widget.UserServersQTableView"
],
"RU": "TLS",
"ZH": "TLS",
@@ -499,7 +499,7 @@ TRANSLATION = {
},
"Subscription": {
"source": [
"Furious.Widget.UserServersQTableWidget",
"Furious.Widget.UserServersQTableView",
"Furious.Window.AppMainWindow"
],
"RU": "Подписка",
@@ -508,7 +508,7 @@ TRANSLATION = {
},
"Latency": {
"source": [
"Furious.Widget.UserServersQTableWidget"
"Furious.Widget.UserServersQTableView"
],
"RU": "Задержка",
"ZH": "延迟",
@@ -517,7 +517,7 @@ TRANSLATION = {
"Speed": {
"source": [
"Furious.Widget.GuiHysteria1",
"Furious.Widget.UserServersQTableWidget"
"Furious.Widget.UserServersQTableView"
],
"RU": "Скорость",
"ZH": "速度",
@@ -533,7 +533,7 @@ TRANSLATION = {
},
"Move Up": {
"source": [
"Furious.Widget.UserServersQTableWidget"
"Furious.Widget.UserServersQTableView"
],
"RU": "Переместить вверх",
"ZH": "上移",
@@ -541,7 +541,7 @@ TRANSLATION = {
},
"Move Down": {
"source": [
"Furious.Widget.UserServersQTableWidget"
"Furious.Widget.UserServersQTableView"
],
"RU": "Переместить вниз",
"ZH": "下移",
@@ -549,7 +549,7 @@ TRANSLATION = {
},
"Duplicate": {
"source": [
"Furious.Widget.UserServersQTableWidget"
"Furious.Widget.UserServersQTableView"
],
"RU": "Дубликат",
"ZH": "克隆",
@@ -557,7 +557,7 @@ TRANSLATION = {
},
"Scroll To Activated Server": {
"source": [
"Furious.Widget.UserServersQTableWidget"
"Furious.Widget.UserServersQTableView"
],
"RU": "Перейдите к активированному серверу",
"ZH": "滚动到激活的服务器",
@@ -565,7 +565,7 @@ TRANSLATION = {
},
"Test Ping Latency": {
"source": [
"Furious.Widget.UserServersQTableWidget"
"Furious.Widget.UserServersQTableView"
],
"RU": "Измерить задержку (ping)",
"ZH": "测试Ping延迟",
@@ -573,7 +573,7 @@ TRANSLATION = {
},
"Test Download Speed": {
"source": [
"Furious.Widget.UserServersQTableWidget"
"Furious.Widget.UserServersQTableView"
],
"RU": "Измерить скорость загрузки",
"ZH": "测试下行速度",
@@ -581,7 +581,7 @@ TRANSLATION = {
},
"Test Download Speed (Multithreaded)": {
"source": [
"Furious.Widget.UserServersQTableWidget"
"Furious.Widget.UserServersQTableView"
],
"RU": "Измерить скорость загрузки (многопоточная)",
"ZH": "测试下行速度(多线程)",
@@ -589,7 +589,7 @@ TRANSLATION = {
},
"Clear Test Results": {
"source": [
"Furious.Widget.UserServersQTableWidget"
"Furious.Widget.UserServersQTableView"
],
"RU": "Очистить результаты тестирования",
"ZH": "清除测试结果",
@@ -597,7 +597,7 @@ TRANSLATION = {
},
"New Empty Configuration": {
"source": [
"Furious.Widget.UserServersQTableWidget",
"Furious.Widget.UserServersQTableView",
"Furious.Window.AppMainWindow"
],
"RU": "Создать новую пустую конфигурацию",
@@ -606,7 +606,7 @@ TRANSLATION = {
},
"Export Share Link To Clipboard": {
"source": [
"Furious.Widget.UserServersQTableWidget"
"Furious.Widget.UserServersQTableView"
],
"RU": "Копировать ссылку в буфер обмена",
"ZH": "导出分享链接至剪贴板",
@@ -614,7 +614,7 @@ TRANSLATION = {
},
"Export As QR Code": {
"source": [
"Furious.Widget.UserServersQTableWidget"
"Furious.Widget.UserServersQTableView"
],
"RU": "Экспорт в виде QR-кода",
"ZH": "导出为二维码",
@@ -622,7 +622,7 @@ TRANSLATION = {
},
"Export JSON Configuration To Clipboard": {
"source": [
"Furious.Widget.UserServersQTableWidget"
"Furious.Widget.UserServersQTableView"
],
"RU": "Копировать JSON конфигурацию в буфер обмена",
"ZH": "导出JSON配置至剪贴板",
@@ -631,7 +631,7 @@ TRANSLATION = {
"Connecting": {
"source": [
"Furious.TrayActions.Connect",
"Furious.Widget.UserServersQTableWidget"
"Furious.Widget.UserServersQTableView"
],
"RU": "Подключение",
"ZH": "正在连接",
@@ -639,7 +639,7 @@ TRANSLATION = {
},
"Connecting. Please wait...": {
"source": [
"Furious.Widget.UserServersQTableWidget"
"Furious.Widget.UserServersQTableView"
],
"RU": "Подключение. Пожалуйста, подождите...",
"ZH": "正在连接。请稍候",
@@ -647,7 +647,7 @@ TRANSLATION = {
},
"Untitled": {
"source": [
"Furious.Widget.UserServersQTableWidget"
"Furious.Widget.UserServersQTableView"
],
"RU": "Без названия",
"ZH": "未命名",
@@ -819,7 +819,7 @@ TRANSLATION = {
},
"Show Furious Log...": {
"source": [
"Furious.Widget.UserServersQTableWidget",
"Furious.Widget.UserServersQTableView",
"Furious.Window.AppMainWindow"
],
"RU": "Показать журнал Furious...",
@@ -828,7 +828,7 @@ TRANSLATION = {
},
"Show Core Log...": {
"source": [
"Furious.Widget.UserServersQTableWidget",
"Furious.Widget.UserServersQTableView",
"Furious.Window.AppMainWindow"
],
"RU": "Показать журнал ядра...",
@@ -837,7 +837,7 @@ TRANSLATION = {
},
"Show Tun2socks Log...": {
"source": [
"Furious.Widget.UserServersQTableWidget",
"Furious.Widget.UserServersQTableView",
"Furious.Window.AppMainWindow"
],
"RU": "Показать журнал Tun2socks...",
@@ -1247,7 +1247,7 @@ TRANSLATION = {
},
"Test Tcping Latency": {
"source": [
"Furious.Widget.UserServersQTableWidget"
"Furious.Widget.UserServersQTableView"
],
"RU": "Измерить задержку (Tсping)",
"ZH": "测试Tcping延迟",
@@ -1342,7 +1342,7 @@ TRANSLATION = {
},
"Customize JSON Configuration...": {
"source": [
"Furious.Widget.UserServersQTableWidget"
"Furious.Widget.UserServersQTableView"
],
"RU": "Редактировать JSON конфигурацию...",
"ZH": "自定义JSON配置...",
@@ -1443,7 +1443,7 @@ TRANSLATION = {
},
"Advanced...": {
"source": [
"Furious.Widget.UserServersQTableWidget"
"Furious.Widget.UserServersQTableView"
],
"RU": "Расширенные...",
"ZH": "高级...",
@@ -1459,7 +1459,7 @@ TRANSLATION = {
},
"Update subscription completed": {
"source": [
"Furious.Widget.UserServersQTableWidget"
"Furious.Widget.UserServersQTableView"
],
"RU": "Подписка на обновление завершена",
"ZH": "更新订阅完成",
@@ -1467,7 +1467,7 @@ TRANSLATION = {
},
"Update subscription failed": {
"source": [
"Furious.Widget.UserServersQTableWidget"
"Furious.Widget.UserServersQTableView"
],
"RU": "Обновить подписку не удалось",
"ZH": "更新订阅失败",
@@ -1475,7 +1475,7 @@ TRANSLATION = {
},
"Configuration has been updated": {
"source": [
"Furious.Widget.UserServersQTableWidget"
"Furious.Widget.UserServersQTableView"
],
"RU": "Конфигурация обновлена",
"ZH": "配置已更新",
@@ -1483,7 +1483,7 @@ TRANSLATION = {
},
"Configuration update failed": {
"source": [
"Furious.Widget.UserServersQTableWidget"
"Furious.Widget.UserServersQTableView"
],
"RU": "Обновление конфигурации не удалось",
"ZH": "配置更新失败",
@@ -1845,10 +1845,26 @@ TRANSLATION = {
},
"Activate Selected Server": {
"source": [
"Furious.Widget.UserServersQTableWidget"
"Furious.Widget.UserServersQTableView"
],
"RU": "Активировать выбранный сервер",
"ZH": "激活选定的服务器",
"isReviewed": "True"
},
"Search servers, e.g. vmess|trojan": {
"source": [
"Furious.Window.AppMainWindow"
],
"RU": "Поиск серверов, например: vmess|trojan",
"ZH": "查找服务器,例如 vmess|trojan",
"isReviewed": "True"
},
"Search": {
"source": [
"Furious.Window.AppMainWindow"
],
"RU": "Поиск",
"ZH": "查找",
"isReviewed": "True"
}
}
+33
View File
@@ -38,12 +38,14 @@ __all__ = [
'AppQGroupBox',
'AppQHeaderView',
'AppQLabel',
'AppQLineEdit',
'AppQListWidget',
'AppQMainWindow',
'AppQMenu',
'AppQMenuBar',
'AppQMessageBox',
'AppQPushButton',
'AppQTableView',
'AppQTableWidget',
'AppQTabWidget',
'AppQToolBar',
@@ -272,6 +274,14 @@ class AppQLabel(Mixins.QTranslatable, QLabel):
self.setText(_(self.text()))
class AppQLineEdit(Mixins.QTranslatable, QLineEdit):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def retranslate(self):
self.setPlaceholderText(_(self.placeholderText()))
class AppQListWidget(Mixins.ConnectionAware, QListWidget):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@@ -486,6 +496,29 @@ class AppQPushButton(Mixins.QTranslatable, QPushButton):
self.setText(_(self.text()))
class AppQTableView(Mixins.ConnectionAware, QTableView):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setWordWrap(False)
self.setAlternatingRowColors(True)
@staticmethod
def getStyleSheet(color):
return f'QTableView {{ selection-background-color: {color}; }}'
def setSelectionColor(self, color):
# self.setStyleSheet(self.getStyleSheet(color))
pass
def disconnectedCallback(self):
self.setSelectionColor(AppHue.disconnectedColor())
def connectedCallback(self):
self.setSelectionColor(AppHue.connectedColor())
class AppQTableWidget(Mixins.ConnectionAware, QTableWidget):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@@ -40,11 +40,11 @@ from PySide6.QtNetwork import *
from typing import Callable, Union, MutableSequence
import re
import queue
import logging
import icmplib
import functools
import re
__all__ = ['UserServersQTableWidget']
@@ -701,43 +701,17 @@ class TestDownloadSpeedWorker(WebGETManager):
self.sync()
class UserServersQTableWidgetHorizontalHeader(AppQHeaderView):
class SortOrder:
Ascending_ = QtCore.Qt.SortOrder.AscendingOrder
Descending = QtCore.Qt.SortOrder.DescendingOrder
class UserServersQTableViewHorizontalHeader(AppQHeaderView):
def __init__(self, *args, **kwargs):
super().__init__(QtCore.Qt.Orientation.Horizontal, *args, **kwargs)
self.sortOrderTable = list(
self.SortOrder.Ascending_ for i in range(self.columnCount)
)
self.sectionClicked.connect(self.handleSectionClicked)
@QtCore.Slot(int)
def handleSectionClicked(self, clickedIndex: int):
parent = self.parent()
if isinstance(parent, QTableView):
order = self.sortOrderTable[clickedIndex]
parent.sortByColumn(clickedIndex, order)
self.setSortIndicator(clickedIndex, order)
if order == self.SortOrder.Ascending_:
self.sortOrderTable[clickedIndex] = self.SortOrder.Descending
else:
self.sortOrderTable[clickedIndex] = self.SortOrder.Ascending_
class UserServersQTableWidgetVerticalHeader(AppQHeaderView):
class UserServersQTableViewVerticalHeader(AppQHeaderView):
def __init__(self, *args, **kwargs):
super().__init__(QtCore.Qt.Orientation.Vertical, *args, **kwargs)
class UserServersQTableWidgetHeaders:
class UserServersQTableViewHeaders:
def __init__(self, name: str, func: Callable[[ConfigFactory], str] = None):
self.name = name
self.func = func
@@ -758,7 +732,7 @@ class UserServersQTableWidgetHeaders:
class UserServersTableModel(QtCore.QAbstractTableModel):
SortRole = QtCore.Qt.ItemDataRole.UserRole + 1
def __init__(self, headers: list[UserServersQTableWidgetHeaders], parent=None):
def __init__(self, headers: list[UserServersQTableViewHeaders], parent=None):
super().__init__(parent)
self.headers = headers
@@ -816,7 +790,7 @@ class UserServersTableModel(QtCore.QAbstractTableModel):
if row == Storage.UserActivatedItemIndex():
return QColor(AppHue.currentColor())
return QBrush()
return None
if role == QtCore.Qt.ItemDataRole.TextAlignmentRole:
if str(header) == 'Latency' or str(header) == 'Speed':
@@ -975,25 +949,22 @@ _TRANSLATABLE_HEADERS = [
]
class UserServersQTableWidget(Mixins.QTranslatable, Mixins.ConnectionAware, QTableView):
class UserServersQTableWidget(Mixins.QTranslatable, AppQTableView):
Headers = [
UserServersQTableWidgetHeaders('Remark'),
UserServersQTableWidgetHeaders('Protocol'),
UserServersQTableWidgetHeaders('Address'),
UserServersQTableWidgetHeaders('Port'),
UserServersQTableWidgetHeaders('Transport'),
UserServersQTableWidgetHeaders('TLS'),
UserServersQTableWidgetHeaders('Subscription'),
UserServersQTableWidgetHeaders('Latency'),
UserServersQTableWidgetHeaders('Speed'),
UserServersQTableViewHeaders('Remark'),
UserServersQTableViewHeaders('Protocol'),
UserServersQTableViewHeaders('Address'),
UserServersQTableViewHeaders('Port'),
UserServersQTableViewHeaders('Transport'),
UserServersQTableViewHeaders('TLS'),
UserServersQTableViewHeaders('Subscription'),
UserServersQTableViewHeaders('Latency'),
UserServersQTableViewHeaders('Speed'),
]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setWordWrap(False)
self.setAlternatingRowColors(True)
self.sourceModel = UserServersTableModel(self.Headers, parent=self)
self.proxyModel = UserServersSortFilterProxyModel(parent=self)
self.proxyModel.setSourceModel(self.sourceModel)
@@ -1019,18 +990,19 @@ class UserServersQTableWidget(Mixins.QTranslatable, Mixins.ConnectionAware, QTab
# Install custom header
self.setHorizontalHeader(
UserServersQTableWidgetHorizontalHeader(
UserServersQTableViewHorizontalHeader(
parent=self,
legacySectionSizeSettingsName='ServerWidgetSectionSizeTable',
sectionSizeSettingsName='UserServersHeaderViewState',
)
)
self.setVerticalHeader(UserServersQTableWidgetVerticalHeader(self))
self.setVerticalHeader(UserServersQTableViewVerticalHeader(self))
self.horizontalHeader().setCustomSectionResizeMode()
self.horizontalHeader().restoreSectionSize()
self.setSortingEnabled(False)
self.setSortingEnabled(True)
self.proxyModel.sort(-1)
# Selection
self.setSelectionColor(AppHue.disconnectedColor())
@@ -1257,13 +1229,6 @@ class UserServersQTableWidget(Mixins.QTranslatable, Mixins.ConnectionAware, QTab
self.setCurrentIndex(self.activatedIndex())
self.activateItemByIndex(Storage.UserActivatedItemIndex(), True)
@staticmethod
def getStyleSheet(color):
return f'QTableView {{ selection-background-color: {color}; }}'
def setSelectionColor(self, color):
self.setStyleSheet(self.getStyleSheet(color))
@property
def selectedIndex(self):
indexes = list(
@@ -1320,12 +1285,12 @@ class UserServersQTableWidget(Mixins.QTranslatable, Mixins.ConnectionAware, QTab
)
def disconnectedCallback(self):
self.setSelectionColor(AppHue.disconnectedColor())
super().disconnectedCallback()
self.activateItemByIndex(Storage.UserActivatedItemIndex(), True)
def connectedCallback(self):
self.setSelectionColor(AppHue.connectedColor())
super().connectedCallback()
self.activateItemByIndex(Storage.UserActivatedItemIndex(), True)
+1 -1
View File
@@ -28,6 +28,6 @@ from .GuiVLESS import *
from .GuiVMess import *
from .IndentSpinBox import *
from .SystemTrayIcon import *
from .UserServersQTableWidget import *
from .UserServersQTableView import *
from .UserSubsQTableWidget import *
from .XrayAssetViewerQListWidget import *
+11 -2
View File
@@ -22,7 +22,7 @@ from Furious.Interface import *
from Furious.Library import *
from Furious.Qt import *
from Furious.Qt import gettext as _
from Furious.Widget.UserServersQTableWidget import *
from Furious.Widget.UserServersQTableView import *
from Furious.Widget.GuiCustomizeNetworkTest import *
from Furious.Widget.GuiCustomizeProxyBypass import *
from Furious.Widget.GuiTUNSettings import *
@@ -441,7 +441,8 @@ class AppMainWindow(AppQMainWindow):
self._widget = QWidget()
self._layout = QVBoxLayout(self._widget)
self.searchLayout = QHBoxLayout()
self.searchLineEdit = QLineEdit()
self.searchLineEdit = AppQLineEdit()
self.searchLineEdit.setPlaceholderText(_('Search servers, e.g. vmess|trojan'))
self.searchButton = AppQPushButton(_('Search'))
self.searchLayout.addWidget(self.searchLineEdit)
self.searchLayout.addWidget(self.searchButton)
@@ -454,9 +455,17 @@ class AppMainWindow(AppQMainWindow):
self.searchLineEdit.returnPressed.connect(
lambda: self.userServersQTableWidget.search(self.searchLineEdit.text())
)
self.searchLineEdit.textChanged.connect(
self.handleUserServersSearchTextChanged
)
self.setCentralWidget(self._widget)
@QtCore.Slot(str)
def handleUserServersSearchTextChanged(self, text: str):
if not text:
self.userServersQTableWidget.clearSearch()
def updateSubsByUnique(self, unique: str, httpProxy: Union[str, None], **kwargs):
self.userServersQTableWidget.updateSubsByUnique(unique, httpProxy, **kwargs)