mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Start to move column display logic into the view classes, where it belongs
This commit is contained in:
parent
ce023e2c56
commit
d393b430bd
@ -19,7 +19,7 @@ from PyQt4.QtCore import QAbstractTableModel, QVariant, Qt, pyqtSignal, \
|
|||||||
from calibre import strftime
|
from calibre import strftime
|
||||||
from calibre.ebooks.metadata import fmt_sidx, authors_to_string, string_to_authors
|
from calibre.ebooks.metadata import fmt_sidx, authors_to_string, string_to_authors
|
||||||
from calibre.ebooks.metadata.meta import set_metadata as _set_metadata
|
from calibre.ebooks.metadata.meta import set_metadata as _set_metadata
|
||||||
from calibre.gui2 import NONE, TableView, config, error_dialog, UNDEFINED_QDATE
|
from calibre.gui2 import NONE, config, error_dialog, UNDEFINED_QDATE
|
||||||
from calibre.gui2.dialogs.comments_dialog import CommentsDialog
|
from calibre.gui2.dialogs.comments_dialog import CommentsDialog
|
||||||
from calibre.gui2.widgets import EnLineEdit, TagsLineEdit
|
from calibre.gui2.widgets import EnLineEdit, TagsLineEdit
|
||||||
from calibre.library.caches import _match, CONTAINS_MATCH, EQUALS_MATCH, REGEXP_MATCH
|
from calibre.library.caches import _match, CONTAINS_MATCH, EQUALS_MATCH, REGEXP_MATCH
|
||||||
@ -30,6 +30,15 @@ from calibre.utils.pyparsing import ParseException
|
|||||||
from calibre.utils.search_query_parser import SearchQueryParser
|
from calibre.utils.search_query_parser import SearchQueryParser
|
||||||
|
|
||||||
# Delegates {{{
|
# Delegates {{{
|
||||||
|
|
||||||
|
class DummyDelegate(QStyledItemDelegate):
|
||||||
|
|
||||||
|
def sizeHint(self, option, index):
|
||||||
|
return QSize(0, 0)
|
||||||
|
|
||||||
|
def paint(self, painter, option, index):
|
||||||
|
pass
|
||||||
|
|
||||||
class RatingDelegate(QStyledItemDelegate):
|
class RatingDelegate(QStyledItemDelegate):
|
||||||
COLOR = QColor("blue")
|
COLOR = QColor("blue")
|
||||||
SIZE = 16
|
SIZE = 16
|
||||||
@ -313,6 +322,7 @@ class BooksModel(QAbstractTableModel): # {{{
|
|||||||
|
|
||||||
orig_headers = {
|
orig_headers = {
|
||||||
'title' : _("Title"),
|
'title' : _("Title"),
|
||||||
|
'ondevice' : _("On Device"),
|
||||||
'authors' : _("Author(s)"),
|
'authors' : _("Author(s)"),
|
||||||
'size' : _("Size (MB)"),
|
'size' : _("Size (MB)"),
|
||||||
'timestamp' : _("Date"),
|
'timestamp' : _("Date"),
|
||||||
@ -321,7 +331,6 @@ class BooksModel(QAbstractTableModel): # {{{
|
|||||||
'publisher' : _("Publisher"),
|
'publisher' : _("Publisher"),
|
||||||
'tags' : _("Tags"),
|
'tags' : _("Tags"),
|
||||||
'series' : _("Series"),
|
'series' : _("Series"),
|
||||||
'ondevice' : _("On Device"),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, parent=None, buffer=40):
|
def __init__(self, parent=None, buffer=40):
|
||||||
@ -342,6 +351,7 @@ class BooksModel(QAbstractTableModel): # {{{
|
|||||||
self.bool_no_icon = QIcon(I('list_remove.svg'))
|
self.bool_no_icon = QIcon(I('list_remove.svg'))
|
||||||
self.bool_blank_icon = QIcon(I('blank.svg'))
|
self.bool_blank_icon = QIcon(I('blank.svg'))
|
||||||
self.device_connected = False
|
self.device_connected = False
|
||||||
|
self.read_config()
|
||||||
|
|
||||||
def is_custom_column(self, cc_label):
|
def is_custom_column(self, cc_label):
|
||||||
return cc_label in self.custom_columns
|
return cc_label in self.custom_columns
|
||||||
@ -352,29 +362,10 @@ class BooksModel(QAbstractTableModel): # {{{
|
|||||||
|
|
||||||
def read_config(self):
|
def read_config(self):
|
||||||
self.use_roman_numbers = config['use_roman_numerals_for_series_number']
|
self.use_roman_numbers = config['use_roman_numerals_for_series_number']
|
||||||
cmap = config['column_map'][:] # force a copy
|
|
||||||
self.headers = {}
|
|
||||||
self.column_map = []
|
|
||||||
for col in cmap: # take out any columns no longer in the db
|
|
||||||
if col == 'ondevice':
|
|
||||||
if self.device_connected:
|
|
||||||
self.column_map.append(col)
|
|
||||||
elif col in self.orig_headers or col in self.custom_columns:
|
|
||||||
self.column_map.append(col)
|
|
||||||
for col in self.column_map:
|
|
||||||
if col in self.orig_headers:
|
|
||||||
self.headers[col] = self.orig_headers[col]
|
|
||||||
elif col in self.custom_columns:
|
|
||||||
self.headers[col] = self.custom_columns[col]['name']
|
|
||||||
self.build_data_convertors()
|
|
||||||
self.reset()
|
|
||||||
self.emit(SIGNAL('columns_sorted()'))
|
|
||||||
|
|
||||||
def set_device_connected(self, is_connected):
|
def set_device_connected(self, is_connected):
|
||||||
self.device_connected = is_connected
|
self.device_connected = is_connected
|
||||||
self.read_config()
|
|
||||||
self.db.refresh_ondevice()
|
self.db.refresh_ondevice()
|
||||||
self.database_changed.emit(self.db)
|
|
||||||
|
|
||||||
def set_book_on_device_func(self, func):
|
def set_book_on_device_func(self, func):
|
||||||
self.book_on_device = func
|
self.book_on_device = func
|
||||||
@ -382,7 +373,24 @@ class BooksModel(QAbstractTableModel): # {{{
|
|||||||
def set_database(self, db):
|
def set_database(self, db):
|
||||||
self.db = db
|
self.db = db
|
||||||
self.custom_columns = self.db.custom_column_label_map
|
self.custom_columns = self.db.custom_column_label_map
|
||||||
self.read_config()
|
self.column_map = list(self.orig_headers.keys()) + \
|
||||||
|
list(self.custom_columns)
|
||||||
|
def col_idx(name):
|
||||||
|
if name == 'ondevice':
|
||||||
|
return -1
|
||||||
|
if name not in self.db.FIELD_MAP:
|
||||||
|
return 100000
|
||||||
|
return self.db.FIELD_MAP[name]
|
||||||
|
|
||||||
|
self.column_map.sort(cmp=lambda x,y: cmp(col_idx(x), col_idx(y)))
|
||||||
|
for col in self.column_map:
|
||||||
|
if col in self.orig_headers:
|
||||||
|
self.headers[col] = self.orig_headers[col]
|
||||||
|
elif col in self.custom_columns:
|
||||||
|
self.headers[col] = self.custom_columns[col]['name']
|
||||||
|
|
||||||
|
self.build_data_convertors()
|
||||||
|
self.reset()
|
||||||
self.database_changed.emit(db)
|
self.database_changed.emit(db)
|
||||||
|
|
||||||
def refresh_ids(self, ids, current_row=-1):
|
def refresh_ids(self, ids, current_row=-1):
|
||||||
@ -982,7 +990,7 @@ class BooksModel(QAbstractTableModel): # {{{
|
|||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
class BooksView(TableView):
|
class BooksView(QTableView): # {{{
|
||||||
TIME_FMT = '%d %b %Y'
|
TIME_FMT = '%d %b %Y'
|
||||||
wrapper = textwrap.TextWrapper(width=20)
|
wrapper = textwrap.TextWrapper(width=20)
|
||||||
|
|
||||||
@ -997,7 +1005,7 @@ class BooksView(TableView):
|
|||||||
return ('%.'+str(precision)+'f') % ((size/(1024.*1024.)),)
|
return ('%.'+str(precision)+'f') % ((size/(1024.*1024.)),)
|
||||||
|
|
||||||
def __init__(self, parent, modelcls=BooksModel):
|
def __init__(self, parent, modelcls=BooksModel):
|
||||||
TableView.__init__(self, parent)
|
QTableView.__init__(self, parent)
|
||||||
self.rating_delegate = RatingDelegate(self)
|
self.rating_delegate = RatingDelegate(self)
|
||||||
self.timestamp_delegate = DateDelegate(self)
|
self.timestamp_delegate = DateDelegate(self)
|
||||||
self.pubdate_delegate = PubDateDelegate(self)
|
self.pubdate_delegate = PubDateDelegate(self)
|
||||||
@ -1005,6 +1013,7 @@ class BooksView(TableView):
|
|||||||
self.authors_delegate = TextDelegate(self)
|
self.authors_delegate = TextDelegate(self)
|
||||||
self.series_delegate = TextDelegate(self)
|
self.series_delegate = TextDelegate(self)
|
||||||
self.publisher_delegate = TextDelegate(self)
|
self.publisher_delegate = TextDelegate(self)
|
||||||
|
self.text_delegate = TextDelegate(self)
|
||||||
self.cc_text_delegate = CcTextDelegate(self)
|
self.cc_text_delegate = CcTextDelegate(self)
|
||||||
self.cc_bool_delegate = CcBoolDelegate(self)
|
self.cc_bool_delegate = CcBoolDelegate(self)
|
||||||
self.cc_comments_delegate = CcCommentsDelegate(self)
|
self.cc_comments_delegate = CcCommentsDelegate(self)
|
||||||
@ -1013,13 +1022,9 @@ class BooksView(TableView):
|
|||||||
self.setModel(self._model)
|
self.setModel(self._model)
|
||||||
self.setSelectionBehavior(QAbstractItemView.SelectRows)
|
self.setSelectionBehavior(QAbstractItemView.SelectRows)
|
||||||
self.setSortingEnabled(True)
|
self.setSortingEnabled(True)
|
||||||
for i in range(10):
|
self.selectionModel().currentRowChanged.connect(self._model.current_changed)
|
||||||
self.setItemDelegateForColumn(i, TextDelegate(self))
|
self.column_header = self.horizontalHeader()
|
||||||
self.columns_sorted()
|
self._model.database_changed.connect(self.database_changed)
|
||||||
QObject.connect(self.selectionModel(), SIGNAL('currentRowChanged(QModelIndex, QModelIndex)'),
|
|
||||||
self._model.current_changed)
|
|
||||||
self.connect(self._model, SIGNAL('columns_sorted()'),
|
|
||||||
self.columns_sorted, Qt.QueuedConnection)
|
|
||||||
hv = self.verticalHeader()
|
hv = self.verticalHeader()
|
||||||
hv.setClickable(True)
|
hv.setClickable(True)
|
||||||
hv.setCursor(Qt.PointingHandCursor)
|
hv.setCursor(Qt.PointingHandCursor)
|
||||||
@ -1040,56 +1045,49 @@ class BooksView(TableView):
|
|||||||
sm.select(idx, sm.Select|sm.Rows)
|
sm.select(idx, sm.Select|sm.Rows)
|
||||||
self.selected_ids = []
|
self.selected_ids = []
|
||||||
|
|
||||||
def columns_sorted(self):
|
def set_ondevice_column_visibility(self):
|
||||||
|
m = self._model
|
||||||
|
self.column_header.setSectionHidden(m.column_map.index('ondevice'),
|
||||||
|
not m.device_connected)
|
||||||
|
|
||||||
|
def set_device_connected(self, is_connected):
|
||||||
|
self._model.set_device_connected(is_connected)
|
||||||
|
self.set_ondevice_column_visibility()
|
||||||
|
|
||||||
|
def database_changed(self, db):
|
||||||
for i in range(self.model().columnCount(None)):
|
for i in range(self.model().columnCount(None)):
|
||||||
if self.itemDelegateForColumn(i) in (self.rating_delegate,
|
if self.itemDelegateForColumn(i) in (self.rating_delegate,
|
||||||
self.timestamp_delegate, self.pubdate_delegate):
|
self.timestamp_delegate, self.pubdate_delegate):
|
||||||
self.setItemDelegateForColumn(i, self.itemDelegate())
|
self.setItemDelegateForColumn(i, self.itemDelegate())
|
||||||
|
|
||||||
cm = self._model.column_map
|
cm = self._model.column_map
|
||||||
|
self.set_ondevice_column_visibility()
|
||||||
|
|
||||||
if 'rating' in cm:
|
|
||||||
self.setItemDelegateForColumn(cm.index('rating'), self.rating_delegate)
|
|
||||||
if 'timestamp' in cm:
|
|
||||||
self.setItemDelegateForColumn(cm.index('timestamp'), self.timestamp_delegate)
|
|
||||||
if 'pubdate' in cm:
|
|
||||||
self.setItemDelegateForColumn(cm.index('pubdate'), self.pubdate_delegate)
|
|
||||||
if 'tags' in cm:
|
|
||||||
self.setItemDelegateForColumn(cm.index('tags'), self.tags_delegate)
|
|
||||||
if 'authors' in cm:
|
|
||||||
self.setItemDelegateForColumn(cm.index('authors'), self.authors_delegate)
|
|
||||||
if 'publisher' in cm:
|
|
||||||
self.setItemDelegateForColumn(cm.index('publisher'), self.publisher_delegate)
|
|
||||||
if 'series' in cm:
|
|
||||||
self.setItemDelegateForColumn(cm.index('series'), self.series_delegate)
|
|
||||||
for colhead in cm:
|
for colhead in cm:
|
||||||
if not self._model.is_custom_column(colhead):
|
if self._model.is_custom_column(colhead):
|
||||||
continue
|
cc = self._model.custom_columns[colhead]
|
||||||
cc = self._model.custom_columns[colhead]
|
if cc['datatype'] == 'datetime':
|
||||||
if cc['datatype'] == 'datetime':
|
delegate = CcDateDelegate(self)
|
||||||
delegate = CcDateDelegate(self)
|
delegate.set_format(cc['display'].get('date_format',''))
|
||||||
delegate.set_format(cc['display'].get('date_format',''))
|
self.setItemDelegateForColumn(cm.index(colhead), delegate)
|
||||||
self.setItemDelegateForColumn(cm.index(colhead), delegate)
|
elif cc['datatype'] == 'comments':
|
||||||
elif cc['datatype'] == 'comments':
|
self.setItemDelegateForColumn(cm.index(colhead), self.cc_comments_delegate)
|
||||||
self.setItemDelegateForColumn(cm.index(colhead), self.cc_comments_delegate)
|
elif cc['datatype'] == 'text':
|
||||||
elif cc['datatype'] == 'text':
|
if cc['is_multiple']:
|
||||||
if cc['is_multiple']:
|
self.setItemDelegateForColumn(cm.index(colhead), self.tags_delegate)
|
||||||
self.setItemDelegateForColumn(cm.index(colhead), self.tags_delegate)
|
else:
|
||||||
else:
|
self.setItemDelegateForColumn(cm.index(colhead), self.cc_text_delegate)
|
||||||
|
elif cc['datatype'] in ('int', 'float'):
|
||||||
self.setItemDelegateForColumn(cm.index(colhead), self.cc_text_delegate)
|
self.setItemDelegateForColumn(cm.index(colhead), self.cc_text_delegate)
|
||||||
elif cc['datatype'] in ('int', 'float'):
|
elif cc['datatype'] == 'bool':
|
||||||
self.setItemDelegateForColumn(cm.index(colhead), self.cc_text_delegate)
|
self.setItemDelegateForColumn(cm.index(colhead), self.cc_bool_delegate)
|
||||||
elif cc['datatype'] == 'bool':
|
elif cc['datatype'] == 'rating':
|
||||||
self.setItemDelegateForColumn(cm.index(colhead), self.cc_bool_delegate)
|
self.setItemDelegateForColumn(cm.index(colhead), self.rating_delegate)
|
||||||
elif cc['datatype'] == 'rating':
|
else:
|
||||||
self.setItemDelegateForColumn(cm.index(colhead), self.rating_delegate)
|
dattr = colhead+'_delegate'
|
||||||
if not self.restore_column_widths():
|
delegate = colhead if hasattr(self, dattr) else 'text'
|
||||||
self.resizeColumnsToContents()
|
self.setItemDelegateForColumn(cm.index(colhead), getattr(self,
|
||||||
|
delegate+'_delegate'))
|
||||||
sort_col = self._model.sorted_on[0]
|
|
||||||
if sort_col in cm:
|
|
||||||
idx = cm.index(sort_col)
|
|
||||||
self.horizontalHeader().setSortIndicator(idx, self._model.sorted_on[1])
|
|
||||||
|
|
||||||
def set_context_menu(self, edit_metadata, send_to_device, convert, view,
|
def set_context_menu(self, edit_metadata, send_to_device, convert, view,
|
||||||
save, open_folder, book_details, delete, similar_menu=None):
|
save, open_folder, book_details, delete, similar_menu=None):
|
||||||
@ -1131,7 +1129,7 @@ class BooksView(TableView):
|
|||||||
idx = self._model.column_map.index(colname)
|
idx = self._model.column_map.index(colname)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
idx = 0
|
idx = 0
|
||||||
TableView.sortByColumn(self, idx, order)
|
QTableView.sortByColumn(self, idx, order)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def paths_from_event(cls, event):
|
def paths_from_event(cls, event):
|
||||||
@ -1195,6 +1193,8 @@ class BooksView(TableView):
|
|||||||
def row_count(self):
|
def row_count(self):
|
||||||
return self._model.count()
|
return self._model.count()
|
||||||
|
|
||||||
|
# }}}
|
||||||
|
|
||||||
class DeviceBooksView(BooksView):
|
class DeviceBooksView(BooksView):
|
||||||
|
|
||||||
def __init__(self, parent):
|
def __init__(self, parent):
|
||||||
@ -1218,7 +1218,7 @@ class DeviceBooksView(BooksView):
|
|||||||
QObject.connect(self._model, SIGNAL('booklist_dirtied()'), slot)
|
QObject.connect(self._model, SIGNAL('booklist_dirtied()'), slot)
|
||||||
|
|
||||||
def sortByColumn(self, col, order):
|
def sortByColumn(self, col, order):
|
||||||
TableView.sortByColumn(self, col, order)
|
QTableView.sortByColumn(self, col, order)
|
||||||
|
|
||||||
def dropEvent(self, *args):
|
def dropEvent(self, *args):
|
||||||
error_dialog(self, _('Not allowed'),
|
error_dialog(self, _('Not allowed'),
|
||||||
|
@ -535,8 +535,6 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
|||||||
self.library_view.model().set_book_on_device_func(self.book_on_device)
|
self.library_view.model().set_book_on_device_func(self.book_on_device)
|
||||||
prefs['library_path'] = self.library_path
|
prefs['library_path'] = self.library_path
|
||||||
self.library_view.restore_sort_at_startup(dynamic.get('sort_history', [('timestamp', Qt.DescendingOrder)]))
|
self.library_view.restore_sort_at_startup(dynamic.get('sort_history', [('timestamp', Qt.DescendingOrder)]))
|
||||||
if not self.library_view.restore_column_widths():
|
|
||||||
self.library_view.resizeColumnsToContents()
|
|
||||||
self.search.setFocus(Qt.OtherFocusReason)
|
self.search.setFocus(Qt.OtherFocusReason)
|
||||||
self.cover_cache = CoverCache(self.library_path)
|
self.cover_cache = CoverCache(self.library_path)
|
||||||
self.cover_cache.start()
|
self.cover_cache.start()
|
||||||
@ -943,7 +941,8 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
|||||||
|
|
||||||
def save_device_view_settings(self):
|
def save_device_view_settings(self):
|
||||||
model = self.location_view.model()
|
model = self.location_view.model()
|
||||||
self.memory_view.write_settings()
|
return
|
||||||
|
#self.memory_view.write_settings()
|
||||||
for x in range(model.rowCount()):
|
for x in range(model.rowCount()):
|
||||||
if x > 1:
|
if x > 1:
|
||||||
if model.location_for_row(x) == 'carda':
|
if model.location_for_row(x) == 'carda':
|
||||||
@ -1030,10 +1029,6 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
|||||||
self.card_b_view.set_editable(self.device_manager.device.CAN_SET_METADATA)
|
self.card_b_view.set_editable(self.device_manager.device.CAN_SET_METADATA)
|
||||||
for view in (self.memory_view, self.card_a_view, self.card_b_view):
|
for view in (self.memory_view, self.card_a_view, self.card_b_view):
|
||||||
view.sortByColumn(3, Qt.DescendingOrder)
|
view.sortByColumn(3, Qt.DescendingOrder)
|
||||||
view.read_settings()
|
|
||||||
if not view.restore_column_widths():
|
|
||||||
view.resizeColumnsToContents()
|
|
||||||
view.resize_on_select = not view.isVisible()
|
|
||||||
if view.model().rowCount(None) > 1:
|
if view.model().rowCount(None) > 1:
|
||||||
view.resizeRowToContents(0)
|
view.resizeRowToContents(0)
|
||||||
height = view.rowHeight(0)
|
height = view.rowHeight(0)
|
||||||
@ -1048,8 +1043,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
|||||||
self.book_on_device(None, reset=True)
|
self.book_on_device(None, reset=True)
|
||||||
if reset_only:
|
if reset_only:
|
||||||
return
|
return
|
||||||
self.library_view.write_settings()
|
self.library_view.set_device_connected(device_connected)
|
||||||
self.library_view.model().set_device_connected(device_connected)
|
|
||||||
############################################################################
|
############################################################################
|
||||||
|
|
||||||
######################### Fetch annotations ################################
|
######################### Fetch annotations ################################
|
||||||
@ -2262,8 +2256,6 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
|||||||
return
|
return
|
||||||
d = ConfigDialog(self, self.library_view.model(),
|
d = ConfigDialog(self, self.library_view.model(),
|
||||||
server=self.content_server)
|
server=self.content_server)
|
||||||
# Save current column widths in case columns are turned on or off
|
|
||||||
self.library_view.write_settings()
|
|
||||||
|
|
||||||
d.exec_()
|
d.exec_()
|
||||||
self.content_server = d.server
|
self.content_server = d.server
|
||||||
@ -2328,14 +2320,6 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
|||||||
'''
|
'''
|
||||||
page = 0 if location == 'library' else 1 if location == 'main' else 2 if location == 'carda' else 3
|
page = 0 if location == 'library' else 1 if location == 'main' else 2 if location == 'carda' else 3
|
||||||
self.stack.setCurrentIndex(page)
|
self.stack.setCurrentIndex(page)
|
||||||
view = self.memory_view if page == 1 else \
|
|
||||||
self.card_a_view if page == 2 else \
|
|
||||||
self.card_b_view if page == 3 else None
|
|
||||||
if view:
|
|
||||||
if view.resize_on_select:
|
|
||||||
if not view.restore_column_widths():
|
|
||||||
view.resizeColumnsToContents()
|
|
||||||
view.resize_on_select = False
|
|
||||||
self.status_bar.reset_info()
|
self.status_bar.reset_info()
|
||||||
self.sidebar.location_changed(location)
|
self.sidebar.location_changed(location)
|
||||||
if location == 'library':
|
if location == 'library':
|
||||||
@ -2442,9 +2426,6 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
|||||||
config.set('main_window_geometry', self.saveGeometry())
|
config.set('main_window_geometry', self.saveGeometry())
|
||||||
dynamic.set('sort_history', self.library_view.model().sort_history)
|
dynamic.set('sort_history', self.library_view.model().sort_history)
|
||||||
self.sidebar.save_state()
|
self.sidebar.save_state()
|
||||||
self.library_view.write_settings()
|
|
||||||
if self.device_connected:
|
|
||||||
self.save_device_view_settings()
|
|
||||||
|
|
||||||
def restart(self):
|
def restart(self):
|
||||||
self.quit(restart=True)
|
self.quit(restart=True)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user