Compare commits
No commits in common. "9a7d24b8957d97d6a2544bf3ba84a951ee88b3dd" and "fed4e9fbdefb6532179695369e6b42c5997ae349" have entirely different histories.
9a7d24b895
...
fed4e9fbde
@ -45,9 +45,11 @@ def Session() -> Generator[scoped_session, None, None]:
|
|||||||
function = frame.function
|
function = frame.function
|
||||||
lineno = frame.lineno
|
lineno = frame.lineno
|
||||||
Session = scoped_session(sessionmaker(bind=engine, future=True))
|
Session = scoped_session(sessionmaker(bind=engine, future=True))
|
||||||
log.debug(f"SqlA: session acquired [{hex(id(Session))}]")
|
log.debug(
|
||||||
log.debug(f"Session acquisition: {function}:{lineno} [{hex(id(Session))}]")
|
f"Session acquired, {file=}, {function=}, "
|
||||||
|
f"function{lineno=}, {Session=}"
|
||||||
|
)
|
||||||
yield Session
|
yield Session
|
||||||
log.debug(f" SqlA: session released [{hex(id(Session))}]")
|
log.debug(" Session released")
|
||||||
Session.commit()
|
Session.commit()
|
||||||
Session.close()
|
Session.close()
|
||||||
|
|||||||
@ -249,17 +249,6 @@ class ImportTrack(QObject):
|
|||||||
self.finished.emit(self.playlist)
|
self.finished.emit(self.playlist)
|
||||||
|
|
||||||
|
|
||||||
class MusicMusterSignals(QObject):
|
|
||||||
"""
|
|
||||||
Class for all MusicMuster signals. See:
|
|
||||||
- https://zetcode.com/gui/pyqt5/eventssignals/
|
|
||||||
- https://stackoverflow.com/questions/62654525/
|
|
||||||
emit-a-signal-from-another-class-to-main-class
|
|
||||||
"""
|
|
||||||
|
|
||||||
save_playlist_signal = pyqtSignal()
|
|
||||||
|
|
||||||
|
|
||||||
class Window(QMainWindow, Ui_MainWindow):
|
class Window(QMainWindow, Ui_MainWindow):
|
||||||
def __init__(self, parent=None, *args, **kwargs) -> None:
|
def __init__(self, parent=None, *args, **kwargs) -> None:
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
@ -278,8 +267,6 @@ class Window(QMainWindow, Ui_MainWindow):
|
|||||||
self.previous_track_position: Optional[float] = None
|
self.previous_track_position: Optional[float] = None
|
||||||
self.selected_plrs: Optional[List[PlaylistRows]] = None
|
self.selected_plrs: Optional[List[PlaylistRows]] = None
|
||||||
|
|
||||||
self.signals = MusicMusterSignals()
|
|
||||||
|
|
||||||
# Set colours that will be used by playlist row stripes
|
# Set colours that will be used by playlist row stripes
|
||||||
palette = QPalette()
|
palette = QPalette()
|
||||||
palette.setColor(QPalette.Base, QColor(Config.COLOUR_EVEN_PLAYLIST))
|
palette.setColor(QPalette.Base, QColor(Config.COLOUR_EVEN_PLAYLIST))
|
||||||
@ -527,9 +514,11 @@ class Window(QMainWindow, Ui_MainWindow):
|
|||||||
"Can't close current track playlist", 5000)
|
"Can't close current track playlist", 5000)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Attempt to close next track playlist
|
# Don't close next track playlist
|
||||||
if self.tabPlaylist.widget(tab_index) == self.next_track.playlist_tab:
|
if self.tabPlaylist.widget(tab_index) == self.next_track.playlist_tab:
|
||||||
self.next_track.playlist_tab.mark_unnext()
|
self.statusbar.showMessage(
|
||||||
|
"Can't close next track playlist", 5000)
|
||||||
|
return False
|
||||||
|
|
||||||
# Record playlist as closed and update remaining playlist tabs
|
# Record playlist as closed and update remaining playlist tabs
|
||||||
with Session() as session:
|
with Session() as session:
|
||||||
@ -625,8 +614,7 @@ class Window(QMainWindow, Ui_MainWindow):
|
|||||||
assert playlist.id
|
assert playlist.id
|
||||||
|
|
||||||
playlist_tab = PlaylistTab(
|
playlist_tab = PlaylistTab(
|
||||||
musicmuster=self, session=session, playlist_id=playlist.id,
|
musicmuster=self, session=session, playlist_id=playlist.id)
|
||||||
signals=self.signals)
|
|
||||||
idx = self.tabPlaylist.addTab(playlist_tab, playlist.name)
|
idx = self.tabPlaylist.addTab(playlist_tab, playlist.name)
|
||||||
self.tabPlaylist.setCurrentIndex(idx)
|
self.tabPlaylist.setCurrentIndex(idx)
|
||||||
|
|
||||||
@ -646,8 +634,6 @@ class Window(QMainWindow, Ui_MainWindow):
|
|||||||
def debug(self):
|
def debug(self):
|
||||||
"""Invoke debugger"""
|
"""Invoke debugger"""
|
||||||
|
|
||||||
visible_playlist_id = self.visible_playlist_tab().playlist_id
|
|
||||||
print(f"Active playlist id={visible_playlist_id}")
|
|
||||||
import ipdb # type: ignore
|
import ipdb # type: ignore
|
||||||
ipdb.set_trace()
|
ipdb.set_trace()
|
||||||
|
|
||||||
|
|||||||
107
app/playlists.py
107
app/playlists.py
@ -8,6 +8,7 @@ from datetime import datetime, timedelta
|
|||||||
from typing import cast, List, Optional, TYPE_CHECKING, Union
|
from typing import cast, List, Optional, TYPE_CHECKING, Union
|
||||||
|
|
||||||
from PyQt5.QtCore import (
|
from PyQt5.QtCore import (
|
||||||
|
pyqtSignal,
|
||||||
QEvent,
|
QEvent,
|
||||||
QModelIndex,
|
QModelIndex,
|
||||||
QObject,
|
QObject,
|
||||||
@ -59,7 +60,7 @@ from models import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from musicmuster import Window, MusicMusterSignals
|
from musicmuster import Window
|
||||||
|
|
||||||
start_time_re = re.compile(r"@\d\d:\d\d:\d\d")
|
start_time_re = re.compile(r"@\d\d:\d\d:\d\d")
|
||||||
HEADER_NOTES_COLUMN = 2
|
HEADER_NOTES_COLUMN = 2
|
||||||
@ -137,11 +138,10 @@ class PlaylistTab(QTableWidget):
|
|||||||
|
|
||||||
def __init__(self, musicmuster: "Window",
|
def __init__(self, musicmuster: "Window",
|
||||||
session: scoped_session,
|
session: scoped_session,
|
||||||
playlist_id: int, signals: "MusicMusterSignals") -> None:
|
playlist_id: int, *args, **kwargs) -> None:
|
||||||
super().__init__()
|
super().__init__(*args, **kwargs)
|
||||||
self.musicmuster: Window = musicmuster
|
self.musicmuster: Window = musicmuster
|
||||||
self.playlist_id = playlist_id
|
self.playlist_id = playlist_id
|
||||||
self.signals = signals
|
|
||||||
|
|
||||||
# Set up widget
|
# Set up widget
|
||||||
self.menu: Optional[QMenu] = None
|
self.menu: Optional[QMenu] = None
|
||||||
@ -170,7 +170,6 @@ class PlaylistTab(QTableWidget):
|
|||||||
self.horizontalHeader().sectionResized.connect(
|
self.horizontalHeader().sectionResized.connect(
|
||||||
self.resizeRowsToContents)
|
self.resizeRowsToContents)
|
||||||
|
|
||||||
# Drag and drop setup
|
|
||||||
self.setAcceptDrops(True)
|
self.setAcceptDrops(True)
|
||||||
self.viewport().setAcceptDrops(True)
|
self.viewport().setAcceptDrops(True)
|
||||||
self.setDragDropOverwriteMode(False)
|
self.setDragDropOverwriteMode(False)
|
||||||
@ -194,7 +193,6 @@ class PlaylistTab(QTableWidget):
|
|||||||
self.selecting_in_progress = False
|
self.selecting_in_progress = False
|
||||||
# Connect signals
|
# Connect signals
|
||||||
self.horizontalHeader().sectionResized.connect(self._column_resize)
|
self.horizontalHeader().sectionResized.connect(self._column_resize)
|
||||||
self.signals.save_playlist_signal.connect(self._deferred_save)
|
|
||||||
|
|
||||||
# Load playlist rows
|
# Load playlist rows
|
||||||
self.populate_display(session, self.playlist_id)
|
self.populate_display(session, self.playlist_id)
|
||||||
@ -292,7 +290,7 @@ class PlaylistTab(QTableWidget):
|
|||||||
act_unnext = self.menu.addAction(
|
act_unnext = self.menu.addAction(
|
||||||
"Unmark as next track")
|
"Unmark as next track")
|
||||||
act_unnext.triggered.connect(
|
act_unnext.triggered.connect(
|
||||||
lambda: self.mark_unnext())
|
lambda: self._mark_unnext(row_number))
|
||||||
if sep:
|
if sep:
|
||||||
self.menu.addSeparator()
|
self.menu.addSeparator()
|
||||||
|
|
||||||
@ -442,7 +440,7 @@ class PlaylistTab(QTableWidget):
|
|||||||
|
|
||||||
track_id = self._get_row_track_id(row)
|
track_id = self._get_row_track_id(row)
|
||||||
|
|
||||||
# Determine cell type changed
|
# Determin cell type changed
|
||||||
with Session() as session:
|
with Session() as session:
|
||||||
# Get playlistrow object
|
# Get playlistrow object
|
||||||
plr_id = self._get_playlistrow_id(row)
|
plr_id = self._get_playlistrow_id(row)
|
||||||
@ -462,12 +460,8 @@ class PlaylistTab(QTableWidget):
|
|||||||
self._set_row_start_time(row, start_time)
|
self._set_row_start_time(row, start_time)
|
||||||
else:
|
else:
|
||||||
self._set_row_start_time(row, None)
|
self._set_row_start_time(row, None)
|
||||||
# Update note display
|
# Update display including note colour
|
||||||
self._set_row_note(session, row, new_text)
|
self._set_row_note(session, row, new_text)
|
||||||
# If this is a header row, ecalcuate track times in case
|
|
||||||
# note added a start time
|
|
||||||
if not track_id:
|
|
||||||
self._update_start_end_times()
|
|
||||||
else:
|
else:
|
||||||
track = None
|
track = None
|
||||||
if track_id:
|
if track_id:
|
||||||
@ -599,14 +593,14 @@ class PlaylistTab(QTableWidget):
|
|||||||
return [self._get_playlistrow_id(a) for a in self._get_selected_rows()]
|
return [self._get_playlistrow_id(a) for a in self._get_selected_rows()]
|
||||||
|
|
||||||
def get_selected_playlistrows(
|
def get_selected_playlistrows(
|
||||||
self, session: scoped_session) -> List[PlaylistRows]:
|
self, session: scoped_session) -> Optional[List[PlaylistRows]]:
|
||||||
"""
|
"""
|
||||||
Return a list of PlaylistRows of the selected rows
|
Return a list of PlaylistRows of the selected rows
|
||||||
"""
|
"""
|
||||||
|
|
||||||
plr_ids = self.get_selected_playlistrow_ids()
|
plr_ids = self.get_selected_playlistrow_ids()
|
||||||
if not plr_ids:
|
if not plr_ids:
|
||||||
return []
|
return None
|
||||||
plrs = [session.get(PlaylistRows, a) for a in plr_ids]
|
plrs = [session.get(PlaylistRows, a) for a in plr_ids]
|
||||||
|
|
||||||
return [plr for plr in plrs if plr is not None]
|
return [plr for plr in plrs if plr is not None]
|
||||||
@ -706,8 +700,7 @@ class PlaylistTab(QTableWidget):
|
|||||||
_ = self._set_row_userdata(row, self.ROW_TRACK_ID, 0)
|
_ = self._set_row_userdata(row, self.ROW_TRACK_ID, 0)
|
||||||
|
|
||||||
if update_track_times:
|
if update_track_times:
|
||||||
# Queue time updates so playlist updates first
|
self._update_start_end_times()
|
||||||
QTimer.singleShot(0, lambda: self._update_start_end_times())
|
|
||||||
|
|
||||||
def insert_track(self, session: scoped_session, track: Tracks,
|
def insert_track(self, session: scoped_session, track: Tracks,
|
||||||
note: Optional[str] = None, repaint: bool = True) -> None:
|
note: Optional[str] = None, repaint: bool = True) -> None:
|
||||||
@ -750,19 +743,6 @@ class PlaylistTab(QTableWidget):
|
|||||||
# Let display update, then save playlist
|
# Let display update, then save playlist
|
||||||
QTimer.singleShot(0, lambda: self.save_playlist(session))
|
QTimer.singleShot(0, lambda: self.save_playlist(session))
|
||||||
|
|
||||||
def mark_unnext(self) -> None:
|
|
||||||
"""
|
|
||||||
Unmark passed row as next track
|
|
||||||
"""
|
|
||||||
|
|
||||||
row = self._get_next_track_row_number()
|
|
||||||
if not row:
|
|
||||||
return
|
|
||||||
self.musicmuster.clear_next()
|
|
||||||
self.clear_selection()
|
|
||||||
self._set_row_colour(row, None)
|
|
||||||
self.musicmuster.update_headers()
|
|
||||||
|
|
||||||
def play_started(self, session: scoped_session) -> None:
|
def play_started(self, session: scoped_session) -> None:
|
||||||
"""
|
"""
|
||||||
Notification from musicmuster that track has started playing.
|
Notification from musicmuster that track has started playing.
|
||||||
@ -853,6 +833,13 @@ class PlaylistTab(QTableWidget):
|
|||||||
for row in sorted(row_numbers, reverse=True):
|
for row in sorted(row_numbers, reverse=True):
|
||||||
self.removeRow(row)
|
self.removeRow(row)
|
||||||
|
|
||||||
|
def remove_selected_rows(self) -> None:
|
||||||
|
"""Remove selected rows from display"""
|
||||||
|
|
||||||
|
self.remove_rows(self._get_selected_rows())
|
||||||
|
# Reset drag mode
|
||||||
|
self.setDragEnabled(False)
|
||||||
|
|
||||||
def reset_plr_row_colour(self, plr_id: int) -> None:
|
def reset_plr_row_colour(self, plr_id: int) -> None:
|
||||||
"""Reset background of row pointed to by plr_id"""
|
"""Reset background of row pointed to by plr_id"""
|
||||||
|
|
||||||
@ -1305,15 +1292,6 @@ class PlaylistTab(QTableWidget):
|
|||||||
cb.clear(mode=cb.Clipboard)
|
cb.clear(mode=cb.Clipboard)
|
||||||
cb.setText(pathqs, mode=cb.Clipboard)
|
cb.setText(pathqs, mode=cb.Clipboard)
|
||||||
|
|
||||||
def _deferred_save(self) -> None:
|
|
||||||
"""
|
|
||||||
Create session and save playlist
|
|
||||||
"""
|
|
||||||
|
|
||||||
print("_deferred_save() called")
|
|
||||||
with Session() as session:
|
|
||||||
self.save_playlist(session)
|
|
||||||
|
|
||||||
def _delete_rows(self) -> None:
|
def _delete_rows(self) -> None:
|
||||||
"""
|
"""
|
||||||
Delete mutliple rows
|
Delete mutliple rows
|
||||||
@ -1323,33 +1301,22 @@ class PlaylistTab(QTableWidget):
|
|||||||
- Save the playlist
|
- Save the playlist
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# Delete rows from database
|
||||||
|
plr_ids = self.get_selected_playlistrow_ids()
|
||||||
|
if not plr_ids:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Get confirmation
|
||||||
|
row_count = len(plr_ids)
|
||||||
|
plural = 's' if row_count > 1 else ''
|
||||||
|
if not ask_yes_no("Delete rows",
|
||||||
|
f"Really delete {row_count} row{plural}?"):
|
||||||
|
return
|
||||||
|
|
||||||
|
self.remove_selected_rows()
|
||||||
|
|
||||||
with Session() as session:
|
with Session() as session:
|
||||||
plrs = self.get_selected_playlistrows(session)
|
QTimer.singleShot(0, lambda: self.save_playlist(session))
|
||||||
row_count = len(plrs)
|
|
||||||
if not row_count:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Get confirmation
|
|
||||||
plural = 's' if row_count > 1 else ''
|
|
||||||
if not ask_yes_no("Delete rows",
|
|
||||||
f"Really delete {row_count} row{plural}?"):
|
|
||||||
return
|
|
||||||
|
|
||||||
rows_to_delete = [a.row_number for a in plrs]
|
|
||||||
|
|
||||||
# Delete rows from database. Would be more efficient to
|
|
||||||
# query then have a single delete.
|
|
||||||
for plr in plrs:
|
|
||||||
session.delete(plr)
|
|
||||||
|
|
||||||
# Remove from display
|
|
||||||
self.remove_rows(rows_to_delete)
|
|
||||||
|
|
||||||
# Reset drag mode
|
|
||||||
self.setDragEnabled(False)
|
|
||||||
|
|
||||||
# QTimer.singleShot(0, lambda: self._deferred_save())
|
|
||||||
self.signals.save_playlist_signal.emit()
|
|
||||||
|
|
||||||
def _drop_on(self, event):
|
def _drop_on(self, event):
|
||||||
"""
|
"""
|
||||||
@ -1651,6 +1618,16 @@ class PlaylistTab(QTableWidget):
|
|||||||
and pos.y() >= rect.center().y() # noqa W503
|
and pos.y() >= rect.center().y() # noqa W503
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def _mark_unnext(self, row: int) -> None:
|
||||||
|
"""
|
||||||
|
Unmark passed row as next track
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.musicmuster.clear_next()
|
||||||
|
self.clear_selection()
|
||||||
|
self._set_row_colour(row, None)
|
||||||
|
self.musicmuster.update_headers()
|
||||||
|
|
||||||
def _mark_unplayed(self, plr: PlaylistRows) -> None:
|
def _mark_unplayed(self, plr: PlaylistRows) -> None:
|
||||||
"""
|
"""
|
||||||
Mark passed row as unplayed in this playlist
|
Mark passed row as unplayed in this playlist
|
||||||
|
|||||||
110
tree.py
110
tree.py
@ -1,110 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
|
||||||
|
|
||||||
datas = {
|
|
||||||
"Category 1": [
|
|
||||||
("New Game 2", "Playnite", "", "", "Never", "Not Played", ""),
|
|
||||||
("New Game 3", "Playnite", "", "", "Never", "Not Played", ""),
|
|
||||||
],
|
|
||||||
"No Category": [
|
|
||||||
("New Game", "Playnite", "", "", "Never", "Not Plated", ""),
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
class GroupDelegate(QtWidgets.QStyledItemDelegate):
|
|
||||||
def __init__(self, parent=None):
|
|
||||||
super(GroupDelegate, self).__init__(parent)
|
|
||||||
self._plus_icon = QtGui.QIcon("plus.png")
|
|
||||||
self._minus_icon = QtGui.QIcon("minus.png")
|
|
||||||
|
|
||||||
def initStyleOption(self, option, index):
|
|
||||||
super(GroupDelegate, self).initStyleOption(option, index)
|
|
||||||
if not index.parent().isValid():
|
|
||||||
is_open = bool(option.state & QtWidgets.QStyle.State_Open)
|
|
||||||
option.features |= QtWidgets.QStyleOptionViewItem.HasDecoration
|
|
||||||
option.icon = self._minus_icon if is_open else self._plus_icon
|
|
||||||
|
|
||||||
class GroupView(QtWidgets.QTreeView):
|
|
||||||
def __init__(self, model, parent=None):
|
|
||||||
super(GroupView, self).__init__(parent)
|
|
||||||
self.setIndentation(0)
|
|
||||||
self.setExpandsOnDoubleClick(False)
|
|
||||||
self.clicked.connect(self.on_clicked)
|
|
||||||
delegate = GroupDelegate(self)
|
|
||||||
self.setItemDelegateForColumn(0, delegate)
|
|
||||||
self.setModel(model)
|
|
||||||
self.header().setSectionResizeMode(0, QtWidgets.QHeaderView.ResizeToContents)
|
|
||||||
self.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
|
|
||||||
self.setStyleSheet("background-color: #0D1225;")
|
|
||||||
|
|
||||||
@QtCore.pyqtSlot(QtCore.QModelIndex)
|
|
||||||
def on_clicked(self, index):
|
|
||||||
if not index.parent().isValid() and index.column() == 0:
|
|
||||||
self.setExpanded(index, not self.isExpanded(index))
|
|
||||||
|
|
||||||
|
|
||||||
class GroupModel(QtGui.QStandardItemModel):
|
|
||||||
def __init__(self, parent=None):
|
|
||||||
super(GroupModel, self).__init__(parent)
|
|
||||||
self.setColumnCount(8)
|
|
||||||
self.setHorizontalHeaderLabels(["", "Name", "Library", "Release Date", "Genre(s)", "Last Played", "Time Played", ""])
|
|
||||||
for i in range(self.columnCount()):
|
|
||||||
it = self.horizontalHeaderItem(i)
|
|
||||||
it.setForeground(QtGui.QColor("#F2F2F2"))
|
|
||||||
|
|
||||||
def add_group(self, group_name):
|
|
||||||
item_root = QtGui.QStandardItem()
|
|
||||||
item_root.setEditable(False)
|
|
||||||
item = QtGui.QStandardItem(group_name)
|
|
||||||
item.setEditable(False)
|
|
||||||
ii = self.invisibleRootItem()
|
|
||||||
i = ii.rowCount()
|
|
||||||
for j, it in enumerate((item_root, item)):
|
|
||||||
ii.setChild(i, j, it)
|
|
||||||
ii.setEditable(False)
|
|
||||||
for j in range(self.columnCount()):
|
|
||||||
it = ii.child(i, j)
|
|
||||||
if it is None:
|
|
||||||
it = QtGui.QStandardItem()
|
|
||||||
ii.setChild(i, j, it)
|
|
||||||
it.setBackground(QtGui.QColor("#002842"))
|
|
||||||
it.setForeground(QtGui.QColor("#F2F2F2"))
|
|
||||||
return item_root
|
|
||||||
|
|
||||||
def append_element_to_group(self, group_item, texts):
|
|
||||||
j = group_item.rowCount()
|
|
||||||
item_icon = QtGui.QStandardItem()
|
|
||||||
item_icon.setEditable(False)
|
|
||||||
item_icon.setIcon(QtGui.QIcon("game.png"))
|
|
||||||
item_icon.setBackground(QtGui.QColor("#0D1225"))
|
|
||||||
group_item.setChild(j, 0, item_icon)
|
|
||||||
for i, text in enumerate(texts):
|
|
||||||
item = QtGui.QStandardItem(text)
|
|
||||||
item.setEditable(False)
|
|
||||||
item.setBackground(QtGui.QColor("#0D1225"))
|
|
||||||
item.setForeground(QtGui.QColor("#F2F2F2"))
|
|
||||||
group_item.setChild(j, i+1, item)
|
|
||||||
|
|
||||||
|
|
||||||
class MainWindow(QtWidgets.QMainWindow):
|
|
||||||
def __init__(self, parent=None):
|
|
||||||
super(MainWindow, self).__init__(parent)
|
|
||||||
|
|
||||||
model = GroupModel(self)
|
|
||||||
tree_view = GroupView(model)
|
|
||||||
self.setCentralWidget(tree_view)
|
|
||||||
|
|
||||||
for group, childrens in datas.items():
|
|
||||||
group_item = model.add_group(group)
|
|
||||||
for children in childrens:
|
|
||||||
model.append_element_to_group(group_item, children)
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
import sys
|
|
||||||
app = QtWidgets.QApplication(sys.argv)
|
|
||||||
w = MainWindow()
|
|
||||||
w.resize(720, 240)
|
|
||||||
w.show()
|
|
||||||
sys.exit(app.exec_())
|
|
||||||
|
|
||||||
Loading…
Reference in New Issue
Block a user