mirror of
https://github.com/aclist/dztui.git
synced 2026-10-30 22:52:42 +01:00
chore: rename to ProxyModelManager
This commit is contained in:
1 parent
fd9a23c6d6
commit
0283baf3a9
7 files changed
+82
-66
No files matched your search
+11
-11
@@ -41,7 +41,7 @@ from dzgui.config.userprefs import UserPrefs
|
|||||||
from dzgui.controllers.emitter import Emitter
|
from dzgui.controllers.emitter import Emitter
|
||||||
from dzgui.managers.config import ConfigManager
|
from dzgui.managers.config import ConfigManager
|
||||||
from dzgui.managers.contextmenu import ContextMenuManager
|
from dzgui.managers.contextmenu import ContextMenuManager
|
||||||
from dzgui.model.filtered_model import FilteredModelManager
|
from dzgui.model.proxy_model import ProxyModelManager
|
||||||
from dzgui.model.servers import ServerModelManager
|
from dzgui.model.servers import ServerModelManager
|
||||||
from dzgui.model.model_factory import ModelFactory
|
from dzgui.model.model_factory import ModelFactory
|
||||||
from dzgui.util import strings
|
from dzgui.util import strings
|
||||||
@@ -585,20 +585,20 @@ class Controller(GObject.GObject):
|
|||||||
def get_player_count(self) -> str:
|
def get_player_count(self) -> str:
|
||||||
treeview = self.get_active_treeview()
|
treeview = self.get_active_treeview()
|
||||||
model = treeview.get_model()
|
model = treeview.get_model()
|
||||||
filter_man = treeview.get_filter_man()
|
proxy_man = treeview.get_proxy_man()
|
||||||
control_model = filter_man.get_control()
|
control_model = proxy_man.get_control()
|
||||||
count = format_player_count(model, control_model)
|
count = format_player_count(model, control_model)
|
||||||
return count
|
return count
|
||||||
|
|
||||||
def get_statusbar(self) -> None:
|
def get_statusbar(self) -> None:
|
||||||
return self.mediator.statusbar
|
return self.mediator.statusbar
|
||||||
|
|
||||||
def get_filter_man(self) -> "FilteredModelManager":
|
def get_proxy_man(self) -> "ProxyModelManager":
|
||||||
return self.filter_man
|
return self.proxy_man
|
||||||
|
|
||||||
def set_filter_man(self, filter_man: "FilteredModelManager") -> None:
|
# def set_proxy_man(self, proxy_man: "ProxyModelManager") -> None:
|
||||||
# TODO: used when staging filter man outside of thread
|
# # TODO: used when staging filter man outside of thread
|
||||||
self.filter_man = filter_man
|
# self.proxy_man = proxy_man
|
||||||
|
|
||||||
def populate_model(self, tv: Gtk.TreeView) -> None:
|
def populate_model(self, tv: Gtk.TreeView) -> None:
|
||||||
# NOTE: skip on previously loaded tabs
|
# NOTE: skip on previously loaded tabs
|
||||||
@@ -624,8 +624,8 @@ class Controller(GObject.GObject):
|
|||||||
# TODO: filterman calls back to here, gets convoluted
|
# TODO: filterman calls back to here, gets convoluted
|
||||||
def get_keyword(self) -> str:
|
def get_keyword(self) -> str:
|
||||||
tv = self.get_active_treeview()
|
tv = self.get_active_treeview()
|
||||||
filter_man = tv.get_filter_man()
|
proxy_man = tv.get_proxy_man()
|
||||||
return filter_man.get_keyword_filter()
|
return proxy_man.get_keyword_filter()
|
||||||
|
|
||||||
def get_map_store(self) -> Gtk.ListStore:
|
def get_map_store(self) -> Gtk.ListStore:
|
||||||
map_man = self.get_map_man()
|
map_man = self.get_map_man()
|
||||||
@@ -677,7 +677,7 @@ class Controller(GObject.GObject):
|
|||||||
map_man = self.get_map_man()
|
map_man = self.get_map_man()
|
||||||
map_man.set_selected_map(selection)
|
map_man.set_selected_map(selection)
|
||||||
# FIXME: label should not be mandatory
|
# FIXME: label should not be mandatory
|
||||||
ServerModelManager(self, self.get_active_treeview()).refilter(FilterMode.MAP)
|
ServerModelManager(self, self.get_active_treeview()).refilter(FilterMode.MAP, selection)
|
||||||
|
|
||||||
def get_notebook(self) -> "Notebook":
|
def get_notebook(self) -> "Notebook":
|
||||||
return self.mediator.notebook
|
return self.mediator.notebook
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import inspect
|
import inspect
|
||||||
import logging
|
import logging
|
||||||
import threading
|
import threading
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from typing import Callable, Optional
|
from typing import Callable, Optional
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
import gi
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
from dzgui.model.model_factory import ModelFactory
|
||||||
from dzgui.util import strings
|
from dzgui.util import strings
|
||||||
from dzgui.util.strings import all_maps
|
from dzgui.util.strings import all_maps
|
||||||
|
|
||||||
gi.require_version("Gtk", "3.0")
|
if TYPE_CHECKING:
|
||||||
from gi.repository.Gtk import ListStore # noqa E402
|
from dzgui.model.model_factory import FastInsertListStore
|
||||||
from gi.repository import Gtk # noqa E402
|
|
||||||
|
|
||||||
|
|
||||||
# TODO: rename class to MetaManager
|
# TODO: rename class to MetaManager
|
||||||
class MapManager:
|
class MapManager:
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
|
|
||||||
self.map_store = ListStore(str)
|
self.map_store = ModelFactory().make_map_store()
|
||||||
self.prior_map: str
|
self.prior_map: str
|
||||||
self.selected_map = all_maps
|
self.selected_map = all_maps
|
||||||
|
|
||||||
@@ -54,11 +54,11 @@ class MapManager:
|
|||||||
def set_filter(self, label: str, state: bool) -> None:
|
def set_filter(self, label: str, state: bool) -> None:
|
||||||
self.enabled_filters[label] = state
|
self.enabled_filters[label] = state
|
||||||
|
|
||||||
def get_map_store(self) -> ListStore:
|
def get_map_store(self) -> "FastInsertListStore":
|
||||||
return self.map_store
|
return self.map_store
|
||||||
|
|
||||||
def reinit_map_store(self) -> None:
|
def reinit_map_store(self) -> None:
|
||||||
model = ListStore(str)
|
model = ModelFactory().make_map_store()
|
||||||
model.append([all_maps])
|
model.append([all_maps])
|
||||||
self.map_store = model
|
self.map_store = model
|
||||||
self.selected_map = all_maps
|
self.selected_map = all_maps
|
||||||
@@ -88,3 +88,7 @@ class MapManager:
|
|||||||
self.reinit_map_store()
|
self.reinit_map_store()
|
||||||
for m in maps:
|
for m in maps:
|
||||||
self.append_map([m])
|
self.append_map([m])
|
||||||
|
|
||||||
|
# when switching views, just grab the map store, active keyword, active map, and selected checks
|
||||||
|
# for that view and apply them to filter panel outside of thread
|
||||||
|
# initialize proxyman with access to filterman
|
||||||
@@ -17,13 +17,13 @@ if TYPE_CHECKING:
|
|||||||
from dzgui.controllers.emitter import Emitter
|
from dzgui.controllers.emitter import Emitter
|
||||||
|
|
||||||
|
|
||||||
class FilteredModelManager:
|
class ProxyModelManager:
|
||||||
"""
|
"""
|
||||||
Manages access to cached ListStore resources and
|
Manages access to cached ListStore resources and
|
||||||
performs filtering on behalf of atomic TreeViews,
|
performs filtering on behalf of atomic TreeViews,
|
||||||
which share the same column structure.
|
which share the same column structure.
|
||||||
|
|
||||||
A FilteredModelManager is attached to each ServerTreeView.
|
A ProxyModelManager is attached to each ServerTreeView.
|
||||||
|
|
||||||
Raw data is cached before being packed into a ListStore, see get_control()
|
Raw data is cached before being packed into a ListStore, see get_control()
|
||||||
Filtration creates a proxy of the TreeView's model, see get_proxy_model()
|
Filtration creates a proxy of the TreeView's model, see get_proxy_model()
|
||||||
@@ -282,3 +282,9 @@ class FilteredModelManager:
|
|||||||
self.ping_cache = {}
|
self.ping_cache = {}
|
||||||
# if full:
|
# if full:
|
||||||
# self.control_model = None
|
# self.control_model = None
|
||||||
|
|
||||||
|
def set_active_map(self, _map: str) -> None:
|
||||||
|
self.active_map = _map
|
||||||
|
|
||||||
|
def get_active_map(self) -> str:
|
||||||
|
return self.active_map
|
||||||
+12
-12
@@ -24,7 +24,7 @@ API_TIMEOUT = 3
|
|||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from dzgui.controllers.mc import Controller
|
from dzgui.controllers.mc import Controller
|
||||||
from dzgui.model.filtered_model import FilteredModelManager
|
from dzgui.model.proxy_model import ProxyModelManager
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -46,7 +46,7 @@ class ServerModelManager:
|
|||||||
self.jobs = 1
|
self.jobs = 1
|
||||||
|
|
||||||
# NOTE: store filter man for access inside thread
|
# NOTE: store filter man for access inside thread
|
||||||
self.filter_man = tv.get_filter_man()
|
self.proxy_man = tv.get_proxy_man()
|
||||||
|
|
||||||
# FIXME: change WaitDialog to use parent window only
|
# FIXME: change WaitDialog to use parent window only
|
||||||
self.thread_man = ThreadingManager(parent=controller)
|
self.thread_man = ThreadingManager(parent=controller)
|
||||||
@@ -195,8 +195,8 @@ class ServerModelManager:
|
|||||||
|
|
||||||
record = Servers.parse_json([res])
|
record = Servers.parse_json([res])
|
||||||
|
|
||||||
filter_man = self._get_filter_man()
|
proxy_man = self._get_proxy_man()
|
||||||
raw_model = filter_man.get_control()
|
raw_model = proxy_man.get_control()
|
||||||
|
|
||||||
fqip = Servers.response_to_fq_ip(res)
|
fqip = Servers.response_to_fq_ip(res)
|
||||||
config_man = self.controller.get_config_man()
|
config_man = self.controller.get_config_man()
|
||||||
@@ -209,7 +209,7 @@ class ServerModelManager:
|
|||||||
raw_model.append(record[0])
|
raw_model.append(record[0])
|
||||||
# TODO: if all filters are already applied, strange behavior may occur
|
# TODO: if all filters are already applied, strange behavior may occur
|
||||||
# -> need to insert and update per current filters
|
# -> need to insert and update per current filters
|
||||||
filter_man.filter(FilterMode.INITIAL)
|
proxy_man.filter(FilterMode.INITIAL)
|
||||||
|
|
||||||
self.thread_man.set_cleanup_func(StoredFunc(self._cleanup_single_ip))
|
self.thread_man.set_cleanup_func(StoredFunc(self._cleanup_single_ip))
|
||||||
|
|
||||||
@@ -249,7 +249,7 @@ class ServerModelManager:
|
|||||||
self._dump_ips(ips)
|
self._dump_ips(ips)
|
||||||
|
|
||||||
def _cleanup_single_ip(self) -> None:
|
def _cleanup_single_ip(self) -> None:
|
||||||
proxy = self._get_filter_man().get_proxy_model()
|
proxy = self._get_proxy_man().get_proxy_model()
|
||||||
self.tv.set_model(proxy)
|
self.tv.set_model(proxy)
|
||||||
|
|
||||||
# TODO: if current tab != self.saved, add label
|
# TODO: if current tab != self.saved, add label
|
||||||
@@ -307,7 +307,7 @@ class ServerModelManager:
|
|||||||
# FIXME: filterman calls GTK methods in thread
|
# FIXME: filterman calls GTK methods in thread
|
||||||
# if mode == FilterMode.INITIAL:
|
# if mode == FilterMode.INITIAL:
|
||||||
|
|
||||||
manager = self._get_filter_man()
|
manager = self._get_proxy_man()
|
||||||
manager.wipe_cache()
|
manager.wipe_cache()
|
||||||
manager.set_control(data)
|
manager.set_control(data)
|
||||||
manager.filter(FilterMode.INITIAL)
|
manager.filter(FilterMode.INITIAL)
|
||||||
@@ -324,13 +324,13 @@ class ServerModelManager:
|
|||||||
def _get_new_maps(self) -> list[str]:
|
def _get_new_maps(self) -> list[str]:
|
||||||
return self.new_maps
|
return self.new_maps
|
||||||
|
|
||||||
def _get_filter_man(self) -> "FilteredModelManager":
|
def _get_proxy_man(self) -> "ProxyModelManager":
|
||||||
return self.filter_man
|
return self.proxy_man
|
||||||
|
|
||||||
@call_on_thread(dialog.filtering)
|
@call_on_thread(dialog.filtering)
|
||||||
def refilter(self, mode: FilterMode, label: str) -> None:
|
def refilter(self, mode: FilterMode, label: str) -> None:
|
||||||
self.first_iteration = False
|
self.first_iteration = False
|
||||||
filter_man = self._get_filter_man()
|
proxy_man = self._get_proxy_man()
|
||||||
filter_man.filter(mode, label)
|
proxy_man.filter(mode, label)
|
||||||
self.to_insert = filter_man.get_proxy_model()
|
self.to_insert = proxy_man.get_proxy_model()
|
||||||
self.thread_man.set_cleanup_func(StoredFunc(self._cleanup_on_success))
|
self.thread_man.set_cleanup_func(StoredFunc(self._cleanup_on_success))
|
||||||
@@ -4,7 +4,6 @@ from typing import Literal, TYPE_CHECKING
|
|||||||
from dzgui.const.enum import FilterMode
|
from dzgui.const.enum import FilterMode
|
||||||
from dzgui.const.constants import EXPAND, NO_EXPAND, NO_FILL, NO_PADDING, SEARCH_ICON
|
from dzgui.const.constants import EXPAND, NO_EXPAND, NO_FILL, NO_PADDING, SEARCH_ICON
|
||||||
from dzgui.model.servers import ServerModelManager
|
from dzgui.model.servers import ServerModelManager
|
||||||
from dzgui.util.margins import set_surrounding_margins
|
|
||||||
from dzgui.views.components.labels import BoldLabel
|
from dzgui.views.components.labels import BoldLabel
|
||||||
|
|
||||||
import gi
|
import gi
|
||||||
@@ -128,12 +127,24 @@ class KeywordEntry(Gtk.Entry):
|
|||||||
|
|
||||||
class FilterPanel(Gtk.Box):
|
class FilterPanel(Gtk.Box):
|
||||||
def __init__(self, controller: "Controller") -> None:
|
def __init__(self, controller: "Controller") -> None:
|
||||||
super().__init__(spacing=6, vexpand=False, orientation=Gtk.Orientation.VERTICAL)
|
super().__init__(
|
||||||
|
spacing=6,
|
||||||
|
vexpand=False,
|
||||||
|
orientation=Gtk.Orientation.VERTICAL,
|
||||||
|
margin_top=1,
|
||||||
|
margin_bottom=10,
|
||||||
|
margin_left=10,
|
||||||
|
margin_right=10,
|
||||||
|
)
|
||||||
|
|
||||||
self.controller = controller
|
self.controller = controller
|
||||||
self.controller.register_widget("filters", self)
|
self.controller.register_widget("filters", self)
|
||||||
self.emitter = self.controller.get_emitter()
|
self.emitter = self.controller.get_emitter()
|
||||||
|
|
||||||
|
# TODO: strings
|
||||||
|
self.sel_map = "All maps"
|
||||||
|
|
||||||
|
# TODO: each treeview for that tab context has its own filter manager, independent of this
|
||||||
# self.selected_map: str = strings.all_maps
|
# self.selected_map: str = strings.all_maps
|
||||||
# self.prior_map: str = strings.all_maps
|
# self.prior_map: str = strings.all_maps
|
||||||
# self.set_orientation(Gtk.Orientation.VERTICAL)
|
# self.set_orientation(Gtk.Orientation.VERTICAL)
|
||||||
@@ -147,11 +158,6 @@ class FilterPanel(Gtk.Box):
|
|||||||
self.keyword_entry = KeywordEntry(self.controller)
|
self.keyword_entry = KeywordEntry(self.controller)
|
||||||
self.button_grid = ButtonGrid(self.controller, defaults)
|
self.button_grid = ButtonGrid(self.controller, defaults)
|
||||||
|
|
||||||
# TODO: unintended legacy behavior?
|
|
||||||
self.connect("button-release-event", lambda *args: True)
|
|
||||||
set_surrounding_margins(self, 10)
|
|
||||||
self.set_margin_top(1)
|
|
||||||
|
|
||||||
# TODO: strings
|
# TODO: strings
|
||||||
self.filters_label = BoldLabel("Filters")
|
self.filters_label = BoldLabel("Filters")
|
||||||
|
|
||||||
@@ -201,11 +207,13 @@ class FilterPanel(Gtk.Box):
|
|||||||
model = self.maps_combo.get_model()
|
model = self.maps_combo.get_model()
|
||||||
if text is None:
|
if text is None:
|
||||||
return
|
return
|
||||||
|
if text == self.sel_map:
|
||||||
|
return
|
||||||
for i, row in enumerate(model):
|
for i, row in enumerate(model):
|
||||||
if text == row[0]:
|
if text == row[0]:
|
||||||
self.maps_combo.set_active(i)
|
self.maps_combo.set_active(i)
|
||||||
self._on_map_changed(self.maps_combo)
|
# self.controller.set_active_map(i)
|
||||||
self.controller.set_active_map(i)
|
# print("HERE3")
|
||||||
|
|
||||||
def _on_maps_loaded(self, emitter: "Emitter", store: Gtk.ListStore) -> None:
|
def _on_maps_loaded(self, emitter: "Emitter", store: Gtk.ListStore) -> None:
|
||||||
self.maps_combo.set_model(store)
|
self.maps_combo.set_model(store)
|
||||||
@@ -272,8 +280,8 @@ class FilterPanel(Gtk.Box):
|
|||||||
store = self.controller.get_map_store()
|
store = self.controller.get_map_store()
|
||||||
if len(text) >= completion.get_minimum_key_length():
|
if len(text) >= completion.get_minimum_key_length():
|
||||||
completion.set_model(store)
|
completion.set_model(store)
|
||||||
ind = self.get_active_combo()
|
# ind = self.get_active_combo()
|
||||||
self.controller.set_active_map(ind)
|
# self.controller.set_active_map(ind)
|
||||||
|
|
||||||
def restore_focus_to_treeview(self) -> Literal[False]:
|
def restore_focus_to_treeview(self) -> Literal[False]:
|
||||||
view = self.controller.get_active_treeview()
|
view = self.controller.get_active_treeview()
|
||||||
@@ -282,17 +290,17 @@ class FilterPanel(Gtk.Box):
|
|||||||
|
|
||||||
def _on_combo_keypress(self, combo: Gtk.ComboBox, event: Gdk.EventKey) -> bool:
|
def _on_combo_keypress(self, combo: Gtk.ComboBox, event: Gdk.EventKey) -> bool:
|
||||||
match event.keyval:
|
match event.keyval:
|
||||||
case Gdk.KEY_Down:
|
case Gdk.KEY_Down | Gdk.KEY_Up:
|
||||||
self.maps_combo.popup()
|
self.maps_combo.popup()
|
||||||
return True
|
return True
|
||||||
case _:
|
case _:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def get_active_combo(self) -> int:
|
# def get_active_combo(self) -> int:
|
||||||
return self.maps_combo.get_active()
|
# return self.maps_combo.get_active()
|
||||||
|
#
|
||||||
def set_active_combo(self, row: int) -> None:
|
# def set_active_combo(self, row: int) -> None:
|
||||||
self.maps_combo.set_active(row)
|
# self.maps_combo.set_active(row)
|
||||||
|
|
||||||
def toggle_check_by_key(self, emitter: "Emitter", keyval: int) -> bool:
|
def toggle_check_by_key(self, emitter: "Emitter", keyval: int) -> bool:
|
||||||
mappings = {
|
mappings = {
|
||||||
@@ -322,16 +330,13 @@ class FilterPanel(Gtk.Box):
|
|||||||
check.set_active(not state)
|
check.set_active(not state)
|
||||||
|
|
||||||
def _on_map_changed(self, combo: Gtk.ComboBox) -> None:
|
def _on_map_changed(self, combo: Gtk.ComboBox) -> None:
|
||||||
old_sel = self.controller.get_selected_map()
|
ind = combo.get_active()
|
||||||
model = combo.get_model()
|
if ind < 0:
|
||||||
tree_iter = combo.get_active_iter()
|
|
||||||
if tree_iter is None:
|
|
||||||
return
|
return
|
||||||
selection = model[tree_iter][0]
|
selection = self.maps_entry.get_text()
|
||||||
if selection == old_sel:
|
self.sel_map = selection
|
||||||
return
|
|
||||||
if not selection:
|
|
||||||
return
|
|
||||||
self.maps_entry.set_text(selection)
|
|
||||||
logger.info(f"User selected map '{selection}'")
|
|
||||||
self.emitter.emit("map_selection_changed", selection)
|
self.emitter.emit("map_selection_changed", selection)
|
||||||
|
|
||||||
|
tv = self.controller.get_active_treeview()
|
||||||
|
proxy_man = tv.get_proxy_man()
|
||||||
|
proxy_man.set_active_map(selection)
|
||||||
@@ -9,7 +9,7 @@ from dzgui.views.mixins.context_mixin import ContextMixin
|
|||||||
from dzgui.const.enum import ContextMenu, ContextMenuGroup, ServerTab
|
from dzgui.const.enum import ContextMenu, ContextMenuGroup, ServerTab
|
||||||
from dzgui.api.servers import Record
|
from dzgui.api.servers import Record
|
||||||
from dzgui.model.map_model import MapManager
|
from dzgui.model.map_model import MapManager
|
||||||
from dzgui.model.filtered_model import FilteredModelManager
|
from dzgui.model.proxy_model import ProxyModelManager
|
||||||
from dzgui.util.dist import CalcDist
|
from dzgui.util.dist import CalcDist
|
||||||
from dzgui.util import strings
|
from dzgui.util import strings
|
||||||
from typing import Literal, TYPE_CHECKING
|
from typing import Literal, TYPE_CHECKING
|
||||||
@@ -45,8 +45,8 @@ class ServerTreeView(ContextMixin, TreeView):
|
|||||||
|
|
||||||
self.loaded = False
|
self.loaded = False
|
||||||
|
|
||||||
self.filter_man = FilteredModelManager(controller)
|
self.proxy_man = ProxyModelManager(controller)
|
||||||
model = self.filter_man.get_proxy_model()
|
model = self.proxy_man.get_proxy_model()
|
||||||
self.set_model(model)
|
self.set_model(model)
|
||||||
|
|
||||||
# NOTE: each tab context has its own unique maps
|
# NOTE: each tab context has its own unique maps
|
||||||
@@ -132,8 +132,8 @@ class ServerTreeView(ContextMixin, TreeView):
|
|||||||
def get_map_man(self) -> MapManager:
|
def get_map_man(self) -> MapManager:
|
||||||
return self.map_man
|
return self.map_man
|
||||||
|
|
||||||
def get_filter_man(self) -> FilteredModelManager:
|
def get_proxy_man(self) -> ProxyModelManager:
|
||||||
return self.filter_man
|
return self.proxy_man
|
||||||
|
|
||||||
# def shrink_to_fit(self) -> None:
|
# def shrink_to_fit(self) -> None:
|
||||||
# cols = self.get_columns()
|
# cols = self.get_columns()
|
||||||
@@ -372,6 +372,6 @@ class ServerTreeView(ContextMixin, TreeView):
|
|||||||
return
|
return
|
||||||
if row_index >= start[0] <= end[0]:
|
if row_index >= start[0] <= end[0]:
|
||||||
# NOTE: fetch raw data rows
|
# NOTE: fetch raw data rows
|
||||||
real_model = self.filter_man.get_control()
|
real_model = self.proxy_man.get_control()
|
||||||
value = real_model[row_index][col_index]
|
value = real_model[row_index][col_index]
|
||||||
cell.set_property("text", str(value))
|
cell.set_property("text", str(value))
|
||||||
Reference in new issue
Block a user