class LoadZeroPointView(QDialog): def __init__(self, zeroPointManager: ZeroPointManager): super().__init__() self.setWindowTitle("Load Zero Point") self._zeroPointManager = zeroPointManager self._listWidget = QListWidget() for zeroPoint in self._zeroPointManager.getZeroPoints(): listItem = QListWidgetItem( f"{zeroPoint.name} : {zeroPoint.offset}") self._listWidget.addItem(listItem) buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) buttonBox.accepted.connect(self.setZeroPoint) buttonBox.rejected.connect(self.reject) layout = QFormLayout() layout.addRow(self._listWidget) layout.addRow(buttonBox) self.setLayout(layout) def setZeroPoint(self): index = self._listWidget.indexFromItem( self._listWidget.selectedItems()[0]).row() self._zeroPointManager.setNewActiveZeroPoint(index) self.close()
class AutocompleteWidget(QDialog): WIDTH = 300 HEIGHT = 120 def __init__(self, suggestions: list): super(AutocompleteWidget, self).__init__() self.widget = QListWidget() self.widget.setStyleSheet("background-color: #232323;") self.setWindowFlags(Qt.FramelessWindowHint | Qt.Window) self.vbox = QVBoxLayout() self.label = QLabel("") self.vbox.addWidget(self.widget, 10) self.vbox.addWidget(self.label, 1) self.setLayout(self.vbox) self.updateSuggestionList(suggestions) self.result = None def updateSuggestionList(self, suggestions): self.widget.clear() if suggestions: for keyword in suggestions: self.widget.addItem(AutoCompleteListWidgetItem(keyword)) self.label.setText("Number of suggestions: {}.".format( len(suggestions))) else: self.label.setText("No available suggestions.") self.setSize() def setSize(self): self.setFixedSize(self.minimumSizeHint()) self.setFixedHeight(AutocompleteWidget.HEIGHT) self.setFixedWidth(AutocompleteWidget.WIDTH) def keyPressEvent(self, e): if e.key() == Qt.Key_Escape: self.close() if e.key() == Qt.Key_Return: selectedItems = self.widget.selectedItems() if len(selectedItems) > 0: self.result = selectedItems[0] self.close() if e.key() == Qt.Key_Backspace: self.close() if e.key() == Qt.Key_Left: self.parent().setFocus()
class ElementPopup(QDialog): def __init__(self): super().__init__() self.setWindowFlags(Qt.CustomizeWindowHint) self.setLayout(QVBoxLayout()) self.resize(300, 300) layout = QHBoxLayout() button_add = QPushButton('Add') button_close = QPushButton('Close') self.list_widget = QListWidget() self.list_widget.addItem("Comment") self.list_widget.addItem("Channel Send") layout.addWidget(button_add) layout.addWidget(button_close) self.layout().addWidget(self.list_widget) self.layout().addLayout(layout) button_add.clicked.connect(lambda: self.addElement()) button_close.clicked.connect(self.close) self.exec_() qtRectangle = self.frameGeometry() centerPoint = QDesktopWidget().availableGeometry().center() qtRectangle.moveCenter(centerPoint) self.move(qtRectangle.center()) def addElement(self): selectedItem = self.list_widget.selectedItems() try: widget.add_element(selectedItem[0].text(), selectedItem[0].text()) except: pass self.close() def mousePressEvent(self, event): self.oldPos = event.globalPos() def mouseMoveEvent(self, event): delta = QPoint(event.globalPos() - self.oldPos) #print(delta) self.move(self.x() + delta.x(), self.y() + delta.y()) self.oldPos = event.globalPos()
class Randomizer(QWidget): """A game randomizer for selecting a random game to play from the user's collection. User can select which platforms to choose from. gamesData: Raw table data in list of dictionaries""" def __init__(self, gamesData: list, platformsData: list, genresData: list): super(Randomizer, self).__init__() self._consoleItems = platformsData self._genreItems = genresData self._gamesData = gamesData self._games = [] # For holding the games to randomize self._gameCount = 0 self._coverdir = path.join("data", "images", "covers") self.consoleLabel = QLabel("Platforms") self.consoleList = QListWidget() self.consoleList.addItems(self._consoleItems) self.consoleList.setSelectionMode(QAbstractItemView.MultiSelection) self.consoleList.setMaximumWidth(350) self.consoleList.itemClicked.connect(self._updateGameCount) self.genreLabel = QLabel("Genres") self.genreMatchExclusiveCB = QCheckBox("Match exclusive") self.genreMatchExclusiveCB.setToolTip( "Only match games which exclusively contain the selected genres.") self.genreMatchExclusiveCB.setChecked(False) self.genreMatchExclusiveCB.stateChanged.connect(self._updateGameCount) self.genreList = QListWidget() self.genreList.addItems(self._genreItems) self.genreList.setSelectionMode(QAbstractItemView.MultiSelection) self.genreList.setMaximumWidth(350) self.genreList.itemClicked.connect(self._updateGameCount) self.btnAll = QPushButton("Select All") self.btnAll.setMaximumSize(self.btnAll.sizeHint()) self.btnAll.clicked.connect(self.consoleList.selectAll) self.btnAll.clicked.connect(self.genreList.selectAll) self.btnAll.clicked.connect(self._updateGameCount) self.btnNone = QPushButton("Select None") self.btnNone.setMaximumSize(self.btnNone.sizeHint()) self.btnNone.clicked.connect(self.consoleList.clearSelection) self.btnNone.clicked.connect(self.genreList.clearSelection) self.btnNone.clicked.connect(self._updateGameCount) self._btnRnd = QPushButton("Randomize") self._btnRnd.setMaximumSize(self._btnRnd.sizeHint()) self._btnRnd.clicked.connect(self._randomize) self._lblFont = QFont() self._lblFont.setPointSize(14) self._lblFont.setBold(True) self._lblPlay = QLabel() self._lblPlay.setAlignment(Qt.AlignCenter) self._lblPlay.setFont(self._lblFont) self._lblTitle = QLabel() self._lblTitle.setAlignment(Qt.AlignCenter) self._lblTitle.setFont(self._lblFont) self._lblTitle.setWordWrap(True) # Cover image self._cover = QLabel() self._cover.setVisible(False) self._cover.setAlignment(Qt.AlignCenter) p = QPixmap(path.join(self._coverdir, "none.png")) w = self._cover.width() h = self._cover.height() self._cover.setPixmap( p.scaled(w, h, Qt.KeepAspectRatio, Qt.SmoothTransformation)) self._hboxButtons = QHBoxLayout() self._vboxLists = QVBoxLayout() self._vboxConsoles = QVBoxLayout() self._hboxGenres = QHBoxLayout() self._vboxGenres = QVBoxLayout() self._vboxResult = QVBoxLayout() self._grid = QGridLayout() self._hboxButtons.addWidget(self.btnAll, 0) self._hboxButtons.addWidget(self.btnNone, 0) self._hboxButtons.addWidget(self._btnRnd, 0) self._vboxConsoles.addWidget(self.consoleLabel, 0) self._vboxConsoles.addWidget(self.consoleList, 1) self._hboxGenres.addWidget(self.genreLabel, 0) self._hboxGenres.addWidget(self.genreMatchExclusiveCB, 0) self._vboxGenres.addWidget(self.genreList, 1) self._vboxLists.addSpacing(10) self._vboxLists.addLayout(self._vboxConsoles, 1) self._vboxLists.addSpacing(10) self._vboxLists.addLayout(self._hboxGenres, 0) self._vboxLists.addLayout(self._vboxGenres, 1) self._vboxResult.addStretch(3) self._vboxResult.addWidget(self._lblPlay, 0) self._vboxResult.addWidget(self._lblTitle, 0) self._vboxResult.addSpacing(50) self._vboxResult.addWidget(self._cover, 0) self._vboxResult.addStretch(3) self._grid.setMargin(0) self._grid.setSpacing(0) self._grid.addLayout(self._vboxLists, 0, 0) self._grid.addLayout(self._hboxButtons, 1, 0) self._grid.addLayout(self._vboxResult, 0, 1, 1, -1) self.widget = QWidget() self.widget.setLayout(self._grid) def _getSelectedItems(self) -> tuple: return [x.text() for x in self.consoleList.selectedItems() ], [x.text() for x in self.genreList.selectedItems()] def _randomize(self): platforms, genres = self._getSelectedItems() if len(self._games) > 0 and (len(platforms) > 0 or len(genres) > 0): choice = randint(0, len(self._games) - 1) self._lblPlay.setText("You will play:") self._lblTitle.setText( f"{self._games[choice]['name']}" if len(platforms) == 1 else f"{self._games[choice]['name']} [{self._games[choice]['platform']}]" ) # Cover image cover = str(self._games[choice]['id']) + ".jpg" if path.exists(path.join(self._coverdir, cover)): # Update cover image if the game has one pixmap = path.join(self._coverdir, cover) self._cover.setVisible(True) else: pixmap = path.join(self._coverdir, "none.png") self._cover.setVisible(False) p = QPixmap(pixmap) w = self._cover.width() h = self._cover.height() self._cover.setPixmap( p.scaled(w, h, Qt.KeepAspectRatio, Qt.SmoothTransformation)) elif len(self._games) == 0 and (len(platforms) > 0 or len(genres) > 0): self._lblPlay.setText("") self._lblTitle.setText("No games found with those criteria.") self._cover.setVisible(False) else: self._lblPlay.setText("") self._lblTitle.setText("Select at least one console or genre...") self._cover.setVisible(False) def _updateGameCount(self): platforms, genres = self._getSelectedItems() self._gameCount = 0 self._games = [] if len(platforms) > 0 or len(genres) > 0: for row in self._gamesData: if len(platforms) > 0 and len(genres) > 0: if row["platform"] in platforms: if self.genreMatchExclusiveCB.isChecked(): count = 0 for genre in row["genre"].split(", "): if genre in genres: count += 1 else: # Not exclusive count = 0 break if count == len(genres): self._gameCount += 1 self._games.append(row) else: for genre in row["genre"].split(", "): if genre in genres: self._gameCount += 1 self._games.append(row) break # We only need to match with one genre elif len(platforms) > 0 and len(genres) == 0: if row["platform"] in platforms: self._gameCount += 1 self._games.append(row) elif len(platforms) == 0 and len(genres) > 0: if self.genreMatchExclusiveCB.isChecked(): count = 0 for genre in row["genre"].split(", "): if genre in genres: count += 1 else: # Not exclusive count = 0 break if count == len(genres): self._gameCount += 1 self._games.append(row) else: for genre in row["genre"].split(", "): if genre in genres: self._gameCount += 1 self._games.append(row) break # We only need to match with one genre def gameCount(self) -> int: return self._gameCount def updateLists(self, gamesData: list, platformsData: list, genresData: list): self._gamesData = gamesData self._consoleItems = platformsData self._genreItems = genresData self.consoleList.clear() self.genreList.clear() self.consoleList.addItems(self._consoleItems) self.genreList.addItems(self._genreItems)
class MainWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle('清单') self.setFixedSize(320, 480) # 布局 layout = QVBoxLayout() # 清单视图 self.items_view = QListWidget() self.items_view.setIconSize(QSize(14, 14)) self.items_view.doubleClicked.connect(self.toggle_complete) layout.addWidget(self.items_view) # 按钮布局 button_layout = QHBoxLayout() layout.addLayout(button_layout) # delete button self.delete_button = QPushButton('DELETE') self.delete_button.clicked.connect(self.delete) button_layout.addWidget(self.delete_button) # Complete button self.complete_button = QPushButton('COMPLETE') self.complete_button.clicked.connect(self.complete) button_layout.addWidget(self.complete_button) # add input self.add_input = QLineEdit() self.add_input.returnPressed.connect(self.add) layout.addWidget(self.add_input) # add button self.add_button = QPushButton('ADD') self.add_button.clicked.connect(self.add) layout.addWidget(self.add_button) # status bar self.setStatusBar(QStatusBar()) container = QWidget() container.setLayout(layout) self.setCentralWidget(container) self.setStyleSheet(""" QListWidget { border: 1px solid #999; font-size: 13px; } QListWidget::item { color: #000; height: 30px; border-bottom: 1px solid #dedede; } QListWidget::item:selected { background-color: #fff9dd; } QPushButton { height: 24px; background-color: #fddb3a; font-weight: 900; } QLineEdit { padding: 5px; } """) self.items = [] self.load() self.list_items() @Slot() def add(self): name = self.add_input.text() if name: self.items_view.addItem(QListWidgetItem(name)) self.add_input.setText('') self.items.append({'name': name, 'done': False}) # self.save() db.save(name, False) @Slot() def delete(self): items = self.items_view.selectedItems() if items: self.items.pop(self.items_view.currentRow()) db.delete(self.items_view.currentRow() + 1) self.items_view.clear() self.list_items() # self.save() @Slot() def complete(self): items = self.items_view.selectedItems() if items: item_data = self.items[self.items_view.currentRow()] if not item_data['done']: icon = QIcon('done.svg') items[0].setIcon(icon) item_data['done'] = True # self.save() db.update(True, self.items_view.currentRow() + 1) @Slot() def toggle_complete(self): items = self.items_view.selectedItems() if items: item_data = self.items[self.items_view.currentRow()] if not item_data['done']: icon = QIcon('done.svg') items[0].setIcon(icon) item_data['done'] = True db.update(True, self.items_view.currentRow() + 1) else: icon = QIcon('') items[0].setIcon(icon) item_data['done'] = False db.update(False, self.items_view.currentRow() + 1) # self.save() def list_items(self): for item in self.items: list_item = QListWidgetItem(item['name']) if item['done']: icon = QIcon('done.svg') list_item.setIcon(icon) self.items_view.addItem(list_item) def load(self): # with open('data.json', 'r') as f: # self.items = json.load(f) self.items = db.select() def save(self): with open('data.json', 'w') as f: json.dump(self.items, f)
class MyWindow(QWidget): def __init__(self, parent=None): super().__init__(parent) self.setWindowTitle( 'Graphical utility for destroying, zeroing, and deleting files') self.label_donate = QLabel( 'Copyright (c) 2021, Aleksandr Suvorov | Donate: 4048 0250 0089 5923' ) self.label_donate.setAlignment(Qt.AlignCenter) self.label_logo = QLabel(f'Smart Cleaner<sup> {VERSION}</sup>') self.label_logo.setAlignment(Qt.AlignCenter) self.label_logo.setStyleSheet('font-size: 48px;') self.label_files = QLabel('Files') self.label_files.setStyleSheet("color: rgb(84, 180, 40);") self.label_dirs = QLabel('Folders') self.label_dirs.setStyleSheet("color: rgb(177, 98, 42);") self.label_errors = QLabel('Errors') self.label_errors.setStyleSheet("color: rgb(255, 68, 44);") self.lcd_files = QLCDNumber() self.lcd_files.setSegmentStyle(QLCDNumber.Flat) self.lcd_files.setStyleSheet("color: rgb(84, 180, 40);") self.lcd_dirs = QLCDNumber() self.lcd_dirs.setSegmentStyle(QLCDNumber.Flat) self.lcd_dirs.setStyleSheet("color: rgb(177, 98, 42);") self.lcd_errors = QLCDNumber() self.lcd_errors.setSegmentStyle(QLCDNumber.Flat) self.lcd_errors.setMinimumHeight(30) self.lcd_errors.setStyleSheet("color: rgb(255, 68, 44);") self.lcd_files.setDigitCount(15) self.lcd_dirs.setDigitCount(15) self.lcd_errors.setDigitCount(15) self.h_box1 = QHBoxLayout() self.h_box1.addWidget(self.label_dirs) self.h_box1.addWidget(self.label_files) self.h_box1.addWidget(self.label_errors) self.h_box2 = QHBoxLayout() self.h_box2.addWidget(self.lcd_dirs) self.h_box2.addWidget(self.lcd_files) self.h_box2.addWidget(self.lcd_errors) self.label_cons = QLabel('Information console:') self.text_browser = QTextBrowser() self.text_browser.setText( f'Smart Cleaner v{VERSION} \nUtility for overwriting, zeroing, and deleting files\n' f'https://github.com/mysmarthub') self.btn_console_clear = QPushButton('Reset') self.btn_donate = QPushButton('Donate | Visa: 4048 0250 0089 5923') self.btn_donate.setToolTip( 'We will be grateful for any financial support.\nThis will help the program ' 'develop and remain free.\nThanks!') self.btn_exit = QPushButton('Exit') self.h_box3 = QHBoxLayout() self.h_box3.addWidget(self.btn_donate) self.h_box3.addStretch(1) self.h_box3.addWidget(self.btn_console_clear) self.chb_del_dirs = QCheckBox('Delete folders') self.chb_del_dirs.setChecked(True) self.label_shred = QLabel('Rewrite:') self.spin_box = QSpinBox() self.spin_box.setMinimum(1) self.spin_box.setMaximum(1000) self.spin_box.setValue(30) self.h_box4 = QHBoxLayout() self.h_box4.addWidget(self.chb_del_dirs) self.h_box4.addWidget(self.label_shred) self.h_box4.addWidget(self.spin_box) self.h_box4.addStretch(1) self.list_widget = QListWidget() self.list_widget.setSelectionMode(QAbstractItemView.ExtendedSelection) self.btn_add_folder = QPushButton('+ Folder') self.btn_add_files = QPushButton('+ Files') self.btn_remove_item = QPushButton('- Remove') self.btn_zero_files = QPushButton('Zeroing') self.btn_shred_files = QPushButton('Erasing') self.btn_del_files = QPushButton('Delete') self.h_box5 = QHBoxLayout() self.h_box5.addWidget(self.btn_add_folder) self.h_box5.addWidget(self.btn_add_files) self.h_box5.addWidget(self.btn_remove_item) self.h_box5.addStretch(1) self.h_box5.addWidget(self.btn_shred_files) self.h_box5.addWidget(self.btn_zero_files) self.h_box5.addWidget(self.btn_del_files) self.h_box5.addWidget(self.btn_exit) self.v_box = QVBoxLayout() self.v_box.addWidget(self.label_logo) self.v_box.addLayout(self.h_box1) self.v_box.addLayout(self.h_box2) self.v_box.addWidget(self.label_cons) self.v_box.addWidget(self.text_browser) self.v_box.addLayout(self.h_box3) self.v_box.addLayout(self.h_box4) self.v_box.addWidget(self.list_widget) self.v_box.addLayout(self.h_box5) self.v_box.addWidget(self.label_donate) self.setLayout(self.v_box) self.smart_cleaner = SmartCleaner() self.btn_donate.clicked.connect( lambda: webbrowser.open('https://yoomoney.ru/to/4100115206129186')) self.btn_console_clear.clicked.connect(self.clear_console) self.btn_add_folder.clicked.connect(self.add_dir) self.btn_add_files.clicked.connect(self.add_files) self.btn_remove_item.clicked.connect(self.remove_items) self.btn_shred_files.clicked.connect(self.shred_start) self.btn_zero_files.clicked.connect(self.zeroing_start) self.btn_del_files.clicked.connect(self.delete_start) self.btn_exit.clicked.connect(self.close) self.smart_cleaner.signal.connect(self.update_information) self.smart_cleaner.started.connect(self.at_start) self.smart_cleaner.finished.connect(self.at_finish) def clear_console(self): self.lcd_dirs.display(0) self.lcd_files.display(0) self.lcd_errors.display(0) self.text_browser.setText( f'Smart Cleaner v{VERSION} \nUtility for overwriting, zeroing, and deleting files\n' f'https://github.com/mysmarthub') def add_dir(self) -> None: path = QFileDialog.getExistingDirectory(self, 'Select the folder to add: ') self._add_path(path) def add_files(self) -> None: path_tuple = QFileDialog.getOpenFileNames(self, 'Select files to add: ') for path in path_tuple[0]: self._add_path(path) def add_item(self, item: str) -> None: self.list_widget.addItem(item) def remove_items(self) -> None: for SelectedItem in self.list_widget.selectedItems(): self.list_widget.takeItem(self.list_widget.row(SelectedItem)) self.smart_cleaner.path_data.del_path(SelectedItem.text()) self.text_browser.append( f'{SelectedItem.text()}\nThe path was successfully deleted!!!') def _add_path(self, path: str) -> None: if path: if self.smart_cleaner.path_data.add_path(path): self.add_item(path) self.text_browser.append( f'{path}\nThe path was added successfully!') else: self.text_browser.append( f'Error when adding or the path was added earlier!!!') def shred_start(self): self.start(method='shred') def zeroing_start(self): self.start(method='zero') def delete_start(self): self.start(method='del') def start(self, method='shred'): if not self.smart_cleaner.path_data.is_any_data: self.show_msg('Warning!', 'There is no data for mashing!!!') else: reply = QMessageBox.question( self, 'Warning!', 'The data will be destroyed, are you sure?', QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.Yes: if method == 'zero': self.text_browser.append('Files are reset to zero.') elif method == 'shred': self.text_browser.append('File mashing is started.') elif method == 'del': self.text_browser.append('File deletion started.') self.smart_cleaner.work_method = method self.smart_cleaner.shreds = self.spin_box.value() self.smart_cleaner.delete_a_folder = self.chb_del_dirs.isChecked( ) self.smart_cleaner.start() def update_information(self, s: str) -> None: self.text_browser.append(s) self.update_lcd() def update_lcd(self) -> None: self.lcd_dirs.display(str(self.smart_cleaner.cleaner.count_del_dirs)) self.lcd_files.display(str(self.smart_cleaner.cleaner.count_del_files)) self.lcd_errors.display(str(self.smart_cleaner.num_errors)) def at_start(self): self.from_disable(True) def at_finish(self) -> None: if self.smart_cleaner.work_method != 'zero': self.list_widget.clear() self.update_lcd() self.from_disable(False) self.finish_msg() def finish_msg(self) -> None: if self.smart_cleaner.work_method == 'zero': msg = ('Reset', 'Reset files: ') count = self.smart_cleaner.cleaner.count_zero_files elif self.smart_cleaner.work_method == 'shred': msg = ('Mashing', 'Passageways: ') count = self.smart_cleaner.cleaner.count_del_files else: msg = ('Delete', 'Deleted files: ') count = self.smart_cleaner.cleaner.count_del_files self.show_msg( 'Warning!', f'{msg[0]} completed successfully!!!\n' f' {msg[1]} {count}\n ' f'Deleted folders: {self.smart_cleaner.cleaner.count_del_dirs}\n ' f'Errors: {self.smart_cleaner.num_errors}') def from_disable(self, status: bool) -> None: self.btn_zero_files.setDisabled(status) self.btn_remove_item.setDisabled(status) self.btn_add_folder.setDisabled(status) self.btn_shred_files.setDisabled(status) self.btn_console_clear.setDisabled(status) self.btn_add_files.setDisabled(status) self.btn_del_files.setDisabled(status) self.chb_del_dirs.setDisabled(status) self.spin_box.setDisabled(status) self.list_widget.setDisabled(status) def show_msg(self, title: str = 'Warning!', msg: str = 'Message...') -> None: QMessageBox.about(self, title, msg) def closeEvent(self, event) -> None: reply = QMessageBox.question( self, 'Exit', 'Are you sure you want to terminate the program?', QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.Yes: self.hide() self.smart_cleaner.wait(3000) event.accept() else: event.ignore()
class EditCommentBankWindow(QMainWindow): def __init__(self, subject_name): QMainWindow.__init__(self) self.setWindowTitle("Edit Comment Bank: {} - {} {}".format( subject_name, config.APP_NAME, config.APP_VERSION)) self.setMinimumWidth(1200) self.setStyleSheet(config.STYLESHEET) self.subject = could_try_harder.load(subject_name) self.saved_list = could_try_harder.get_saved_list() # Widgets self.intro_comment_label = QLabel("Introductory Comment:") self.intro_comment_label.setProperty("styleClass", "heading") self.intro_comment_textedit = QTextEdit() self.comment_bank_label = QLabel("Comment Bank") self.comment_bank_label.setProperty("styleClass", "heading") self.comment_bank_listwidget = QListWidget() self.placeholder_instructions_label = QLabel( config.PLACEHOLDER_INSTRUCTIONS) self.add_comment_label = QLabel("Add Comment:") self.add_comment_entry = QLineEdit() self.add_comment_button = QPushButton("Add") self.update_comment_label = QLabel("Update Comment:") self.update_comment_entry = QLineEdit() self.update_comment_button = QPushButton("Update") self.delete_comment_button = QPushButton("Delete Comment") self.import_comments_combo = QComboBox() self.import_comments_button = QPushButton("Import...") self.cancel_button = QPushButton("Cancel") self.save_button = QPushButton("Save") # Layout self.layout = QVBoxLayout() self.top_layout = QHBoxLayout() self.intro_comment_layout = QVBoxLayout() self.intro_comment_layout.addWidget(self.intro_comment_label) self.intro_comment_layout.addWidget(self.intro_comment_textedit) self.top_layout.addLayout(self.intro_comment_layout) self.top_layout.addWidget(self.placeholder_instructions_label) self.layout.addLayout(self.top_layout) self.middle_layout = QVBoxLayout() self.middle_layout.addWidget(self.comment_bank_label) self.middle_layout.addWidget(self.comment_bank_listwidget) self.comment_actions_layout = QHBoxLayout() self.comment_actions_layout.addWidget(self.delete_comment_button, 0, Qt.AlignLeft) self.comment_actions_layout.addWidget(self.import_comments_combo, 1, Qt.AlignRight) self.comment_actions_layout.addWidget(self.import_comments_button, 0, Qt.AlignRight) self.middle_layout.addLayout(self.comment_actions_layout) self.update_comment_layout = QGridLayout() self.update_comment_layout.addWidget(self.update_comment_label, 0, 0) self.update_comment_layout.addWidget(self.update_comment_entry, 0, 1) self.update_comment_layout.addWidget(self.update_comment_button, 0, 2) self.update_comment_layout.addWidget(self.add_comment_label, 1, 0) self.update_comment_layout.addWidget(self.add_comment_entry, 1, 1) self.update_comment_layout.addWidget(self.add_comment_button, 1, 2) self.middle_layout.addLayout(self.update_comment_layout) self.layout.addLayout(self.middle_layout) self.bottom_layout = QHBoxLayout() self.bottom_layout.addWidget(self.cancel_button, 0, Qt.AlignLeft) self.bottom_layout.addWidget(self.save_button, 0, Qt.AlignRight) self.layout.addLayout(self.bottom_layout) # Slot connections self.comment_bank_listwidget.itemSelectionChanged.connect( self.do_update_comment_bank_selection) self.import_comments_button.clicked.connect(self.do_import_comments) self.update_comment_button.clicked.connect(self.do_update_comment) self.update_comment_entry.returnPressed.connect(self.do_update_comment) self.add_comment_button.clicked.connect(self.do_add_comment) self.add_comment_entry.returnPressed.connect(self.do_add_comment) self.delete_comment_button.clicked.connect(self.do_delete_comment) self.cancel_button.clicked.connect(self.do_cancel) self.save_button.clicked.connect(self.do_save) # Initial UI update self.update_ui() self.widget = QWidget() self.widget.setLayout(self.layout) self.setCentralWidget(self.widget) def update_ui(self): self.update_import_comments_list() self.update_intro_comment() self.update_comment_bank() def update_import_comments_list(self): self.import_comments_combo.clear() self.import_comments_combo.insertItems(0, self.saved_list) def update_intro_comment(self): self.intro_comment_textedit.clear() self.intro_comment_textedit.insertPlainText( self.subject['intro_comment']) def update_comment_bank(self): self.comment_bank_listwidget.clear() self.comment_bank_listwidget.addItems(self.subject['comment_bank']) self.do_update_comment_bank_selection() @Slot() def do_import_comments(self): # TODO confirm dialog first confirm_msg = QMessageBox(self) confirm_msg.setWindowTitle("Confirm") confirm_msg.setText("This will override current comments.") confirm_msg.setInformativeText("Do you want to continue?") confirm_msg.setStandardButtons(QMessageBox.No | QMessageBox.Yes) confirm_msg.setDefaultButton(QMessageBox.Yes) confirm = confirm_msg.exec() if confirm == QMessageBox.Yes: if self.import_comments_combo.count() > 0: new_subject = could_try_harder.load( self.import_comments_combo.currentText()) if new_subject: self.subject['intro_comment'] = new_subject[ 'intro_comment'] self.subject['comment_bank'] = new_subject['comment_bank'] self.update_ui() else: # TODO better error handling here print('Tried to import empty subject.') return return @Slot() def do_update_comment_bank_selection(self): if self.comment_bank_listwidget.selectedItems(): state = True else: state = False self.delete_comment_button.setEnabled(state) self.update_comment_button.setEnabled(state) # Update the text in the update comment line edit self.update_comment_entry.clear() if self.comment_bank_listwidget.currentItem(): self.update_comment_entry.insert( self.comment_bank_listwidget.currentItem().text()) @Slot() def do_update_comment(self): if self.update_comment_entry.text(): self.comment_bank_listwidget.currentItem().setText( could_try_harder.do_style( self.update_comment_entry.text().strip())) self.do_update_comment_bank_selection() @Slot() def do_add_comment(self): if self.add_comment_entry.text(): self.comment_bank_listwidget.addItem( could_try_harder.do_style( self.add_comment_entry.text().strip())) self.add_comment_entry.clear() self.do_update_comment_bank_selection() @Slot() def do_delete_comment(self): self.comment_bank_listwidget.takeItem( self.comment_bank_listwidget.currentRow()) self.do_update_comment_bank_selection() @Slot() def do_cancel(self): self.close() @Slot() def do_save(self): self.subject['intro_comment'] = could_try_harder.do_style( self.intro_comment_textedit.toPlainText().strip()) self.subject['comment_bank'] = [] for i in range(self.comment_bank_listwidget.count()): self.subject['comment_bank'].append( self.comment_bank_listwidget.item(i).text()) if could_try_harder.save(self.subject): self.close() else: # TODO better error handling here print("Save failed.")
class SelectPlayersWindow(QDialog): def __init__(self, parent=None): super(SelectPlayersWindow, self).__init__(parent) self.parent = parent self.setModal(True) self.setWindowTitle("Torna résztvevők") self.resize(520, 600) self.layout = QVBoxLayout() self.setLayout(self.layout) self.read_config() self.create_torna_selection() self.nevek_layout = QHBoxLayout() self.layout.addLayout(self.nevek_layout) self.show_saved_players() self.show_torna_players() self.gomb_nev_layout = QVBoxLayout() self.nevek_layout.addLayout(self.gomb_nev_layout) self.show_current_players() self.buttonbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.buttonbox.clicked.connect(self.buttonbox_click) self.layout.addWidget(self.buttonbox) def create_torna_selection(self): self.tournaments = QComboBox() self.tournaments.setModelColumn(0) self.tournaments.currentIndexChanged.connect(self.torna_valasztas) self.layout.addWidget(self.tournaments) self.load_torna() def load_torna(self): torna = QSqlQueryModel() query = QSqlQuery("select * from torna_settings where aktiv=2") torna.setQuery(query) if torna.record(0).value(0): for i in range(torna.rowCount()): self.tournaments.addItem( torna.record(i).value(1), torna.record(i).value(0)) # a value(0) a torna_id else: print("Nincs aktív torna") def show_saved_players(self): self.saved_players = QListWidget() self.saved_players.setFixedHeight(500) self.saved_players.setFixedWidth(150) self.saved_players.setSortingEnabled(True) self.saved_players.itemDoubleClicked.connect(self.add_resztvevo) self.load_saved_players() self.nevek_layout.addWidget(self.saved_players) def show_torna_players(self): self.torna_players = QListWidget() self.torna_players.setFixedHeight(500) self.torna_players.setFixedWidth(150) self.torna_players.setSortingEnabled(True) self.torna_players.itemDoubleClicked.connect(self.add_resztvevo) self.load_torna_players() self.nevek_layout.addWidget(self.torna_players) def load_saved_players(self): players = QSqlQueryModel() players_query = QSqlQuery("select * from players where aktiv=1") players.setQuery(players_query) self.saved_players.clear() for i in range(players.rowCount()): item = QListWidgetItem(players.record(i).value(1)) item.setData(Qt.UserRole, players.record(i).value(0)) self.saved_players.addItem(item) def load_torna_players(self): players = QSqlQueryModel() players_query = QSqlQuery( "select * from torna_resztvevok where 1 group by player_id, player_name" ) players.setQuery(players_query) self.torna_players.clear() for i in range(players.rowCount()): item = QListWidgetItem(players.record(i).value(1)) item.setData(Qt.UserRole, players.record(i).value(0)) self.torna_players.addItem(item) def add_resztvevo(self, item): new_item = QListWidgetItem(item) new_item.setData(Qt.UserRole, item.data(Qt.UserRole)) self.current_players.addItem(new_item) query = QSqlQuery( f"insert into torna_resztvevok (player_id, player_name, torna_id) values ({new_item.data(Qt.UserRole)}, '{new_item.text()}', {self.torna_id})" ) query.exec_() def show_current_players(self): query = QSqlQuery("select max(player_id) from torna_resztvevok") query.exec_() while query.next(): self.first_new_id = int(query.value(0)) + 1 print(self.first_new_id) self.add_new = QPushButton("Új") self.add_new.clicked.connect(self.uj_ember) self.current_players = QListWidget() self.current_players.setFixedHeight(470) self.current_players.setFixedWidth(150) self.current_players.setSortingEnabled(True) self.gomb_nev_layout.addWidget(self.add_new) self.current_players.itemDoubleClicked.connect(self.remove_resztvevo) self.gomb_nev_layout.addWidget(self.current_players) def uj_ember(self): ujember, ok = QInputDialog.getText( self, "Új versenyző", '<html style="font-size: 15px;">Írd be a versenyző nevét!</html>') if ok and len(ujember): item = QListWidgetItem(ujember) item.setData(Qt.UserRole, self.first_new_id) self.current_players.addItem(item) self.first_new_id += 1 query = QSqlQuery( f"insert into torna_resztvevok (player_id, player_name, torna_id) values ({item.data(Qt.UserRole)}, '{item.text()}', {self.torna_id})" ) query.exec_() def remove_resztvevo(self, item): self.current_players.takeItem( self.current_players.row(self.current_players.selectedItems()[0])) # print(item.data(Qt.UserRole), item.text()) query = QSqlQuery( f"delete from torna_resztvevok where player_id={item.data(Qt.UserRole)} and torna_id={self.torna_id}" ) query.exec_() def torna_valasztas(self, i): self.torna_id = self.tournaments.itemData(i) players = QSqlQueryModel() players_query = QSqlQuery( f"select * from torna_resztvevok where torna_id={self.torna_id}") players.setQuery(players_query) self.current_players.clear() for i in range(players.rowCount()): item = QListWidgetItem(players.record(i).value(1)) item.setData(Qt.UserRole, players.record(i).value(0)) self.current_players.addItem(item) def buttonbox_click(self, b): if b.text() == "OK": self.accept() elif b.text() == "Cancel": self.reject() def accept(self): # for i in range(self.current_players.count()): # item = self.current_players.item(i) # print(self.torna_id, item.data(Qt.UserRole), item.text()) # itt vannak a beszúrandó adatok super().accept() # INSERT INTO `torna_resztvevok` (`player_id`, `player_name`, `torna_id`) # VALUES ('1111', 'teszt_user2', '8892') ON DUPLICATE KEY UPDATE player_name='teszt_user2'; def read_config(self): if os.path.exists('config.ini'): # Van config.ini, ki kell értékelni config.read('config.ini') self.station_id = config['DEFAULT'].get('station id') self.secret = config['DEFAULT'].get('secret key') # todo módosítani kell a torna_match táblát, hogy tartalmazza a tabla mellett a hozzá tartozó secret-et is else: # Nincs config.ini, alapértékekkel inicializálni msg = QMessageBox(self) msg.setStyleSheet("fonz-size: 20px") msg.setWindowTitle("Hiányzó beállítás file!") msg.setText( '<html style="font-size: 14px; color: red">Nem tudtam beolvasni a konfigurációt!<br></html>' + '<html style="font-size: 16px">Kérem módosítsa a beállításokat!</html>' ) msg.exec_() sys.exit(1)
class FileSystemWidget(QWidget, DirectoryObserver): """ Widget for listing directory contents and download files from the RDP client. """ # fileDownloadRequested(file, targetPath, dialog) fileDownloadRequested = Signal(File, str, FileDownloadDialog) def __init__(self, root: Directory, parent: QObject = None): """ :param root: root of all directories. Directories in root will be displayed with drive icons. :param parent: parent object. """ super().__init__(parent) self.root = root self.breadcrumbLabel = QLabel() self.titleLabel = QLabel() self.titleLabel.setStyleSheet("font-weight: bold") self.titleSeparator: QFrame = QFrame() self.titleSeparator.setFrameShape(QFrame.HLine) self.listWidget = QListWidget() self.listWidget.setSortingEnabled(True) self.listWidget.setContextMenuPolicy(Qt.CustomContextMenu) self.listWidget.customContextMenuRequested.connect(self.onCustomContextMenu) self.verticalLayout = QVBoxLayout() self.verticalLayout.addWidget(self.breadcrumbLabel) self.verticalLayout.addWidget(self.listWidget) self.setLayout(self.verticalLayout) self.listWidget.itemDoubleClicked.connect(self.onItemDoubleClicked) self.currentPath: Path = Path("/") self.currentDirectory: Directory = root self.listCurrentDirectory() self.currentDirectory.addObserver(self) def setWindowTitle(self, title: str): """ Set the window title. When the title is not blank, a title label and a separator is displayed. :param title: the new title. """ previousTitle = self.windowTitle() super().setWindowTitle(title) self.titleLabel.setText(title) if previousTitle == "" and title != "": self.verticalLayout.insertWidget(0, self.titleLabel) self.verticalLayout.insertWidget(1, self.titleSeparator) elif title == "" and previousTitle != "": self.verticalLayout.removeWidget(self.titleLabel) self.verticalLayout.removeWidget(self.titleSeparator) # noinspection PyTypeChecker self.titleLabel.setParent(None) # noinspection PyTypeChecker self.titleSeparator.setParent(None) def onItemDoubleClicked(self, item: FileSystemItem): """ Handle double-clicks on items in the list. When the item is a directory, the current path changes and the contents of the directory are listed. Files are ignored. :param item: the item that was clicked. """ if not item.isDirectory() and not item.isDrive(): return if item.text() == "..": self.currentPath = self.currentPath.parent else: self.currentPath = self.currentPath / item.text() self.listCurrentDirectory() def listCurrentDirectory(self): """ Refresh the list widget with the current directory's contents. """ node = self.root for part in self.currentPath.parts[1 :]: node = next(d for d in node.directories if d.name == part) self.listWidget.clear() self.breadcrumbLabel.setText(f"Location: {str(self.currentPath)}") if node != self.root: self.listWidget.addItem(FileSystemItem("..", FileSystemItemType.Directory)) for directory in node.directories: self.listWidget.addItem(FileSystemItem(directory.name, directory.type)) for file in node.files: self.listWidget.addItem(FileSystemItem(file.name, file.type)) if node is not self.currentDirectory: self.currentDirectory.removeObserver(self) node.addObserver(self) self.currentDirectory = node node.list() def onDirectoryChanged(self): """ Refresh the directory view when the directory has changed. """ self.listCurrentDirectory() def currentItemText(self) -> str: try: return self.listWidget.selectedItems()[0].text() except IndexError: return "" def selectedFile(self) -> Optional[File]: text = self.currentItemText() if text == "": return None if text == "..": return self.currentDirectory.parent for sequence in [self.currentDirectory.files, self.currentDirectory.directories]: for file in sequence: if text == file.name: return file return None def canDownloadSelectedItem(self) -> bool: return self.selectedFile().type == FileSystemItemType.File def onCustomContextMenu(self, localPosition: QPoint): """ Show a custom context menu with a "Download file" action when a file is right-clicked. :param localPosition: position where the user clicked. """ selectedFile = self.selectedFile() if selectedFile is None: return globalPosition = self.listWidget.mapToGlobal(localPosition) downloadAction = QAction("Download file") downloadAction.setEnabled(selectedFile.type in [FileSystemItemType.File]) downloadAction.triggered.connect(self.downloadFile) itemMenu = QMenu() itemMenu.addAction(downloadAction) itemMenu.exec_(globalPosition) def downloadFile(self): file = self.selectedFile() if file.type != FileSystemItemType.File: return filePath = file.getFullPath() targetPath, _ = QFileDialog.getSaveFileName(self, f"Download file {filePath}", file.name) if targetPath != "": dialog = FileDownloadDialog(filePath, targetPath, self) dialog.show() self.fileDownloadRequested.emit(file, targetPath, dialog)
class DirectorySetChooseDialog(QDialog): """ simple dialog to let user choose from the available directory sets """ def __init__(self, parent, set_pieces_and_playlist_function): """ pretty standard constructor: set up class variables, ui elements and layout parameters: - parent: parent widget of this dialog - set_pieces_and_playlist_function: is called to set the pieces and playlist variables of the parent """ super(DirectorySetChooseDialog, self).__init__(parent) # -- create and setup ui elements -- self._lbl_prompt = QLabel( 'Please choose one or more from the available directory sets:') self._lbl_prompt.setMaximumHeight(30) self._listwidget_sets = QListWidget() self._checkbox_shuffle = QCheckBox('Shuffle playlist') self._checkbox_shuffle.setChecked(True) # shuffling enabled by default self._listwidget_sets.itemDoubleClicked.connect(self.__action_choose) self._btn_choose = QPushButton('Choose') self._btn_choose.clicked.connect(self.__action_choose) # -- setup _listwidget_sets options = listdir('../directories') # get list of available sets options.remove('Default.txt') # remove default entry # and reinsert at index 0 to ensure that 'Default' is the first option options.insert(0, 'Default.txt') self._listwidget_sets.addItems([s[:-4] for s in options]) self._listwidget_sets.setSelectionMode( # allow multiple selections QAbstractItemView.ExtendedSelection) self._listwidget_sets.setCurrentRow(0) # select default entry # -- create layout -- self._layout = QGridLayout(self) self._layout.addWidget(self._lbl_prompt, 0, 0, 1, -1) self._layout.addWidget(self._listwidget_sets, 1, 0, 4, -1) self._layout.addWidget(self._checkbox_shuffle, 5, 0, 1, -1) self._layout.addWidget(self._btn_choose, 6, 0, 1, -1) # -- various setup -- self._set_pieces_and_playlist = set_pieces_and_playlist_function self.setModal(True) self.setWindowTitle('Please choose a directory set') self.setMinimumWidth(600) self.setMinimumHeight(400) def __action_choose(self): """ (gets called when self._btn_choose is clicked) loads pieces from selected sets, creates a playlist (shuffled if wanted) and calls self._set_pieces_and_playlist """ selected_sets = self._listwidget_sets.selectedItems() selected_str = 'Currently loaded directory set(s):\n"' + \ '", "'.join([s.text() for s in selected_sets]) + '"' pieces = get_pieces_from_sets( [s.text() + '.txt' for s in selected_sets]) playlist = list(pieces.keys()) # must be a list to be shuffled shuffled = self._checkbox_shuffle.isChecked() if shuffled: shuffle(playlist) self._set_pieces_and_playlist(pieces, playlist, selected_str, shuffled) self.close()
class WordSetWidget(PluginWidget): """Plugin to show handle word sets from user and gather matching variants as a new selection. """ ENABLE = True def __init__(self, parent=None): super().__init__(parent) self.conn = None self.set_names = list() self.setWindowTitle(self.tr("Word Sets")) self.toolbar = QToolBar() self.view = QListWidget() self.view.setIconSize(QSize(20, 20)) self.view.itemDoubleClicked.connect(self.open_wordset) self.view.setSelectionMode(QAbstractItemView.ExtendedSelection) # setup tool bar self.toolbar.setIconSize(QSize(16, 16)) self.toolbar.addAction(FIcon(0xF0415), self.tr("Add Word set"), self.add_wordset) self.edit_action = self.toolbar.addAction(FIcon(0xF0DC9), self.tr("Edit Word set"), self.open_wordset) self.remove_action = self.toolbar.addAction(FIcon(0xF0A7A), self.tr("Remove Word set"), self.remove_wordset) self.edit_action.setEnabled(False) self.remove_action.setEnabled(False) v_layout = QVBoxLayout() v_layout.setContentsMargins(0, 0, 0, 0) v_layout.setSpacing(0) v_layout.addWidget(self.view) v_layout.addWidget(self.toolbar) self.setLayout(v_layout) # Item selected in view self.view.selectionModel().selectionChanged.connect( self.on_item_selected) def on_item_selected(self, *args): """Enable the remove button when an item is selected""" # Get list of all selected model item indexes if self.view.selectionModel().selectedIndexes(): self.edit_action.setEnabled(True) self.remove_action.setEnabled(True) else: self.edit_action.setEnabled(False) self.remove_action.setEnabled(False) def import_wordset(self, words, wordset_name): """Import given words into a new wordset in database Warnings: There is NO CHECK on manual user's inputs! Except during DB insertion. Args: words(list): List of words to be inserted wordset_name(str): Name of the word set Returns: (boolean): Status of the wordset creation """ # Dump the list in a temporary file _, filename = tempfile.mkstemp() with open(filename, "w") as file: [file.write(word + "\n") for word in words] # Import the content of the temp file in DB result = import_cmd(self.conn, "wordsets", wordset_name, filename) if not result["success"]: LOGGER.error(result) QMessageBox.critical( self, self.tr("Error while importing set"), self.tr("Error while importing set '%s'") % wordset_name, ) os.remove(filename) return result["success"] def add_wordset(self): """Display a window to allow to add/edit/remove word sets The set is then imported in database. """ dialog = WordListDialog() if dialog.exec_() != QDialog.Accepted: return wordset_name = None while not wordset_name: wordset_name, _ = QInputDialog.getText( self, self.tr("Create a new set"), self.tr("Name of the new set:")) if not wordset_name: return if wordset_name in self.set_names: # Name already used QMessageBox.critical( self, self.tr("Error while creating set"), self. tr("Error while creating set '%s'; Name is already used") % wordset_name, ) wordset_name = None # Import & update view self.import_wordset(dialog.model.stringList(), wordset_name) self.populate() def remove_wordset(self): """Delete word set from database""" if len(self.view.selectedItems()) == 0: # if selection is empty return reply = QMessageBox.question( self, self.tr("Drop word set"), self.tr("Are you sure you want to remove the selected set(s)?"), QMessageBox.Yes | QMessageBox.No, ) if reply != QMessageBox.Yes: return # Delete all selected sets for i in self.view.selectedItems(): result = drop_cmd(self.conn, "wordsets", i.text()) if not result["success"]: LOGGER.error(result) QMessageBox.critical( self, self.tr("Error while deleting set"), self.tr("Error while deleting set '%s'") % i.text(), ) self.populate() def open_wordset(self): """Display a window to allow to edit the selected word set The previous set is dropped and the new is then imported in database. """ wordset_name = self.view.currentItem().text() dialog = WordListDialog() # populate dialog dialog.model.setStringList( list(get_words_in_set(self.conn, wordset_name))) if dialog.exec_() == QDialog.Accepted: # Drop previous drop_cmd(self.conn, "wordsets", wordset_name) # Import new self.import_wordset(dialog.model.stringList(), wordset_name) self.populate() def on_open_project(self, conn): """ override """ self.conn = conn self.on_refresh() def on_refresh(self): """ override """ if self.conn: self.populate() def populate(self): """Actualize the list of word sets""" self.view.clear() self.set_names = list() for data in get_wordsets(self.conn): set_name = data["name"] item = QListWidgetItem() item.setText(set_name) item.setIcon(FIcon(0xF0A38)) self.view.addItem(item) self.set_names.append(set_name)
class SynthPdfWidget(QWidget): def __init__(self, status_bar): super(SynthPdfWidget, self).__init__() self.status_bar = status_bar layout = QHBoxLayout() self.list_view = QListWidget() self.list_view.setSelectionMode(QAbstractItemView.ExtendedSelection) self.list_view.setAlternatingRowColors(True) self.list_view.itemClicked.connect(self.click_item_signal) self.list_view.itemEntered.connect(self.click_item_signal) self.list_view.itemSelectionChanged.connect( self.change_selection_signal) controls_layout = QVBoxLayout() controls_layout.setAlignment(Qt.AlignTop) select_zone = SelectWidget(self.click_move_up, self.click_move_down, self.click_invert, self.click_delete) select_zone.setMinimumWidth(250) add_pages_button = QPushButton("Add Pages From PDF") add_pages_button.clicked.connect(self.add_pdf) controls_layout.addWidget(select_zone) controls_layout.addWidget(add_pages_button) layout.addWidget(self.list_view) layout.addLayout(controls_layout) self.setLayout(layout) def _add_pages_from_pdf(self, pdf_path): with open(pdf_path, "rb") as file: reader = PdfFileReader(file) file_name = os.path.basename(pdf_path) pages_count = reader.getNumPages() for i in range(pages_count): new_item = ImageListItem( "page " + str(i + 1) + " (" + file_name + ")", (pdf_path, i)) self.list_view.addItem(new_item) self.status_bar.showMessage("Add " + str(pages_count) + " pages") # ----------external methods from create command def extern_get_files_and_pages( self ): # return selected pages in the form {file1: [p1, p2, ...], file2: [p1, p2, ...], ...} to_return = {} if len(self.list_view.selectedItems() ) == 0: # nothing selected, add all pages items_count = self.list_view.count() for item_index in range(items_count): item = self.list_view.item(item_index) item_data = item.get_data() file_name = item_data[0] page_index = item_data[1] if file_name in to_return: to_return[file_name].append(page_index) else: to_return[file_name] = [page_index] else: for s in self.list_view.selectedItems(): s_data = s.get_data() file_name = s_data[0] page_index = s_data[1] if file_name in to_return: to_return[file_name].append(page_index) else: to_return[file_name] = [page_index] return to_return # ----------add button----------------------- def add_pdf(self): files_dialog = QFileDialog() files_dialog.setNameFilter("PDF (*.pdf)") files_dialog.setFileMode(QFileDialog.ExistingFiles) if files_dialog.exec_(): files = files_dialog.selectedFiles() for f_path in files: self._add_pages_from_pdf(f_path) # ----------List_view signals---------------- def click_item_signal(self, item): self.status_bar.showMessage("Select " + str(item.text())) def change_selection_signal(self): if len(self.list_view.selectedItems()) == 0: # self._set_preview(None) # nothing selected pass # ----------list_view commands--------------- def click_move_up(self): selected = self.list_view.selectedItems() selected_indexes = [ self.list_view.indexFromItem(sel).row() for sel in selected ] selected_indexes.sort() if len(selected_indexes) > 0 and selected_indexes[0] > 0: for index in selected_indexes: prev_item = self.list_view.takeItem(index - 1) self.list_view.insertItem(index, prev_item) self.status_bar.showMessage("Move " + str(len(selected_indexes)) + " items") else: self.status_bar.showMessage("Nothing to move") def click_move_down(self): selected = self.list_view.selectedItems() selected_indexes = [ self.list_view.indexFromItem(sel).row() for sel in selected ] selected_indexes.sort() sel_count = len(selected_indexes) if len(selected_indexes) > 0 and selected_indexes[ sel_count - 1] < self.list_view.count() - 1: for i_index in range(sel_count): next_item = self.list_view.takeItem( selected_indexes[sel_count - i_index - 1] + 1) self.list_view.insertItem( selected_indexes[sel_count - i_index - 1], next_item) self.status_bar.showMessage("Move " + str(len(selected_indexes)) + " items") else: self.status_bar.showMessage("Nothing to move") def click_invert(self): selected = self.list_view.selectedItems() selected_indexes = [] for sel in selected: selected_indexes.append(self.list_view.indexFromItem(sel).row()) total_indexes = [i for i in range(self.list_view.count())] new_indexes = [] for i in total_indexes: if i not in selected_indexes: new_indexes.append(i) self.list_view.clearSelection() for i in new_indexes: self.list_view.item(i).setSelected(True) self.status_bar.showMessage("Invert selection: " + str(new_indexes)) def click_delete(self): selected = self.list_view.selectedItems() delete_names = [] for s in selected: s_index = self.list_view.indexFromItem(s).row() del_item = self.list_view.takeItem(s_index) delete_names.append(del_item.text()) if len(delete_names) == 0: self.status_bar.showMessage("Nothing to delete") else: self.status_bar.showMessage("Delete items: " + ", ".join(delete_names))
class EditReportsWindow(QMainWindow): def __init__(self, subject_name): QMainWindow.__init__(self) self.setWindowTitle("Edit Reports: {} - {} {}".format( subject_name, config.APP_NAME, config.APP_VERSION)) self.setMinimumWidth(1200) self.setStyleSheet(config.STYLESHEET) self.subject = could_try_harder.load(subject_name) self.student = {} # Used to keep track of current student self.s_index = 0 self.load_student() # Widgets self.previous_student_button = QPushButton("Previous Student") self.student_name_label = QLabel() self.student_name_label.setProperty("styleClass", "title") self.next_student_button = QPushButton("Next Student") self.intro_comment_label = QLabel("Introductory Comment") self.intro_comment_label.setProperty("styleClass", "heading") self.intro_comment = QLabel() self.comment_bank_label = QLabel("Comment Bank") self.comment_bank_label.setProperty("styleClass", "heading") self.comment_bank_listwidget = QListWidget() self.add_comment_button = QPushButton("Add Selected Comment") self.comment_label = QLabel("Student Comment") self.comment_label.setProperty("styleClass", "heading") self.comment_textedit = QTextEdit() self.cancel_button = QPushButton("Cancel") self.save_button = QPushButton("Save") # Layout self.layout = QVBoxLayout() self.top_layout = QHBoxLayout() self.top_layout.addWidget(self.previous_student_button, 0, Qt.AlignLeft) self.top_layout.addWidget(self.student_name_label, 1, Qt.AlignCenter) self.top_layout.addWidget(self.next_student_button, 0, Qt.AlignRight) self.layout.addLayout(self.top_layout) self.middle_layout = QVBoxLayout() self.middle_layout.addWidget(self.intro_comment_label) self.middle_layout.addWidget(self.intro_comment) self.middle_layout.addWidget(self.comment_bank_label) self.middle_layout.addWidget(self.comment_bank_listwidget) self.middle_layout.addWidget(self.add_comment_button) self.middle_layout.addWidget(self.comment_label) self.middle_layout.addWidget(self.comment_textedit) self.layout.addLayout(self.middle_layout) self.bottom_layout = QHBoxLayout() self.bottom_layout.addWidget(self.cancel_button, 0, Qt.AlignLeft) self.bottom_layout.addWidget(self.save_button, 0, Qt.AlignRight) self.layout.addLayout(self.bottom_layout) # Slots self.previous_student_button.clicked.connect(self.do_previous_student) self.next_student_button.clicked.connect(self.do_next_student) self.comment_bank_listwidget.itemSelectionChanged.connect( self.do_update_comment_bank_selection) self.add_comment_button.clicked.connect(self.do_add_comment) self.cancel_button.clicked.connect(self.do_cancel) self.save_button.clicked.connect(self.do_save) # Initial UI Update self.update_ui() self.widget = QWidget() self.widget.setLayout(self.layout) self.setCentralWidget(self.widget) def update_ui(self): # Page title self.student_name_label.setText(self.student['first_name'] + " " + self.student['last_name']) # Enable/Disabled Next/Previous Buttons if self.s_index <= 0: self.previous_student_button.setEnabled(False) else: self.previous_student_button.setEnabled(True) if self.s_index < len(self.subject['students']) - 1: self.next_student_button.setEnabled(True) else: self.next_student_button.setEnabled(False) self.do_update_comment_bank_selection() # Intro comment self.intro_comment.setText( could_try_harder.do_placeholders(self.subject['intro_comment'], self.student['first_name'], self.student['pronouns'])) # Comment Bank self.comment_bank_listwidget.clear() for comment in self.subject['comment_bank']: self.comment_bank_listwidget.addItem( could_try_harder.do_placeholders(comment, self.student['first_name'], self.student['pronouns'])) # Student Comment self.comment_textedit.clear() self.comment_textedit.insertPlainText(self.student['comment']) def load_student(self): """ Loads student into the self.student dict temporarily """ self.student = self.subject['students'][self.s_index] def save_comment(self): """ Saves student comment into the self.subject dict temporarily. Not saved into the save file. """ comment = self.comment_textedit.toPlainText() comment = could_try_harder.do_placeholders(comment, self.student['first_name'], self.student['pronouns']) comment = could_try_harder.do_style(comment) self.subject['students'][self.s_index]['comment'] = comment @Slot() def do_next_student(self): if self.s_index < len(self.subject['students']) - 1: self.save_comment() self.s_index += 1 self.load_student() self.update_ui() @Slot() def do_previous_student(self): if self.s_index > 0: self.save_comment() self.s_index -= 1 self.load_student() self.update_ui() @Slot() def do_update_comment_bank_selection(self): # Enable/Disable the Add Comment button if self.comment_bank_listwidget.selectedItems(): self.add_comment_button.setEnabled(True) else: self.add_comment_button.setEnabled(False) @Slot() def do_add_comment(self): # Add a space if we need to. if not self.comment_textedit.textCursor().atStart( ) and not self.comment_textedit.textCursor().block().text().endswith( " "): self.comment_textedit.insertPlainText(" ") self.comment_textedit.insertPlainText( self.comment_bank_listwidget.currentItem().text()) @Slot() def do_save(self): self.save_comment() if could_try_harder.save(self.subject): self.close() else: # TODO better error handling here print("Save failed") @Slot() def do_cancel(self): self.close()
class MainWindow(QMainWindow): def __init__(self): QMainWindow.__init__(self) self.setWindowTitle("{} {}".format(config.APP_NAME, config.APP_VERSION)) self.setMinimumWidth(600) self.setStyleSheet(config.STYLESHEET) # Widgets self.import_label = QLabel( 'Import a CSV class list to add a new class of reports.') self.import_button = QPushButton('Import CSV...') self.saved_label = QLabel('You have the following saved classes:') self.saved_listwidget = QListWidget(self) self.edit_comment_bank_button = QPushButton('Edit Comment Bank') self.edit_reports_button = QPushButton('Edit Reports') self.export_reports_button = QPushButton('Export Reports') self.delete_class_button = QPushButton('Delete') # Layout self.layout = QVBoxLayout() self.layout.addWidget(self.import_label) self.layout.addWidget(self.import_button) self.layout.addWidget(self.saved_label) self.layout.addWidget(self.saved_listwidget) self.layout.addWidget(self.edit_comment_bank_button) self.layout.addWidget(self.edit_reports_button) self.layout.addWidget(self.export_reports_button) self.layout.addWidget(self.delete_class_button) # Initial run of listbox update self.update_saved_list() # Slot connections self.import_button.clicked.connect(self.import_csv) self.saved_listwidget.itemSelectionChanged.connect( self.do_update_selection) self.edit_comment_bank_button.clicked.connect(self.edit_comment_bank) self.edit_reports_button.clicked.connect(self.edit_reports) self.export_reports_button.clicked.connect(self.export_reports) self.delete_class_button.clicked.connect(self.delete_class) self.widget = QWidget() self.widget.setLayout(self.layout) self.setCentralWidget(self.widget) @Slot() def import_csv(self): # TODO proper validation for input will mean building this dialog out # properly using QLineEdit subject_name, ok = QInputDialog().getText( self, "Subject Name", "Enter a name or code for the subject:") if subject_name and ok: filename, filt = QFileDialog.getOpenFileName( self, "Import CSV", os.path.expanduser("~"), "Comma Separated (*.csv)") if could_try_harder.import_class_list(filename, subject_name): self.update_saved_list() else: # TODO better error handling here print("Import Failed") return def update_saved_list(self): self.saved_listwidget.clear() self.saved_listwidget.addItems(could_try_harder.get_saved_list()) self.do_update_selection() @Slot() def do_update_selection(self): if self.saved_listwidget.selectedItems(): state = True else: state = False self.edit_comment_bank_button.setEnabled(state) self.edit_reports_button.setEnabled(state) self.export_reports_button.setEnabled(state) self.delete_class_button.setEnabled(state) @Slot() def edit_comment_bank(self): subject_name = self.saved_listwidget.currentItem().text() self.edit_comment_bank_window = EditCommentBankWindow(subject_name) self.edit_comment_bank_window.show() @Slot() def edit_reports(self): subject_name = self.saved_listwidget.currentItem().text() self.edit_reports_window = EditReportsWindow(subject_name) self.edit_reports_window.show() @Slot() def export_reports(self): subject_name = self.saved_listwidget.currentItem().text() filename, filt = QFileDialog.getSaveFileName(self, "Export", os.path.expanduser("~"), "Text Files (*.txt)") if filename: if not could_try_harder.export(subject_name, filename): # TODO better error handling here print("Export failed.") return @Slot() def delete_class(self): confirm_msg = QMessageBox(self) confirm_msg.setWindowTitle("Confirm") confirm_msg.setText( "This will delete the class, comment bankd and reports.") confirm_msg.setInformativeText("Continue?") confirm_msg.setStandardButtons(QMessageBox.No | QMessageBox.Yes) confirm_msg.setDefaultButton(QMessageBox.Yes) confirm = confirm_msg.exec() if confirm == QMessageBox.Yes: subject_name = self.saved_listwidget.currentItem().text() if not could_try_harder.delete(subject_name): # TODO better error handling here print("Delete failed.") self.update_saved_list()
class Window(QMainWindow): def __init__(self): super(Window, self).__init__() self.lastProjectDirectory = None self.view = None self.items = dict() self.loadSettings() self.setWindowTitle("2D Tagger") self.createActions() self.createMenus() self.createFileList() self.createContainer() self.createStatusBar() def loadSettings(self): try: with io.open(Path(__file__).parent / 'app.json', 'r') as f: data = json.load(f) if 'lastProjectDirectory' in data: self.lastProjectDirectory = data['lastProjectDirectory'] except: pass def saveSettings(self): data = dict() data['lastProjectDirectory'] = self.lastProjectDirectory with io.open(Path(__file__).parent / 'app.json', 'w') as f: json.dump(data, f, indent=' ') def createActions(self): # root = QFileInfo(__file__).absolutePath() self.openAct = QAction( # QIcon(root + '/icons/open.png'), "&Open", self, shortcut=QKeySequence.Open, statusTip="Open project", triggered=self.openProject) self.closeAct = QAction( # QIcon(root + '/icons/close.png'), "&Close", self, shortcut=QKeySequence.Close, statusTip="Close project", triggered=self.closeProject, enabled=False) self.exitAct = QAction( # QIcon(root + '/icons/quit.png'), "&Quit", self, shortcut=QKeySequence.Quit, statusTip="Close the application", triggered=self.close) self.aboutAct = QAction("&About", self, statusTip="Show the application's About box", triggered=self.about) def createMenus(self): fileMenu = self.menuBar().addMenu("&File") fileMenu.addAction(self.openAct) fileMenu.addAction(self.closeAct) fileMenu.addSeparator() fileMenu.addAction(self.exitAct) self.menuBar().addSeparator() helpMenu = self.menuBar().addMenu("&Help") helpMenu.addAction(self.aboutAct) def createFileList(self): self.files = QListWidget() self.files.setSelectionMode(QAbstractItemView.SingleSelection) self.files.setSortingEnabled(False) self.files.itemSelectionChanged.connect(self.onSelect) self.files.itemDoubleClicked.connect(self.onToggle) self.files.setEnabled(False) dock = QDockWidget("Images", self) dock.setAllowedAreas(Qt.LeftDockWidgetArea) dock.setFixedWidth(320) dock.setFeatures(QDockWidget.NoDockWidgetFeatures) dock.setWidget(self.files) self.addDockWidget(Qt.LeftDockWidgetArea, dock) def createContainer(self): self.container = QWidget(self) self.container.setStyleSheet("View { background: black; }") self.container.setLayout(QVBoxLayout()) self.setCentralWidget(self.container) def createStatusBar(self): self.statusBar().showMessage("Initialized") def enableProjectActions(self, enabled): actions = [self.closeAct, self.files] for action in actions: action.setEnabled(enabled) def closeEvent(self, event): event.accept() def openProject(self, path=None): if not path: path = QFileDialog.getExistingDirectory( self, "Choose image directory", dir=self.lastProjectDirectory) if path: self.closeProject() self.view = View(self) self.view.previous.connect(self.onPrevious) self.view.next.connect(self.onNext) self.container.layout().addWidget(self.view) filename = Path(path) / "items.json" if filename.exists(): with io.open(filename, 'r') as f: self.items = json.load(f) else: self.items = dict() for filename in find_images(Path(path)): if filename in self.items: continue self.items[filename] = dict({"active": True}) self.refreshItems() self.files.setItemSelected(self.files.item(0), True) self.enableProjectActions(True) self.lastProjectDirectory = path self.saveProject() self.saveSettings() def saveProject(self): if not self.view: return with io.open(Path(self.lastProjectDirectory) / "items.json", 'w') as f: json.dump(self.items, f, indent=' ') def closeProject(self): self.enableProjectActions(False) if self.view: self.container.layout().removeWidget(self.view) self.view.close() self.view = None self.items = dict() self.refreshItems() def selection(self): selection = self.files.selectedItems() if len(selection) > 0: return selection[0].text() return None def refreshItems(self): filenames = sorted(self.items.keys()) for i in range(len(filenames)): filename = filenames[i] item = self.items[filename] file = self.files.item(i) if not file: file = QListWidgetItem(filenames[i]) self.files.insertItem(i, file) if item['active']: file.setTextColor(QColor.fromRgbF(0.0, 0.5, 0.0)) else: file.setTextColor(QColor.fromRgbF(0.5, 0.0, 0.0)) while self.files.count() > len(filenames): self.files.takeItem(len(filenames)) @Slot() def onToggle(self, item): self.items[ item.text()]['active'] = not self.items[item.text()]['active'] self.refreshItems() self.saveProject() @Slot() def onSelect(self): selection = self.selection() if self.view: self.view.save() if selection: self.view.load(Path(self.lastProjectDirectory) / selection) @Slot() def onPrevious(self): if self.files.count() == 0: return index = 0 selection = self.selection() if selection: for i in range(self.files.count()): item = self.files.item(i) if item.text() == selection: index = i - 1 break if index < 0: index = self.files.count() - 1 self.files.setItemSelected(self.files.item(index), True) @Slot() def onNext(self): if self.files.count() == 0: return index = 0 selection = self.selection() if selection: for i in range(self.files.count()): item = self.files.item(i) if item.text() == selection: index = i + 1 break if index >= self.files.count(): index = 0 self.files.setItemSelected(self.files.item(index), True) @Slot(str) def showMessage(self, message): self.statusBar().showMessage(message) def about(self): QMessageBox.about( self, "About Application", "The <b>2D Tagger</b> can load images, manually tag/label images and export result." )
class TimerWindow(QWidget): elapsed_time = 0 thread_running = False #api_token = 'fea20970f5bb1b75c96dfa8985fd15b2a3c0f8a8d3261381d0176a05475781ee88d9f7252511e5e085b99e1cee37efa86f7364b7ed5203bccd2c2fd9b76057fe' apa = IdleTime() workingthread = None systray = None db = Database() api_token = db.readDatabase()['userdata']['api_token'] def __init__(self): super().__init__() self.title = 'Timey' self.left = 10 self.top = 10 self.width = 440 self.height = 680 self.initUI() self.NetworkSetup() def initUI(self): self.setWindowTitle(self.title) self.setWindowIcon(QIcon(Config.icon)) self.setGeometry(self.left, self.top, self.width, self.height) layout = QGridLayout() self.setLayout(layout) layout.setColumnStretch(1, 4) layout.setColumnStretch(2, 4) style = """ background-color: white; border-bottom: 1px solid #fff123; """ self.setStyleSheet(style) self.labeltimer = QLabel('00:00:00', self) self.labeltimer.setAlignment(Qt.AlignCenter) self.labeltimer.setStyleSheet("""QLabel { font: medium Ubuntu; font-size: 32px; color: #006325; border-width:1px; } """) fa5_icon = qta.icon('fa5s.play') #fa5_button = QtGui.QPushButton(fa5_icon, 'Font Awesome! (regular)') self.button = QPushButton(fa5_icon, "Start") self.button.clicked.connect(self.clickStart) self.button.setStyleSheet("""QPushButton { #background-color: #f06325; #color: white; #font-color: white; #min-width: 70px; #max-width: 70px; #min-height: 70px; #max-height: 70px; #border-radius: 35px; #border-width: 1px; border-color: #ae32a0; border-style: solid; } QPushButton:hover { background-color: #328930; } QPushButton:pressed { background-color: black; color: black; border-width:10px; } """) #self.label4 = QLabel('Working: ', self) #self.label4.setAlignment(Qt.AlignCenter) self.label3 = QLabel('None', self) self.label3.setAlignment(Qt.AlignCenter) self.test = QListWidget(self) self.test.addItem("item4") self.test.itemSelectionChanged.connect(self.selectItem) self.addtask = QLineEdit(self) #fa5_plusicon = qta.icon('fa5s.plus') self.addtaskbutton = QPushButton(qta.icon('fa5s.plus'), "Add") self.addtaskbutton.clicked.connect(self._click_additem) self.deltaskbutton = QPushButton(qta.icon('fa5s.minus'), "Del") self.deltaskbutton.clicked.connect(self._click_deltask) self.tasks = QListWidget(self) self.tasks.addItem("No tasks") layout.addWidget(self.labeltimer, 0, 0) layout.addWidget(self.button, 0, 1) #layout.addWidget(self.label4,1,0) layout.addWidget(self.label3, 1, 0) layout.addWidget(self.test, 2, 0) layout.addWidget(self.addtask, 3, 0) layout.addWidget(self.addtaskbutton, 3, 1) layout.addWidget(self.deltaskbutton, 3, 2) layout.addWidget(self.tasks, 4, 0) self.show() print("init end") def _update_timer(self): if self.thread_running == True: self.thread_running = False return 0 while True: #self.elapsed_time = time.time() self.thread_running = True ''' this is little weired.. but works for now ''' if self.apa.thread_exit == True or self.thread_exit == True: data = api.activity_current(self.api_token) print(data) if not data.get('error'): api.activity_stop(self.api_token, data['id']) self.button.setText('Start') self.workingthread.exit() self.thread_running = False break self.elapsed_time += 1 self.labeltimer.setText(self._online_time(self.elapsed_time)) time.sleep(1) def _click_additem(self): print("oh oh oh oh") print(self.addtask.text()) text = self.addtask.text() project_id = None if len(text) > 0: print("This seems acceptable") for _x in self.test.selectedItems(): project_id = _x.data(Qt.UserRole) print(project_id) if project_id == None: QMessageBox.information(self, 'PyQt5 message', "Please select a a project first", QMessageBox.Ok) else: api.task_add(self.api_token, project_id, text) self.selectItem() else: QMessageBox.information(self, 'PyQt5 message', "Please enter a taskname first!", QMessageBox.Ok) print("please enter a name of the task you want to add first") def _click_deltask(self): print("Ooopsie") task_id = None for _x in self.tasks.selectedItems(): task_id = _x.data(Qt.UserRole) print(task_id) if task_id is not None: api.task_delete(self.api_token, task_id) self.selectItem() else: QMessageBox.information(self, 'PyQt5 message', "unable to delete task, no such task_id", QMessageBox.Ok) def clickStart(self): logging.debug("click start/stop button") print(self.button.text()) #self.test.clear() data = {} ''' user click start timer ''' if self.button.text() == 'Start': project_id = None task_id = None for _x in self.test.selectedItems(): project_id = _x.data(Qt.UserRole) for _x in self.tasks.selectedItems(): task_id = _x.data(Qt.UserRole) if project_id == None: msgBox = QMessageBox() msgBox.setIcon(QMessageBox.Information) msgBox.setText("Please select a project first") msgBox.setWindowTitle("Select project") msgBox.setStandardButtons(QMessageBox.Ok) msgBox.exec() print("Please select one of your project first") return 0 else: data = api.activity_start(self.api_token, project_id, task_id) ''' this need to be fix ''' try: if data.get('error'): errorBox = DialogBox() returnValue = errorBox.MsgBox( data.get('error') + "Do you want to stop current activity?", "error") if returnValue == QMessageBox.Ok: data = api.activity_current(self.api_token) print(data) api.activity_stop(self.api_token, data['id']) print("Time to do something weired!") return 0 #do we need this? #print(data.get('error')) elif data: #apa = IdleTime() self.apa.thread_exit = False print(self.workingthread) self.workingthread = QThread() self.workingthread.started.connect(self.apa.thread_handle) #self.apa.moveToThread(self.workingthread) self.workingthread.start() self.thread_exit = False t = threading.Thread(target=self._update_timer) t.start() self.systray.StartWorking.setText("Stop Working") self.button.setText('Stop') fa5s_icon = qta.icon('fa5s.stop') self.button.setIcon(fa5s_icon) self.test.setDisabled(True) return 1 except AttributeError: ''' if we reach this stage we either got an invalid API response (no error field) or our internet connection isnt working ''' #errorBox = DialogBox() #errorBox.MsgBox("No internet connection or invalid API response", "error") QMessageBox.information( self, 'error', "No internet connection or invalid API response", QMessageBox.Ok) else: ''' stop activity if its running ''' data = api.activity_current(self.api_token) print(data) api.activity_stop(self.api_token, data['id']) self.thread_exit = True self.apa.thread_exit = True self.systray.StartWorking.setText("Start Working") self.button.setText('Start') fa5s_icon = qta.icon('fa5s.play') self.button.setIcon(fa5s_icon) self.test.setDisabled(False) print("end of clickstart") def selectItem(self): for _x in self.test.selectedItems(): self.label3.setText(_x.text()) print(_x.text()) print(_x.data(Qt.UserRole)) try: data = api.task_get(self.api_token, _x.data(Qt.UserRole)) except ApiException: data = self.db.searchTask(_x.data(Qt.UserRole)) self.tasks.clear() print(data) for _x in data: item = QListWidgetItem(_x["name"], self.tasks) item.setData(Qt.UserRole, _x["task_id"]) #print(self.test.text()) def NetworkSetup(self): self.test.clear() data_p2 = None data_p = None try: data_p = api.get_projects(self.api_token) except ApiException: data_p = self.db.readDatabase()["projects"] #print("data_p:") #print(data_p) #print("data_p2") #print(data_p2) l = [] print(data_p) for _x in data_p: print("loop") print(_x) #self.test.addItem(_x["name"]) item = QListWidgetItem(_x["name"], self.test) item.setData(Qt.UserRole, _x["id"]) #l = [] try: lol = api.task_get(self.api_token, _x["id"]) print(lol) for _x in lol: #if len(lol) > 0: l.append(_x) except ApiException: pass #print(lol) print(l) if len(l) > 0: self.db.saveTasks(l) end_date = datetime.utcnow().isoformat() start_date = datetime.utcnow().replace(hour=0, minute=0, second=0, microsecond=0).isoformat() try: data = api.get_activity(self.api_token, start_date, end_date) except ApiException: data = [{"duration": 20}, {"duration": 400}, {"duration": 300}] print(data) for _x in data: #self.db.save_activity() self.elapsed_time += _x['duration'] ''' Set timer ''' self.labeltimer.setText(self._online_time(self.elapsed_time)) if len(data) > 0: self.db.save_activity(data) try: whoami = api.whoami(self.api_token) self.db.saveUser(whoami["username"], whoami["api_token"], whoami['api_expire']) self.db.saveProjects(data_p) except (ApiException, TypeError): pass #self.db.saveTasks def _online_time(self, data): hour = 0 minute = 0 second = 0 hour = int(data / 60 / 60) data = data - (hour * 3600) minute = int(data / 60) data = data - (minute * 60) second = int(data) if hour < 10: hour = "0" + str(hour) if minute < 10: minute = "0" + str(minute) if second < 10: second = "0" + str(second) time_string = str(hour) + ":" + str(minute) + ":" + str(second) return time_string def closeEvent(self, event): data = api.activity_current(self.api_token) print(data) try: if data.get('error'): msg = "are you sure you want to quit?" elif data.get('id'): msg = "Are you sure you want to quit? if you quit the app we will stop the timetracker." except: msg = "Are you sure you want to quit" #pass errorBox = DialogBox() returnValue = errorBox.MsgBox(msg, "error") if returnValue == QMessageBox.Ok: #if data.get('id'): try: api.activity_stop(self.api_token, data['id']) except (TypeError, KeyError): pass self.thread_exit = True self.apa.thread_exit = True time.sleep(2) event.accept() else: event.ignore() print("close event")
class AddMovieDialog(QDialog): client = Client() def __init__(self, parent): super(AddMovieDialog, self).__init__(parent) self.setWindowTitle("Add Movie") main_layout = QVBoxLayout(self) self.selected_movies = None search_layout = QHBoxLayout() main_layout.addLayout(search_layout) self.search_field = QLineEdit() self.search_field.returnPressed.connect(self.find_action) self.search_field.setPlaceholderText("Search movies...") search_layout.addWidget(self.search_field) self.find_all_checkbx = QCheckBox("Find All") search_layout.addWidget(self.find_all_checkbx) self.result_list = QListWidget() self.result_list.setSelectionMode(QListWidget.ExtendedSelection) main_layout.addWidget(self.result_list) button_layout = QHBoxLayout() main_layout.addLayout(button_layout) add_bttn = QPushButton("Add Movie") add_bttn.clicked.connect(self.accept) add_all_bttn = QPushButton("Add All Movies") add_all_bttn.clicked.connect(self.add_all_action) cancel_bttn = QPushButton("Cancel") cancel_bttn.clicked.connect(self.reject) button_layout.addWidget(add_bttn) button_layout.addWidget(add_all_bttn) button_layout.addWidget(cancel_bttn) def keyPressEvent(self, event): event.ignore() def add_all_action(self): for i in range(self.result_list.count()): self.result_list.setItemSelected(self.result_list.item(i), True) self.accept() def accept(self): selected_items = self.result_list.selectedItems() if not selected_items: return self.selected_movies = [ i.movie_data for i in selected_items if not self.client.find_movie(i.movie_data["id"]) ] super(AddMovieDialog, self).accept() def find_action(self): movie_title = self.search_field.text() if len(movie_title): self.result_list.clear() for item in find_movie( movie_title, all_pages=self.find_all_checkbx.isChecked()): MovieItem(self.result_list, item)
class SteamAccountSwitcherGui(QMainWindow, Accounts, DialogAccount, DialogImportAccount, DialogSteamapiKey, Settings, SystemTray): account_dialog_window: QDialog submit_button: QPushButton tray_menu: QMenu def __init__(self): QMainWindow.__init__(self) signal.signal(signal.SIGINT, self.exit_app) self.setWindowTitle("Steam Account Switcher") self.setMinimumSize(300, 200) self.resize(300, 300) # Logo self.switcher_logo = QIcon("logo.png") self.setWindowIcon(self.switcher_logo) if platform.system() == "Windows": # windows taskbar app icon fix import ctypes win_appid = 'github.tommis.steam_account_switcher' ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID( win_appid) from steamswitcher import SteamSwitcher self.switcher = SteamSwitcher() self.args = self.switcher.args self.main_widget = QWidget() if self.args.gui or self.switcher.settings.get( "show_on_startup", True) and not self.args.no_gui: self.show() elif self.args.no_gui and self.args.no_tray: self.exit_app() self.menu_bar = self.menuBar() self.file_menu = self.menu_bar.addMenu(_("File")) self.settings_menu = self.menu_bar.addMenu(_("Settings")) self.size_menu = self.menu_bar.addMenu(_("Size")) refresh_action = QAction(_("Refresh"), self) import_action = QAction(_("Import accounts"), self) open_skinsdir_action = QAction(_("Skins dir"), self) about_action = QAction(_("About"), self) exit_action = QAction(_("Exit"), self) refresh_action.triggered.connect(Accounts.steamapi_refresh) import_action.triggered.connect( lambda: DialogImportAccount.import_accounts_dialog(self)) open_skinsdir_action.triggered.connect(self.open_skinsdir) about_action.triggered.connect(lambda: DialogAbout.about_dialog(self)) exit_action.triggered.connect(self.exit_app) refresh_action.setShortcut("F5") exit_action.setShortcut("Ctrl+Q") self.file_menu.addActions([ refresh_action, import_action, open_skinsdir_action, about_action ]) self.file_menu.addSeparator() self.file_menu.addAction(exit_action) set_steamapi_key = QAction(_("Set steamapi key"), self) show_avatars = QAction(_("Show avatars"), self, checkable=True) use_systemtray = QAction(_("Use systemtray"), self, checkable=True) after_login_menu = QMenu(_("After login")) after_login_behaviour_group = QActionGroup(after_login_menu) nothing_behaviour = QAction(_('Nothing'), after_login_behaviour_group, checkable=True, data="nothing") close_behaviour = QAction(_('Close'), after_login_behaviour_group, checkable=True, data="close") minimize_behaviour = QAction(_('Minimize to taskbar'), after_login_behaviour_group, checkable=True, data="minimize") minimize_tray_behaviour = QAction(_('Minimize to tray'), after_login_behaviour_group, checkable=True, data="minimize_tray") after_login_menu.addActions([ nothing_behaviour, close_behaviour, minimize_behaviour, minimize_tray_behaviour ]) behaviour_switcher = { "close": lambda: close_behaviour.setChecked(True), "minimize": lambda: minimize_behaviour.setChecked(True), "minimize_tray": lambda: minimize_tray_behaviour.setChecked(True) } behaviour_switcher.get(self.switcher.settings["behavior_after_login"], lambda: nothing_behaviour.setChecked(True))() after_login_menu.triggered.connect(self.set_after_login_action) self.systemtray(self.main_widget) set_steamapi_key.triggered.connect(lambda: self.steamapi_key_dialog()) show_avatars.triggered.connect(lambda: self.set_show_avatars()) use_systemtray.triggered.connect(lambda: self.set_use_systemtray()) self.settings_menu.addAction(set_steamapi_key) self.settings_menu.addSeparator() self.settings_menu.addActions([show_avatars, use_systemtray]) self.settings_menu.addMenu(after_login_menu) show_avatars.setChecked(self.switcher.settings.get("show_avatars")) use_systemtray.setChecked(self.switcher.settings.get("use_systemtray")) set_size_small = QAction(_("Small"), self) set_size_medium = QAction(_("Medium"), self) set_size_large = QAction(_("Large"), self) set_size_small.triggered.connect(lambda: self.set_size("small")) set_size_medium.triggered.connect(lambda: self.set_size("medium")) set_size_large.triggered.connect(lambda: self.set_size("large")) self.size_menu.addActions( [set_size_small, set_size_medium, set_size_large]) set_size_small.setShortcut("Ctrl+1") set_size_medium.setShortcut("Ctrl+2") set_size_large.setShortcut("Ctrl+3") self.add_button = QPushButton(_("Add account")) self.edit_button = QPushButton(_("Edit account")) self.edit_button.setDisabled(True) self.buttons = QHBoxLayout() self.buttons.addWidget(self.add_button) self.buttons.addWidget(self.edit_button) self.layout = QVBoxLayout() self.main_widget.setLayout(self.layout) self.accounts_list = QListWidget() self.accounts_list.setDragDropMode(QAbstractItemView.InternalMove) self.layout.addWidget(self.accounts_list) self.layout.addLayout(self.buttons) self.layout.setSpacing(10) self.accounts_list.setSpacing(1) self.import_accounts_window = QDialog() self.load_accounts() def edit_button_enabled(): if self.accounts_list.selectedItems(): self.edit_button.setEnabled(True) else: self.edit_button.setEnabled(False) # Signals and Slots self.add_button.clicked.connect(lambda: self.account_dialog(True)) self.edit_button.clicked.connect(lambda: self.account_dialog(False)) self.accounts_list.itemSelectionChanged.connect(edit_button_enabled) self.accounts_list.doubleClicked.connect(lambda: self.steam_login( self.accounts_list.currentIndex().data(5))) self.accounts_list.setContextMenuPolicy(Qt.CustomContextMenu) self.accounts_list.customContextMenuRequested.connect( lambda: RightClickMenu.show_rightclick_menu(self)) #self.accounts_list.layoutChanged.connect(lambda: self.account_reordered) #self.accounts_list.dropEvent(self.dropEvent(QDropEvent)) self.setCentralWidget(self.main_widget) if self.args.no_tray: print("test") elif self.switcher.settings.get("use_systemtray") or self.args.tray: self.tray_icon.show() if self.switcher.first_run or self.args.first_run: self.steamapi_key_dialog() elif not self.switcher.first_run and \ not self.is_valid_steampi_key(self.switcher.settings["steam_api_key"]): self.tray_icon.showMessage("No api key", "Set the steam web api key.", self.switcher_logo) def exit_app(self): self.tray_icon.hide() QApplication.quit() def open_steam_profile( self, account, ): webbrowser.open(account["steam_user"].get("profileurl")) def open_skinsdir(self): if self.switcher.system_os == "Windows": os.startfile(self.switcher.skins_dir) elif self.switcher.system_os == "Linux": subprocess.Popen(["xdg-open", self.switcher.skins_dir]) def dropEvent(self, event): print("hallo") def set_stay_on_top(self): pass
class ImageToPdfWidget(QWidget): def __init__(self, status_link): super(ImageToPdfWidget, self).__init__() LABEL_WIDTH = 80 self.last_selected_items = [] self.options_mode = 0 self.update_status_combobox = False layout = QHBoxLayout() self.status_bar = status_link self.list_view = QListWidget() self.list_view.setSelectionMode(QAbstractItemView.ExtendedSelection) self.list_view.setAlternatingRowColors(True) self.list_view.itemClicked.connect(self.click_item_signal) self.list_view.itemEntered.connect(self.click_item_signal) self.list_view.itemSelectionChanged.connect( self.change_selection_signal) controls_layout = QVBoxLayout() controls_layout.setAlignment(Qt.AlignTop) select_zone = SelectWidget(self.click_move_up, self.click_move_down, self.click_invert, self.click_delete) # options zone ------------------------------------- options_zone = QGroupBox("Options") self.options_zone_layout = QVBoxLayout() self.options_mode_combobox = QComboBox() self._add_items_to_mode_combobox(self.options_mode_combobox) options_mode_label = QLabel("Mode") options_mode_label.setMaximumWidth(LABEL_WIDTH) options_mode_layout = QHBoxLayout() options_mode_layout.addWidget(options_mode_label) options_mode_layout.addWidget(self.options_mode_combobox) self.options_zone_layout.addLayout(options_mode_layout) self.option_source_widget = OptionsFromSourceWidget( label_width=LABEL_WIDTH, status_bar=self.status_bar) self.options_a_widget = OptionsAWidget(label_width=LABEL_WIDTH, status_bar=self.status_bar) self.options_mode_combobox.currentIndexChanged.connect( self.change_options_mode_signal) self.change_options_mode_signal(self.options_mode) options_zone.setLayout(self.options_zone_layout) # Add files button and final structures --------------------------- add_file_button = QPushButton("Add Files") add_file_button.clicked.connect(self.click_add_files) controls_layout.addWidget(select_zone) controls_layout.addWidget(options_zone) controls_layout.addWidget(add_file_button) # image preview --------------------------------------------------- image_prev_layout = QVBoxLayout() image_prev_layout.setContentsMargins(0, 0, 4, 0) self.IMG_PREVIEW_WIDTH = 256 self.img_label = QLabel() self.img_label.setAlignment(Qt.AlignCenter) self.select_text = "Click item to preview" self.last_created_pix_path = "" self.img_label.setText(self.select_text) image_prev_layout.addWidget(self.img_label) # slider for the preview scale self.img_scale_slider = QSlider() self.img_scale_slider.setMinimum(6) self.img_scale_slider.setMaximum(2048) self.img_scale_slider.setValue(self.IMG_PREVIEW_WIDTH) self.img_scale_slider.setOrientation(Qt.Horizontal) self.img_scale_slider.valueChanged.connect( self.change_scale_slider_signal) image_prev_layout.addWidget(self.img_scale_slider) self._update_preview() layout.addLayout(image_prev_layout) layout.addWidget(self.list_view) layout.addLayout(controls_layout) self.setLayout(layout) self.update_status_combobox = True def _pillow_to_pixmap(self, img): if img.mode == "RGB": r, g, b = img.split() img = Image.merge("RGB", (b, g, r)) elif img.mode == "RGBA": r, g, b, a = img.split() img = Image.merge("RGBA", (b, g, r, a)) elif img.mode == "L": img = img.convert("RGBA") img2 = img.convert("RGBA") data = img2.tobytes("raw", "RGBA") qim = QImage(data, img.size[0], img.size[1], QImage.Format_ARGB32) pixmap = QPixmap.fromImage(qim) return pixmap def _update_preview(self): self.img_label.setMinimumWidth(self.IMG_PREVIEW_WIDTH) self.img_label.setMaximumWidth( max(self.IMG_PREVIEW_WIDTH, self.img_scale_slider.width())) if len(self.last_created_pix_path) > 0: img = Image.open(self.last_created_pix_path) img_pix = self._pillow_to_pixmap(img) img_pix = img_pix.scaled( QSize(self.IMG_PREVIEW_WIDTH, self.IMG_PREVIEW_WIDTH), Qt.KeepAspectRatio) self.img_label.setPixmap(img_pix) def _set_preview(self, img_path): if img_path is None: self.last_created_pix_path = "" self.img_label.setText(self.select_text) else: if img_path != self.last_created_pix_path: self.last_created_pix_path = img_path self._update_preview() def _add_items_to_mode_combobox(self, combobox): combobox.addItem("From Source") combobox.addItem("A4") # combobox.addItem("A5") # combobox.addItem("A6") # combobox.addItem("Letter") def _get_filtered_string(self, string): to_return_array = [] for s in string: if s.isdigit(): to_return_array.append(s) return "".join(to_return_array) def get_images_to_save(self): path_array = [] for i in range(self.list_view.count()): path_array.append(self.list_view.item(i).get_data()) return path_array def get_image_parameters(self): # return as dictionary if self.options_mode == 0: return { "mode": 0, "pixels": self.option_source_widget.get_pixel_value(), "margin": self.option_source_widget.get_margin_value(), "background": self.option_source_widget.get_background_value() } else: return { "mode": self.options_mode, "align": self.options_a_widget.get_align_value(), "margin": self.options_a_widget.get_margin_value(), "background": self.options_a_widget.get_background_value() } def add_items(self, array): added_names = [] for a in array: new_name = os.path.basename(a) new_item = ImageListItem(new_name, a) added_names.append(new_name) self.list_view.addItem(new_item) self.status_bar.showMessage("Add items: " + ", ".join(added_names)) def change_scale_slider_signal(self, value): self.IMG_PREVIEW_WIDTH = value self._update_preview() self.status_bar.showMessage("Set preview scale to " + str(value)) def click_item_signal(self, item): pass # self._set_preview(item.get_data()) # self.status_bar.showMessage("Select " + str(item.text())) def _get_first_new_index(self, current, last): for v in current: if v not in last: return v return current[0] def change_selection_signal(self): if len(self.list_view.selectedItems()) == 0: self._set_preview(None) # nothing selected else: selected_indexes = [ self.list_view.indexFromItem(sel).row() for sel in self.list_view.selectedItems() ] item = self.list_view.item( self._get_first_new_index(selected_indexes, self.last_selected_items)) self._set_preview(item.get_data()) self.status_bar.showMessage("Select " + str(item.text())) self.last_selected_items = selected_indexes def change_options_mode_signal(self, index): self.options_mode = index if self.options_mode == 0: self.options_zone_layout.removeWidget(self.options_a_widget) self.options_a_widget.setParent(None) self.options_zone_layout.addWidget(self.option_source_widget) else: self.options_zone_layout.removeWidget(self.option_source_widget) self.option_source_widget.setParent(None) self.options_zone_layout.addWidget(self.options_a_widget) if self.update_status_combobox: self.status_bar.showMessage( "Set combine mode to \"" + self.options_mode_combobox.itemText(index) + "\"") def resizeEvent(self, size): # self._update_preview() pass def click_move_up(self): selected = self.list_view.selectedItems() selected_indexes = [ self.list_view.indexFromItem(sel).row() for sel in selected ] selected_indexes.sort() if len(selected_indexes) > 0 and selected_indexes[0] > 0: for index in selected_indexes: prev_item = self.list_view.takeItem(index - 1) self.list_view.insertItem(index, prev_item) self.status_bar.showMessage("Move " + str(len(selected_indexes)) + " items") else: self.status_bar.showMessage("Nothing to move") def click_move_down(self): selected = self.list_view.selectedItems() selected_indexes = [ self.list_view.indexFromItem(sel).row() for sel in selected ] selected_indexes.sort() sel_count = len(selected_indexes) if len(selected_indexes) > 0 and selected_indexes[ sel_count - 1] < self.list_view.count() - 1: for i_index in range(sel_count): next_item = self.list_view.takeItem( selected_indexes[sel_count - i_index - 1] + 1) self.list_view.insertItem( selected_indexes[sel_count - i_index - 1], next_item) self.status_bar.showMessage("Move " + str(len(selected_indexes)) + " items") else: self.status_bar.showMessage("Nothing to move") def click_invert(self): selected = self.list_view.selectedItems() selected_indexes = [] for sel in selected: selected_indexes.append(self.list_view.indexFromItem(sel).row()) total_indexes = [i for i in range(self.list_view.count())] new_indexes = [] for i in total_indexes: if i not in selected_indexes: new_indexes.append(i) self.list_view.clearSelection() for i in new_indexes: self.list_view.item(i).setSelected(True) self.status_bar.showMessage("Invert selection: " + str(new_indexes)) def click_delete(self): selected = self.list_view.selectedItems() delete_names = [] for s in selected: s_index = self.list_view.indexFromItem(s).row() del_item = self.list_view.takeItem(s_index) delete_names.append(del_item.text()) if len(delete_names) == 0: self.status_bar.showMessage("Nothing to delete") else: self.status_bar.showMessage("Delete items: " + ", ".join(delete_names)) def click_add_files(self): files_dialog = QFileDialog() files_dialog.setNameFilter("Images (*.jpg *.jpeg *.bmp *.png *.tiff)") files_dialog.setFileMode(QFileDialog.ExistingFiles) if files_dialog.exec_(): files = files_dialog.selectedFiles() self.add_items(files)
class ImportWindow(QDialog): def __init__(self): super(ImportWindow, self).__init__() self.setContentsMargins(5, 5, 5, 5) self._gamesdata = [] self._platforms = [] self._regions = set() self._platformListPath = Path("data/vgdb/") self._platformList = [] for file in self._platformListPath.iterdir(): self._platformList.append(file.stem) self._lblSelect = QLabel("Select platforms to import from:") self._consoleList = QListWidget() self._consoleList.addItems(sorted(self._platformList)) self._consoleList.setSelectionMode(QAbstractItemView.MultiSelection) self._btnCancel = QPushButton("Cancel") self._btnCancel.clicked.connect(self.close) self._btnOK = QPushButton("OK") self._btnOK.clicked.connect(self._doImport) self._hboxOKCancel = QHBoxLayout() self._hboxOKCancel.addStretch(5) self._hboxOKCancel.addWidget(self._btnOK, 0) self._hboxOKCancel.addWidget(self._btnCancel, 1) self._vbox = QVBoxLayout() self._vbox.addWidget(self._lblSelect, 0) self._vbox.addWidget(self._consoleList, 1) self._vbox.addLayout(self._hboxOKCancel, 2) self.setLayout(self._vbox) self.setWindowTitle("Import games") self.setFixedSize(500, 280) self._center() def _center(self): """Centers window on screen""" qr = self.frameGeometry() cp = QDesktopWidget().availableGeometry().center() qr.moveCenter(cp) self.move(qr.topLeft()) def _doImport(self): platforms = [x.text() for x in self._consoleList.selectedItems()] proceed = QMessageBox.Ok if len(platforms) > 1: proceed = QMessageBox.warning( self, "Import warning", "Importing multiple platforms can take a long time.\n" "Are you sure you want to proceed?", QMessageBox.Cancel | QMessageBox.Ok, QMessageBox.Cancel) if proceed == QMessageBox.Ok: newData = [] for file in self._platformListPath.iterdir(): if file.stem in platforms: self._platforms.append(file.stem) newData.append(createGameData(file)) for lst in newData: for game in lst: self._gamesdata.append(game) self._regions.add(game["region"]) self.accept() def returnData(self): return self._gamesdata, self._platforms, self._regions
class VODCutter(QMainWindow): def __init__(self): QMainWindow.__init__(self) self.twitch_client_id = config.TWITCH_API_CLIENT_ID self.twitch_oauth_token = config.TWITCH_API_OAUTH_TOKEN self.twitch_interface = TwitchInterface( api_client_id=config.TWITCH_API_CLIENT_ID, api_oauth_token=config.TWITCH_API_OAUTH_TOKEN, browser_client_id=config.TWITCH_BROWSER_OAUTH_TOKEN, browser_oauth_token=config.TWITCH_BROWSER_OAUTH_TOKEN) self.vlc_interface = VLCInterface(config.VLC_PATH) self.loaded_video = None self.main_layout = QVBoxLayout() self.launch_vlc_btn = QPushButton("Launch VLC") self.info_layout = QGridLayout() self.file_picker_layout = QHBoxLayout() self.file_path_field = QLineEdit() self.file_browser_btn = QPushButton(text="...") self.file_picker_layout.addWidget(self.file_path_field) self.file_picker_layout.addWidget(self.file_browser_btn) vod_filepath_label = QLabel("VOD Filepath") id_twitch_label = QLabel("ID Twitch") created_at_label = QLabel("Created at") duration_label = QLabel("Duration") title_label = QLabel("Title") streamer_label = QLabel("Streamer") self.id_twitch_field = QLineEdit() self.created_at_field = QLineEdit() self.duration_field = QLineEdit() self.title_field = QLineEdit() self.streamer_field = QLineEdit() self.id_twitch_field.setEnabled(False) self.created_at_field.setEnabled(False) self.duration_field.setEnabled(False) self.title_field.setEnabled(False) self.streamer_field.setEnabled(False) self.info_layout.addWidget(vod_filepath_label, 0, 0) self.info_layout.addWidget(id_twitch_label, 1, 0) self.info_layout.addWidget(created_at_label, 2, 0) self.info_layout.addWidget(duration_label, 3, 0) self.info_layout.addWidget(title_label, 4, 0) self.info_layout.addWidget(streamer_label, 5, 0) self.info_layout.addLayout(self.file_picker_layout, 0, 1) self.info_layout.addWidget(self.id_twitch_field, 1, 1) self.info_layout.addWidget(self.created_at_field, 2, 1) self.info_layout.addWidget(self.duration_field, 3, 1) self.info_layout.addWidget(self.title_field, 4, 1) self.info_layout.addWidget(self.streamer_field, 5, 1) self.segments_create_btn = QPushButton("Import Chapters") self.download_thumbnails_btn = QPushButton("Download Thumbnails") self.download_chatlog_btn = QPushButton("Download Chat Log") self.segments_list = QListWidget() self.segments_add_btn = QPushButton(text="+") self.segments_delete_btn = QPushButton(text="-") self.jump_start_btn = QPushButton(text="Jump To Start") self.jump_end_btn = QPushButton(text="Jump To End") self.set_start_btn = QPushButton(text="Set Start") self.set_end_btn = QPushButton(text="Set End") self.split_btn = QPushButton(text="Split") self.process_selected_btn = QPushButton( text="Process Selected Segment") self.process_all_btn = QPushButton(text="Process All Segments") self.jump_layout = QHBoxLayout() self.jump_layout.addWidget(self.jump_start_btn) self.jump_layout.addWidget(self.jump_end_btn) self.set_layout = QHBoxLayout() self.set_layout.addWidget(self.set_start_btn) self.set_layout.addWidget(self.set_end_btn) self.main_layout.addWidget(self.launch_vlc_btn) self.main_layout.addLayout(self.file_picker_layout) self.main_layout.addLayout(self.info_layout) self.main_layout.addWidget(self.segments_create_btn) self.main_layout.addWidget(self.download_thumbnails_btn) self.main_layout.addWidget(self.download_chatlog_btn) self.main_layout.addWidget(self.segments_list) self.main_layout.addWidget(self.segments_add_btn) self.main_layout.addWidget(self.segments_delete_btn) self.main_layout.addLayout(self.jump_layout) self.main_layout.addLayout(self.set_layout) self.main_layout.addWidget(self.split_btn) self.main_layout.addWidget(self.process_selected_btn) self.main_layout.addWidget(self.process_all_btn) self.main_widget = QWidget() self.main_widget.setLayout(self.main_layout) self.setCentralWidget(self.main_widget) self.segments_list.itemDoubleClicked.connect( self.on_segments_list_doubleclick) self.jump_start_btn.clicked.connect(self.jump_to_segment_start) self.jump_end_btn.clicked.connect(self.jump_to_segment_end) self.set_start_btn.clicked.connect(self.set_segment_start) self.set_end_btn.clicked.connect(self.set_segment_end) self.download_thumbnails_btn.clicked.connect(self.download_thumbnails) self.segments_add_btn.clicked.connect(self.create_segment) self.segments_delete_btn.clicked.connect(self.delete_segment) self.split_btn.clicked.connect(self.split_selected_segment) self.launch_vlc_btn.clicked.connect(self.on_launch_vlc) self.file_path_field.returnPressed.connect(self.on_video_url_changed) self.file_browser_btn.clicked.connect(self.on_filebrowse_btn_click) self.process_selected_btn.clicked.connect( self.process_selected_segment) self.process_all_btn.clicked.connect(self.process_all_segments) def on_launch_vlc(self): self.vlc_interface.launch() def on_filebrowse_btn_click(self): filename = QFileDialog.getOpenFileName(self, "Select a video file") if filename[0]: self.set_video_file(filename[0]) def on_video_url_changed(self): self.set_video_file(self.file_path_field.text()) def on_segments_list_doubleclick(self, item): current_segment = item.get_segment() if current_segment: self.vlc_interface.set_current_time(int( current_segment.start_time)) def set_video_file(self, filepath=None): self.file_path_field.setText("" if filepath is None else filepath) if filepath: self.loaded_video = InputVideo() if re.search(r"^(?:/|[a-z]:[\\/])", filepath, re.I): file_url = "file://" + filepath self.loaded_video.is_local = True else: file_url = filepath if not self.loaded_video.is_local: streams = streamlink.streams(file_url) if streams: self.loaded_video.filepath = streams["best"].url else: self.loaded_video.filepath = file_url else: self.loaded_video.filepath = file_url try: self.update_twitch_metadatas() except requests.exceptions.ConnectionError: print("<!!> Can't connect to Twitch API.") try: self.vlc_interface.open_url(self.loaded_video.filepath) except requests.exceptions.ConnectionError: print("<!!> Can't connect to local VLC instance.") def get_twitch_id_from_filepath(self): filename = self.file_path_field.text() parsed_filename = re.search("([0-9]+)\.mp4$", filename, re.I) if parsed_filename: video_id = parsed_filename.group(1) return int(video_id) else: parsed_url = re.search("videos/([0-9]+)", filename, re.I) if parsed_url: video_id = parsed_url.group(1) return int(video_id) else: raise Exception( f"<!!> Can't find video Twitch id in video filename ({filename})" ) def create_segment_before(self, segment_obj): pass def create_segment_after(self, segment_obj): pass def update_twitch_metadatas(self): twitch_video_id = self.get_twitch_id_from_filepath() metadatas = self.twitch_interface.get_twitch_metadatas(twitch_video_id) self.loaded_video.metadatas = metadatas duration = parse_duration(metadatas["duration"]) self.id_twitch_field.setText(metadatas["id"]) self.created_at_field.setText(str(metadatas["created_at"])) self.duration_field.setText(format_time(duration.seconds)) self.title_field.setText(metadatas["title"]) self.streamer_field.setText(metadatas["user_login"]) for moment in self.twitch_interface.get_video_games_list( metadatas["id"]): s = Segment() s.name = f"{moment['description']} ({moment['type']})" s.start_time = moment['positionMilliseconds'] / 1000 s.end_time = (moment['positionMilliseconds'] + moment['durationMilliseconds']) / 1000 self.segments_list.addItem(SegmentListItem(s)) def create_segment(self): s = Segment() s.name = f"Segment {self.segments_list.count()}" s.start_time = 0 s.end_time = self.vlc_interface.get_duration() self.segments_list.addItem(SegmentListItem(s)) def delete_segment(self): for item in self.segments_list.selectedItems(): idx = self.segments_list.indexFromItem(item) item = self.segments_list.takeItem(idx.row()) del item def split_selected_segment(self): current_time = self.vlc_interface.get_current_time() for segment_item in self.segments_list.selectedItems(): current_segment = segment_item.get_segment() if current_segment: new_segment = segment_item.split( current_time, name="Splitted " + current_segment.name, split_mode=SPLIT_MODE.ABSOLUTE) self.segments_list.addItem(SegmentListItem(new_segment)) def get_selected_segments(self): return list( map(lambda item: item.get_segment(), self.segments_list.selectedItems())) def jump_to_segment_start(self): selected_segments = self.get_selected_segments() if selected_segments: self.vlc_interface.set_current_time( math.floor(selected_segments[0].start_time)) def jump_to_segment_end(self): selected_segments = self.get_selected_segments() if selected_segments: self.vlc_interface.set_current_time( math.floor(selected_segments[0].end_time)) def set_segment_start(self): current_time = self.vlc_interface.get_current_time() selected_segments = self.segments_list.selectedItems() if selected_segments: selected_segments[0].get_segment().start_time = current_time selected_segments[0].update() def set_segment_end(self): current_time = self.vlc_interface.get_current_time() selected_segments = self.segments_list.selectedItems() if selected_segments: selected_segments[0].get_segment().end_time = current_time selected_segments[0].update() def process_selected_segment(self): for segment in self.get_selected_segments(): self.process_segment(segment) def process_all_segments(self): for idx in range(self.segments_list.count()): segment_item = self.segments_list.item(idx) self.process_segment(segment_item.get_segment()) def process_segment(self, segment_obj): if not self.loaded_video: raise Exception("<!!> No video loaded") video_id = self.loaded_video.metadatas.get("id", None) created_at = self.loaded_video.metadatas.get("created_at", None) user_login = self.loaded_video.metadatas.get("user_login", None) if not (video_id and created_at and user_login): raise Exception("<!!> Missing video metadatas") created_at_timestamp = int(datetime.datetime.timestamp(created_at)) if self.loaded_video.is_local: cmd = f'ffmpeg -i "{self.loaded_video.filepath}" -ss {segment_obj.start_time} -to {segment_obj.end_time} -c:v copy -c:a copy "{user_login}_{created_at_timestamp}_{video_id}.mp4"' else: cmd = f'streamlink -f --hls-start-offset {format_time(segment_obj.start_time)} --hls-duration {format_time(segment_obj.end_time - segment_obj.start_time)} --player-passthrough hls "{self.loaded_video.filepath}" best -o "{user_login}_{created_at_timestamp}_{video_id}.mp4"' print(cmd) os.system(cmd) def download_thumbnails(self): twitch_video_id_str = self.id_twitch_field.text() if twitch_video_id_str: thumbnails_manifest_url = self.twitch_interface.get_video_thumbnails_manifest_url( int(twitch_video_id_str)) thumbnails_manifest, images_url_list = self.twitch_interface.get_thumbnails_url_from_manifest( thumbnails_manifest_url) for img in images_url_list: r = requests.get(images_url_list[img]) fp = open(img, "wb") fp.write(r.content) fp.close()
class BrowserWin(QWidget): def __init__(self, *args, **kwargs): super(BrowserWin, self).__init__(*args, **kwargs) # parent Maya window self.setParent(mainWindow) self.setWindowFlags(Qt.Window) # Window settings self.setWindowTitle('AC_AssetBrowser') # Build window self.mainLayout = QVBoxLayout() self.btnLayout = QHBoxLayout() self.radioLayout = QHBoxLayout() # radio buttons load import self.radioLabel = QLabel("Action: ") self.importRadioBtn = QRadioButton("Import File") self.openRadioBtn = QRadioButton("Open File") self.saveRadioBtn = QRadioButton("Save File") # Find asset directories to load from and populate the drop down self.fileType = QComboBox() self.__populate_list(self.fileType) self.curr_cat = self.fileType.currentText() # list of assets in self.list self.fileList = QListWidget() self.fileList.setSelectionMode(QAbstractItemView.ExtendedSelection) self.__populate_list(self.fileList, directory=os.path.join(DIRECTORY, self.curr_cat)) self.fileName = QLineEdit() self.loadBtn = QPushButton("Load Asset") self.publishBtn = QPushButton("Publish") self.closeBtn = QPushButton("Close") # Add widgets to layouts self.radioLayout.addWidget(self.radioLabel) self.radioLayout.addWidget(self.importRadioBtn) self.radioLayout.addWidget(self.openRadioBtn) self.radioLayout.addWidget(self.saveRadioBtn) self.mainLayout.addLayout(self.radioLayout) self.mainLayout.addWidget(self.fileType) self.mainLayout.addWidget(self.fileList) self.mainLayout.addWidget(self.fileName) self.btnLayout.addWidget(self.loadBtn) self.btnLayout.addWidget(self.publishBtn) self.btnLayout.addWidget(self.closeBtn) self.mainLayout.addLayout(self.btnLayout) self.setLayout(self.mainLayout) # Set state of widgets self.importRadioBtn.toggle() self.fileName.setPlaceholderText("file_name") self.fileName.setEnabled(False) self.publishBtn.setEnabled(False) # Signals self.fileType.currentIndexChanged.connect(self.selectionChanged) self.loadBtn.clicked.connect(self.loadBtnCmd) self.publishBtn.clicked.connect(self.publishBtnCmd) self.closeBtn.clicked.connect(self.closeBtnCmd) self.importRadioBtn.toggled.connect(self.onImportToggled) self.openRadioBtn.toggled.connect(self.onOpenToggled) self.saveRadioBtn.toggled.connect(self.onSaveToggled) def __populate_list(self, destination, directory=DIRECTORY): _dirs = os.listdir(directory) _items = [_dir for _dir in _dirs] return destination.addItems(_items) def selectionChanged(self): self.curr_cat = self.fileType.currentText() self.fileList.clear() self.__populate_list(self.fileList, directory=os.path.join(DIRECTORY, self.curr_cat)) def loadBtnCmd(self): if self.importRadioBtn.isChecked(): selected_files = self.fileList.selectedItems() for _file in selected_files: asset_file = os.path.join(DIRECTORY, self.curr_cat, _file.text()) cmds.file(asset_file, i=True) elif self.openRadioBtn.isChecked(): selected_file = self.fileList.currentItem() asset_file = os.path.join(DIRECTORY, self.curr_cat, selected_file.text()) cmds.file(asset_file, o=True, force=True) else: print("Did you mean to publish this asset?") def publishBtnCmd(self): if self.saveRadioBtn.isChecked() and self.fileName.text() is not None: path_to_save = os.path.join(DIRECTORY, self.curr_cat, self.fileName.text()) cmds.file(rn="{}.ma".format(path_to_save)) cmds.file(save=True) self.fileList.clear() self.__populate_list(self.fileList, directory=os.path.join( DIRECTORY, self.curr_cat)) def closeBtnCmd(self): self.close() def onSaveToggled(self): items = self.fileList.selectedItems() for item in items: item.setSelected(False) self.fileName.setEnabled(not self.fileName.isEnabled()) self.publishBtn.setEnabled(not self.publishBtn.isEnabled()) def onImportToggled(self): if self.importRadioBtn.isChecked(): self.fileList.setSelectionMode(QAbstractItemView.ExtendedSelection) def onOpenToggled(self): if self.openRadioBtn.isChecked(): items = self.fileList.selectedItems() items.pop() for item in items: item.setSelected(False) self.fileList.setSelectionMode(QAbstractItemView.SingleSelection)
class FilterDock(QDockWidget): def __init__(self, platforms, regions, genres, years): super(FilterDock, self).__init__() # QDockWidget settings self.setAllowedAreas(Qt.BottomDockWidgetArea) self.setFeatures(QDockWidget.DockWidgetClosable) self.setFixedHeight(150) self.setVisible(False) self.setWindowTitle("Filter options") # The selected items for each widget are saved in a set-dictionary self._selections = defaultdict(set) # Widget settings # Platform widgets self._platformLabel = QLabel("Platform") self._platforms = QListWidget() self._platforms.addItems(platforms) self._platforms.setSelectionMode(QAbstractItemView.MultiSelection) self._platforms.setMaximumWidth(200) self._platformVBox = QVBoxLayout() self._platformVBox.addWidget(self._platformLabel, 0) self._platformVBox.addWidget(self._platforms, 1) # Region widgets self._regionLabel = QLabel("Region") self._regions = QListWidget() self._regions.addItems(regions) self._regions.setSelectionMode(QAbstractItemView.MultiSelection) self._regions.setMaximumWidth(200) self._regionVBox = QVBoxLayout() self._regionVBox.addWidget(self._regionLabel, 0) self._regionVBox.addWidget(self._regions, 1) # Genre widgets self._genreLabel = QLabel("Genre") self._genres = QListWidget() self._genres.addItems(genres) self._genres.setSelectionMode(QAbstractItemView.MultiSelection) self._genres.setMaximumWidth(300) self._genreVBox = QVBoxLayout() self._genreVBox.addWidget(self._genreLabel, 0) self._genreVBox.addWidget(self._genres, 1) # Year widgets self._yearLabel = QLabel("Year") self._years = QListWidget() self._years.addItems(years) self._years.setSelectionMode(QAbstractItemView.MultiSelection) self._years.setMaximumWidth(75) self._yearsVbox = QVBoxLayout() self._yearsVbox.addWidget(self._yearLabel, 0) self._yearsVbox.addWidget(self._years, 1) # Inventory widgets self._itemType = {1: "Game", 2: "Console", 3: "Accessory"} self._item = QCheckBox(self._itemType[1]) self._item.setTristate(True) self._item.setCheckState(Qt.PartiallyChecked) self._manual = QCheckBox("Manual") self._manual.setTristate(True) self._manual.setCheckState(Qt.PartiallyChecked) self._box = QCheckBox("Box") self._box.setTristate(True) self._box.setCheckState(Qt.PartiallyChecked) self._inventoryLabelsVBox = QVBoxLayout() self._inventorySelectionsVBox = QVBoxLayout() self._inventorySelectionsVBox.addStretch(3) self._inventorySelectionsVBox.addWidget(self._item, 0) self._inventorySelectionsVBox.addWidget(self._box, 1) self._inventorySelectionsVBox.addWidget(self._manual, 2) self._inventorySelectionsVBox.setAlignment(Qt.AlignLeft) self._haveHBox = QHBoxLayout() self._haveHBox.addLayout(self._inventoryLabelsVBox, 0) self._haveHBox.addLayout(self._inventorySelectionsVBox, 1) self._inventoryGroup = QGroupBox("Inventory") self._inventoryGroup.setMaximumWidth(120) self._inventoryGroup.setLayout(self._haveHBox) # Clear and Apply button widgets self._clearBtn = QPushButton("Clear selection") self._clearBtn.setMaximumSize(self._clearBtn.sizeHint()) self._clearBtn.clicked.connect(self._clearFilters) self._btnHBox = QHBoxLayout() self._btnHBox.setAlignment(Qt.AlignBottom | Qt.AlignRight) self._btnHBox.addWidget(self._clearBtn, 0) # General layout mainHBox = QHBoxLayout() mainHBox.setAlignment(Qt.AlignLeft) mainHBox.addLayout(self._platformVBox, 0) mainHBox.addLayout(self._regionVBox, 0) mainHBox.addLayout(self._genreVBox, 0) mainHBox.addLayout(self._yearsVbox, 0) mainHBox.addWidget(self._inventoryGroup, 0) mainHBox.addLayout(self._btnHBox, 0) mainWidget = QWidget() mainWidget.setLayout(mainHBox) self.setWidget(mainWidget) def _clearFilters(self): self._platforms.clearSelection() self._regions.clearSelection() self._genres.clearSelection() self._years.clearSelection() self._item.setCheckState(Qt.PartiallyChecked) self._box.setCheckState(Qt.PartiallyChecked) self._manual.setCheckState(Qt.PartiallyChecked) logger.info("Cleared all filters.") def getSelections(self): self._selections = defaultdict(set) # Reset selections if len(self._platforms.selectedItems()) > 0: platforms = [x.text() for x in self._platforms.selectedItems()] for platform in platforms: self._selections["Platform"].add(platform) if len(self._regions.selectedItems()) > 0: regions = [x.text() for x in self._regions.selectedItems()] for region in regions: self._selections["Region"].add(region) if len(self._genres.selectedItems()) > 0: genres = [x.text() for x in self._genres.selectedItems()] for genre in genres: self._selections["Genre"].add(genre) if len(self._years.selectedItems()) > 0: years = [x.text() for x in self._years.selectedItems()] for year in years: self._selections["Year"].add(year) if self._item.checkState() is not Qt.PartiallyChecked: self._selections[self._item.text()].add("Yes" if self._item.isChecked() else "No") if self._manual.checkState() is not Qt.PartiallyChecked: self._selections["Manual"].add("Yes" if self._manual.isChecked() else "No") if self._box.checkState() is not Qt.PartiallyChecked: self._selections["Box"].add("Yes" if self._box.isChecked() else "No") return self._selections def setItemType(self, itemType: int): if 0 < itemType < 4: if self._item.text() in self._selections: # Delete previous item entry so we don't search for the wrong type in the wrong table del self._selections[self._item.text()] self._item.setText(self._itemType[itemType]) def toggleVisibility(self): self.setVisible(False if self.isVisible() else True) def updatePlatforms(self, platforms): self._platforms.clear() self._platforms.addItems(platforms) logger.info("Updated platforms list.") def updateRegions(self, regions): self._regions.clear() self._regions.addItems(regions) logger.info("Updated regions list.") def updateGenres(self, genres): self._genres.clear() self._genres.addItems(genres) logger.info("Updated genres list.") def updateYears(self, years): self._years.clear() self._years.addItems(years) logger.info("Updated years list.")