class FindToolBar(QToolBar): find = QtCore.Signal(str, QWebEnginePage.FindFlags) def __init__(self): super(FindToolBar, self).__init__() self._line_edit = QLineEdit() self._line_edit.setClearButtonEnabled(True) self._line_edit.setPlaceholderText("Find...") self._line_edit.setMaximumWidth(300) self._line_edit.returnPressed.connect(self._find_next) self.addWidget(self._line_edit) self._previous_button = QToolButton() self._previous_button.setIcon( QIcon(':/qt-project.org/styles/commonstyle/images/up-32.png')) self._previous_button.clicked.connect(self._find_previous) self.addWidget(self._previous_button) self._next_button = QToolButton() self._next_button.setIcon( QIcon(':/qt-project.org/styles/commonstyle/images/down-32.png')) self._next_button.clicked.connect(self._find_next) self.addWidget(self._next_button) self._case_sensitive_checkbox = QCheckBox('Case Sensitive') self.addWidget(self._case_sensitive_checkbox) self._hideButton = QToolButton() self._hideButton.setShortcut(QKeySequence(Qt.Key_Escape)) self._hideButton.setIcon( QIcon(':/qt-project.org/styles/macstyle/images/closedock-16.png')) self._hideButton.clicked.connect(self.hide) self.addWidget(self._hideButton) def focus_find(self): self._line_edit.setFocus() def _emit_find(self, backward): needle = self._line_edit.text().strip() if needle: flags = QWebEnginePage.FindFlags() if self._case_sensitive_checkbox.isChecked(): flags |= QWebEnginePage.FindCaseSensitively if backward: flags |= QWebEnginePage.FindBackward self.find.emit(needle, flags) def _find_next(self): self._emit_find(False) def _find_previous(self): self._emit_find(True)
class SearchBar(QWidget): QueryLaunched = Signal(str) StopPressed = Signal() def __init__(self, parent=None): super().__init__(parent) self._query_edit = QLineEdit() self._query_edit.setClearButtonEnabled(True) self._query_edit.setPlaceholderText("Type you query here") self._query_edit.returnPressed.connect(self._HandleReturnPressed) self._query_edit.addAction(QIcon(icons.SEARCH), QLineEdit.LeadingPosition) self._kill_button = QToolButton() self._kill_button.setIcon(QIcon(icons.STOP)) self._kill_button.setAutoRaise(True) self._kill_button.setMinimumSize(31, 31) self._kill_button.setEnabled(False) self._kill_button.clicked.connect(self.StopPressed) layout = QHBoxLayout() layout.addWidget(self._kill_button) layout.addWidget(self._query_edit) layout.setContentsMargins(0, 0, 0, 0) self.setLayout(layout) self.setFocusProxy(self._query_edit) def Clear(self): self._query_edit.clear() def SetStopEnabled(self, bool_): self._kill_button.setEnabled(bool_) def SetQueryEditEnabled(self, bool_): self._query_edit.setEnabled(bool_) def SetQuery(self, query): self._query_edit.setText(query) def LaunchQuery(self, query): self._query_edit.setText(query) self._HandleReturnPressed() def _HandleReturnPressed(self): query = " ".join(self._query_edit.text().split()) if query == "": return self.QueryLaunched.emit(query)
class MainWindow(QMainWindow): def __init__(self): super(MainWindow, self).__init__() centralWidget = QWidget() self.setCentralWidget(centralWidget) layout = QFormLayout(centralWidget) textLayout = QHBoxLayout() self.text = QLineEdit('Hello, PySide2') self.text.setClearButtonEnabled(True) textLayout.addWidget(self.text) self.sayButton = QPushButton('Say') textLayout.addWidget(self.sayButton) self.text.returnPressed.connect(self.sayButton.animateClick) self.sayButton.clicked.connect(self.say) layout.addRow('Text:', textLayout) self.voiceCombo = QComboBox() layout.addRow('Voice:', self.voiceCombo) self.volumeSlider = QSlider(Qt.Horizontal) self.volumeSlider.setMinimum(0) self.volumeSlider.setMaximum(100) self.volumeSlider.setValue(100) layout.addRow('Volume:', self.volumeSlider) self.engine = None engineNames = QTextToSpeech.availableEngines() if len(engineNames) > 0: engineName = engineNames[0] self.engine = QTextToSpeech(engineName) self.engine.stateChanged.connect(self.stateChanged) self.setWindowTitle( 'QTextToSpeech Example ({})'.format(engineName)) self.voices = [] for voice in self.engine.availableVoices(): self.voices.append(voice) self.voiceCombo.addItem(voice.name()) else: self.setWindowTitle('QTextToSpeech Example (no engines available)') self.sayButton.setEnabled(False) def say(self): self.sayButton.setEnabled(False) self.engine.setVoice(self.voices[self.voiceCombo.currentIndex()]) self.engine.setVolume(float(self.volumeSlider.value()) / 100) self.engine.say(self.text.text()) def stateChanged(self, state): if (state == QTextToSpeech.State.Ready): self.sayButton.setEnabled(True)
class Browser_Toolbar(QToolBar): def __init__(self): QToolBar.__init__(self) self.back = QPushButton("back") self.next = QPushButton("next") self.reload = QPushButton("reload") self.url_box = QLineEdit(self) self.url_box.setClearButtonEnabled(True) self.addWidget(self.back) self.addWidget(self.next) self.addWidget(self.reload) self.addWidget(self.url_box)
class MainWindow(QMainWindow): def __init__(self): super(MainWindow, self).__init__() # Initialize main widget centralWidget = QWidget() self.setCentralWidget(centralWidget) layout = QFormLayout(centralWidget) # QLineEdit example textLayout = QHBoxLayout() self.text = QLineEdit('Hello, PySide2') self.text.setClearButtonEnabled(True) textLayout.addWidget(self.text) self.btn = QPushButton('Say') textLayout.addWidget(self.btn) self.text.returnPressed.connect(self.btn.animateClick) self.btn.clicked.connect(self.say) layout.addRow('Text:', textLayout) # QComboBox example self.combo = QComboBox() self.combo.addItems(items) layout.addRow('Item:', self.combo) self.combo.currentIndexChanged.connect(self.selected) # QSlide example self.slider = QSlider(Qt.Horizontal) self.slider.setMinimum(0) self.slider.setMaximum(100) self.slider.setValue(100) self.slider.valueChanged.connect(self.slide) layout.addRow('Volume:', self.slider) # QLabel example self.label = QLabel() self.label.setText('') layout.addRow('Result:', self.label) # button event handler def say(self): text = self.text.text() self.label.setText(text) # combobox event handler def selected(self, index): self.label.setText(items[index]) # slider event handler def slide(self, value): self.label.setText(str(value))
class FindToolBar(QToolBar): find = QtCore.Signal(str, QWebEnginePage.FindFlags) def __init__(self): super(FindToolBar, self).__init__() self._line_edit = QLineEdit() self._line_edit.setClearButtonEnabled(True) self._line_edit.setPlaceholderText("Find...") self._line_edit.setMaximumWidth(300) self._line_edit.returnPressed.connect(self._find_next) self.addWidget(self._line_edit) self._previous_button = QToolButton() self._previous_button.setIcon(QIcon(':/qt-project.org/styles/commonstyle/images/up-32.png')) self._previous_button.clicked.connect(self._find_previous) self.addWidget(self._previous_button) self._next_button = QToolButton() self._next_button.setIcon(QIcon(':/qt-project.org/styles/commonstyle/images/down-32.png')) self._next_button.clicked.connect(self._find_next) self.addWidget(self._next_button) self._case_sensitive_checkbox = QCheckBox('Case Sensitive') self.addWidget(self._case_sensitive_checkbox) self._hideButton = QToolButton() self._hideButton.setShortcut(QKeySequence(Qt.Key_Escape)) self._hideButton.setIcon(QIcon(':/qt-project.org/styles/macstyle/images/closedock-16.png')) self._hideButton.clicked.connect(self.hide) self.addWidget(self._hideButton) def focus_find(self): self._line_edit.setFocus() def _emit_find(self, backward): needle = self._line_edit.text().strip() if needle: flags = QWebEnginePage.FindFlags() if self._case_sensitive_checkbox.isChecked(): flags |= QWebEnginePage.FindCaseSensitively if backward: flags |= QWebEnginePage.FindBackward self.find.emit(needle, flags) def _find_next(self): self._emit_find(False) def _find_previous(self): self._emit_find(True)
class MainWindow(QMainWindow): """Provides the parent window that includes the BookmarkWidget, BrowserTabWidget, and a DownloadWidget, to offer the complete web browsing experience.""" def __init__(self): super(MainWindow, self).__init__() self.setWindowTitle('PySide2 tabbed browser Example') self._tab_widget = BrowserTabWidget(create_main_window_with_browser) self._tab_widget.enabled_changed.connect(self._enabled_changed) self._tab_widget.download_requested.connect(self._download_requested) self.setCentralWidget(self._tab_widget) self.connect(self._tab_widget, QtCore.SIGNAL("url_changed(QUrl)"), self.url_changed) self._bookmark_dock = QDockWidget() self._bookmark_dock.setWindowTitle('Bookmarks') self._bookmark_widget = BookmarkWidget() self._bookmark_widget.open_bookmark.connect(self.load_url) self._bookmark_widget.open_bookmark_in_new_tab.connect( self.load_url_in_new_tab) self._bookmark_dock.setWidget(self._bookmark_widget) self.addDockWidget(Qt.LeftDockWidgetArea, self._bookmark_dock) self._find_tool_bar = None self._actions = {} self._create_menu() self._tool_bar = QToolBar() self.addToolBar(self._tool_bar) for action in self._actions.values(): if not action.icon().isNull(): self._tool_bar.addAction(action) self._addres_line_edit = QLineEdit() self._addres_line_edit.setClearButtonEnabled(True) self._addres_line_edit.returnPressed.connect(self.load) self._tool_bar.addWidget(self._addres_line_edit) self._zoom_label = QLabel() self.statusBar().addPermanentWidget(self._zoom_label) self._update_zoom_label() self._bookmarksToolBar = QToolBar() self.addToolBar(Qt.TopToolBarArea, self._bookmarksToolBar) self.insertToolBarBreak(self._bookmarksToolBar) self._bookmark_widget.changed.connect(self._update_bookmarks) self._update_bookmarks() def _update_bookmarks(self): self._bookmark_widget.populate_tool_bar(self._bookmarksToolBar) self._bookmark_widget.populate_other(self._bookmark_menu, 3) def _create_menu(self): file_menu = self.menuBar().addMenu("&File") exit_action = QAction(QIcon.fromTheme("application-exit"), "E&xit", self, shortcut="Ctrl+Q", triggered=qApp.quit) file_menu.addAction(exit_action) navigation_menu = self.menuBar().addMenu("&Navigation") style_icons = ':/qt-project.org/styles/commonstyle/images/' back_action = QAction(QIcon.fromTheme( "go-previous", QIcon(style_icons + 'left-32.png')), "Back", self, shortcut=QKeySequence(QKeySequence.Back), triggered=self._tab_widget.back) self._actions[QWebEnginePage.Back] = back_action back_action.setEnabled(False) navigation_menu.addAction(back_action) forward_action = QAction(QIcon.fromTheme( "go-next", QIcon(style_icons + 'right-32.png')), "Forward", self, shortcut=QKeySequence(QKeySequence.Forward), triggered=self._tab_widget.forward) forward_action.setEnabled(False) self._actions[QWebEnginePage.Forward] = forward_action navigation_menu.addAction(forward_action) reload_action = QAction(QIcon(style_icons + 'refresh-32.png'), "Reload", self, shortcut=QKeySequence(QKeySequence.Refresh), triggered=self._tab_widget.reload) self._actions[QWebEnginePage.Reload] = reload_action reload_action.setEnabled(False) navigation_menu.addAction(reload_action) navigation_menu.addSeparator() new_tab_action = QAction("New Tab", self, shortcut='Ctrl+T', triggered=self.add_browser_tab) navigation_menu.addAction(new_tab_action) close_tab_action = QAction("Close Current Tab", self, shortcut="Ctrl+W", triggered=self._close_current_tab) navigation_menu.addAction(close_tab_action) edit_menu = self.menuBar().addMenu("&Edit") find_action = QAction("Find", self, shortcut=QKeySequence(QKeySequence.Find), triggered=self._show_find) edit_menu.addAction(find_action) edit_menu.addSeparator() undo_action = QAction("Undo", self, shortcut=QKeySequence(QKeySequence.Undo), triggered=self._tab_widget.undo) self._actions[QWebEnginePage.Undo] = undo_action undo_action.setEnabled(False) edit_menu.addAction(undo_action) redo_action = QAction("Redo", self, shortcut=QKeySequence(QKeySequence.Redo), triggered=self._tab_widget.redo) self._actions[QWebEnginePage.Redo] = redo_action redo_action.setEnabled(False) edit_menu.addAction(redo_action) edit_menu.addSeparator() cut_action = QAction("Cut", self, shortcut=QKeySequence(QKeySequence.Cut), triggered=self._tab_widget.cut) self._actions[QWebEnginePage.Cut] = cut_action cut_action.setEnabled(False) edit_menu.addAction(cut_action) copy_action = QAction("Copy", self, shortcut=QKeySequence(QKeySequence.Copy), triggered=self._tab_widget.copy) self._actions[QWebEnginePage.Copy] = copy_action copy_action.setEnabled(False) edit_menu.addAction(copy_action) paste_action = QAction("Paste", self, shortcut=QKeySequence(QKeySequence.Paste), triggered=self._tab_widget.paste) self._actions[QWebEnginePage.Paste] = paste_action paste_action.setEnabled(False) edit_menu.addAction(paste_action) edit_menu.addSeparator() select_all_action = QAction("Select All", self, shortcut=QKeySequence( QKeySequence.SelectAll), triggered=self._tab_widget.select_all) self._actions[QWebEnginePage.SelectAll] = select_all_action select_all_action.setEnabled(False) edit_menu.addAction(select_all_action) self._bookmark_menu = self.menuBar().addMenu("&Bookmarks") add_bookmark_action = QAction("&Add Bookmark", self, triggered=self._add_bookmark) self._bookmark_menu.addAction(add_bookmark_action) add_tool_bar_bookmark_action = QAction( "&Add Bookmark to Tool Bar", self, triggered=self._add_tool_bar_bookmark) self._bookmark_menu.addAction(add_tool_bar_bookmark_action) self._bookmark_menu.addSeparator() tools_menu = self.menuBar().addMenu("&Tools") download_action = QAction( "Open Downloads", self, triggered=DownloadWidget.open_download_directory) tools_menu.addAction(download_action) window_menu = self.menuBar().addMenu("&Window") window_menu.addAction(self._bookmark_dock.toggleViewAction()) window_menu.addSeparator() zoom_in_action = QAction(QIcon.fromTheme("zoom-in"), "Zoom In", self, shortcut=QKeySequence(QKeySequence.ZoomIn), triggered=self._zoom_in) window_menu.addAction(zoom_in_action) zoom_out_action = QAction(QIcon.fromTheme("zoom-out"), "Zoom Out", self, shortcut=QKeySequence(QKeySequence.ZoomOut), triggered=self._zoom_out) window_menu.addAction(zoom_out_action) reset_zoom_action = QAction(QIcon.fromTheme("zoom-original"), "Reset Zoom", self, shortcut="Ctrl+0", triggered=self._reset_zoom) window_menu.addAction(reset_zoom_action) about_menu = self.menuBar().addMenu("&About") about_action = QAction("About Qt", self, shortcut=QKeySequence( QKeySequence.HelpContents), triggered=qApp.aboutQt) about_menu.addAction(about_action) def add_browser_tab(self): return self._tab_widget.add_browser_tab() def _close_current_tab(self): if self._tab_widget.count() > 1: self._tab_widget.close_current_tab() else: self.close() def close_event(self, event): main_windows.remove(self) event.accept() def load(self): url_string = self._addres_line_edit.text().strip() if url_string: self.load_url_string(url_string) def load_url_string(self, url_s): url = QUrl.fromUserInput(url_s) if (url.isValid()): self.load_url(url) def load_url(self, url): self._tab_widget.load(url) def load_url_in_new_tab(self, url): self.add_browser_tab().load(url) def url_changed(self, url): self._addres_line_edit.setText(url.toString()) def _enabled_changed(self, web_action, enabled): action = self._actions[web_action] if action: action.setEnabled(enabled) def _add_bookmark(self): index = self._tab_widget.currentIndex() if index >= 0: url = self._tab_widget.url() title = self._tab_widget.tabText(index) icon = self._tab_widget.tabIcon(index) self._bookmark_widget.add_bookmark(url, title, icon) def _add_tool_bar_bookmark(self): index = self._tab_widget.currentIndex() if index >= 0: url = self._tab_widget.url() title = self._tab_widget.tabText(index) icon = self._tab_widget.tabIcon(index) self._bookmark_widget.add_tool_bar_bookmark(url, title, icon) def _zoom_in(self): new_zoom = self._tab_widget.zoom_factor() * 1.5 if (new_zoom <= WebEngineView.maximum_zoom_factor()): self._tab_widget.set_zoom_factor(new_zoom) self._update_zoom_label() def _zoom_out(self): new_zoom = self._tab_widget.zoom_factor() / 1.5 if (new_zoom >= WebEngineView.minimum_zoom_factor()): self._tab_widget.set_zoom_factor(new_zoom) self._update_zoom_label() def _reset_zoom(self): self._tab_widget.set_zoom_factor(1) self._update_zoom_label() def _update_zoom_label(self): percent = int(self._tab_widget.zoom_factor() * 100) self._zoom_label.setText("{}%".format(percent)) def _download_requested(self, item): # Remove old downloads before opening a new one for old_download in self.statusBar().children(): if type(old_download).__name__ == 'download_widget' and \ old_download.state() != QWebEngineDownloadItem.DownloadInProgress: self.statusBar().removeWidget(old_download) del old_download item.accept() download_widget = download_widget(item) download_widget.removeRequested.connect( self._remove_download_requested, Qt.QueuedConnection) self.statusBar().addWidget(download_widget) def _remove_download_requested(self): download_widget = self.sender() self.statusBar().removeWidget(download_widget) del download_widget def _show_find(self): if self._find_tool_bar is None: self._find_tool_bar = FindToolBar() self._find_tool_bar.find.connect(self._tab_widget.find) self.addToolBar(Qt.BottomToolBarArea, self._find_tool_bar) else: self._find_tool_bar.show() self._find_tool_bar.focus_find() def write_bookmarks(self): self._bookmark_widget.write_bookmarks()
class AddPerson(QWidget): def __init__(self, parent): QWidget.__init__(self) self.setWindowTitle("Add person") self.setWindowIcon(QIcon("assets/icons/icon.ico")) self.setGeometry(450, 150, 400, 250) # self.setFixedSize(self.size()) self.Parent = parent self.filePathName = "" self.UI() self.show() def UI(self): self.widgets() self.layouts() def widgets(self): # Top layout widgets self.addPersonImg = QLabel() self.img = QPixmap('assets/icons/edit-item.png') self.addPersonImg.setPixmap(self.img) self.addPersonImg.setAlignment(Qt.AlignCenter) self.titleText = QLabel("ADD PERSON") self.titleText.setObjectName("add_person_title_txt") self.titleText.setAlignment(Qt.AlignCenter) # Bottom layout widgets self.firstNameEntry = QLineEdit() self.firstNameEntry.setClearButtonEnabled(True) self.lastNameEntry = QLineEdit() self.lastNameEntry.setClearButtonEnabled(True) self.titleEntry = QLineEdit() self.titleEntry.setClearButtonEnabled(True) self.phoneEntry = QLineEdit() self.phoneEntry.setClearButtonEnabled(True) self.emailEntry = QLineEdit() self.emailEntry.setClearButtonEnabled(True) self.locationEntry = QLineEdit() self.locationEntry.setClearButtonEnabled(True) emplTypes = ["Employee", "Contractor", "Subcontractor"] self.employmentTypeEntry = QComboBox() self.employmentTypeEntry.addItems(emplTypes) self.employmentTypeEntry.setEditable(True) self.attachPhotoBtn = QPushButton("Attach photo") self.attachPhotoBtn.clicked.connect(self.funcAttachFiles) self.addPersonBtn = QPushButton("Add person") self.addPersonBtn.clicked.connect(self.addPerson) self.cancelBtn = QPushButton("Cancel") self.cancelBtn.clicked.connect(self.closeWindow) def layouts(self): self.mainLayout = QVBoxLayout() self.topLayout = QHBoxLayout() self.bottomLayout = QFormLayout() self.bottomLayout.setVerticalSpacing(20) self.bottomBtnLayout = QHBoxLayout() # Put elements into frames for visual distinction self.topFrame = QFrame() self.bottomFrame = QFrame() # Add widgets to top layout # self.topLayout.addWidget(self.addPersonImg) self.topLayout.addWidget(self.titleText) self.topFrame.setLayout(self.topLayout) # Add widgets to middle layout self.bottomLayout.addRow(QLabel("First name: "), self.firstNameEntry) self.bottomLayout.addRow(QLabel("Last name: "), self.lastNameEntry) self.bottomLayout.addRow(QLabel("Title: "), self.titleEntry) self.bottomLayout.addRow(QLabel("Phone: "), self.phoneEntry) self.bottomLayout.addRow(QLabel("Email: "), self.emailEntry) self.bottomLayout.addRow(QLabel("Location: "), self.locationEntry) self.bottomLayout.addRow(QLabel("Employment type: "), self.employmentTypeEntry) self.bottomLayout.addRow(QLabel(""), self.attachPhotoBtn) self.bottomBtnLayout.addWidget(self.cancelBtn) self.bottomBtnLayout.addItem(QSpacerItem(200, 5, QSizePolicy.Minimum, QSizePolicy.Expanding)) self.bottomBtnLayout.addWidget(self.addPersonBtn) self.bottomBtnLayout.setAlignment(Qt.AlignBottom) self.bottomLayout.addRow(self.bottomBtnLayout) self.bottomFrame.setLayout(self.bottomLayout) # Add frames to main layout self.mainLayout.addWidget(self.topFrame) self.mainLayout.addWidget(self.bottomFrame) self.setLayout(self.mainLayout) @Slot() def closeWindow(self): self.close() @Slot() def addPerson(self): firstName = self.firstNameEntry.text() lastName = self.lastNameEntry.text() title = self.titleEntry.text() phone = self.phoneEntry.text() email = self.emailEntry.text() location = self.locationEntry.text() emplType = self.employmentTypeEntry.currentText() # If user selected a file to attach, rename the file and copy it to media folder # Else set the path variables to empty strings to avoid problems with db write if self.filePathName != "": self.newFilePath = ShCopy2(self.filePathName, self.attachedFilePath) im = Image.open(self.filePathName) im_resized = self.crop_max_square(im).resize((800, 800), Image.LANCZOS) im_resized.save(self.attachedResizedFilePath) im_square = self.crop_max_square(im).resize((60, 60), Image.LANCZOS) im_thumb = self.mask_circle_transparent(im_square, 60, 60, 2) im_thumb.save(self.attachedThumbnailPath) else: self.attachedFilePath = "" self.attachedResizedFilePath = "" self.attachedThumbnailPath = "" if (firstName and lastName and title and phone and email and location and emplType != ""): try: query = "INSERT INTO people (person_first_name, person_last_name, person_title, person_phone," \ "person_email, person_location, person_empl_type, photo_original_path, photo_resized_path, " \ "thumbnail_path) " \ "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" db.cur.execute(query, (firstName, lastName, title, phone, email, location, emplType, self.attachedFilePath, self.attachedResizedFilePath, self.attachedThumbnailPath)) db.conn.commit() self.Parent.funcDisplayPeople() QMessageBox.information(self, "Info", "Member has been added") self.close() except: QMessageBox.information(self, "Info", "Member has not been added") else: QMessageBox.information(self, "Info", "Fields cannot be empty") @Slot() def funcAttachFiles(self): self.filePathName = QFileDialog.getOpenFileName(self, "Attach file...", "/", "Image files (*.png *.jpeg *.jpg)")[0] if osPath.isfile(self.filePathName): fileName, fileExt = osPath.splitext(self.filePathName) if fileExt == '.jpg' or fileExt == '.jpeg' or fileExt == '.png': date = datetime.now() randomSuffix = "".join(random.choice(string.ascii_lowercase) for i in range(15)) self.attachedFilePath = osPath.join("assets", "media", "people-media", "photos", ("{:%d%b%Y_%Hh%Mm}".format(date) + randomSuffix + fileExt)) self.attachedResizedFilePath = osPath.join("assets", "media", "people-media", "photos_resized", ("{:%d%b%Y_%Hh%Mm}".format(date) + randomSuffix + "_resized" + fileExt)) self.attachedThumbnailPath = osPath.join("assets", "media", "people-media", "photos_thumbnails", ("{:%d%b%Y_%Hh%Mm}".format(date) + randomSuffix + "_resized.png")) QMessageBox.information(self, "Info", "File attached successfully") else: QMessageBox.information(self, "Info", "Wrong file type!") else: QMessageBox.information(self, "Info", "No file selected") ################ Image processing functions ########################################## def crop_center(self, pil_img, crop_width, crop_height): img_width, img_height = pil_img.size fill_color = 'rgba(255, 255, 255, 1)' if pil_img.mode in ('RGBA', 'LA'): background = Image.new(pil_img.mode[:-1], pil_img.size, fill_color) background.paste(pil_img, pil_img.split()[-1]) image = background return pil_img.crop(((img_width - crop_width) // 2, (img_height - crop_height) // 2, (img_width + crop_width) // 2, (img_height + crop_height) // 2)) # Crop the largest possible square from a rectangle def crop_max_square(self, pil_img): return self.crop_center(pil_img, min(pil_img.size), min(pil_img.size)) # crop a square image into a circular image def mask_circle_transparent(self, pil_img, crop_width, crop_height, blur_radius, offset=0): img_width, img_height = pil_img.size pil_img.crop(((img_width - crop_width) // 2, (img_height - crop_height) // 2, (img_width + crop_width) // 2, (img_height + crop_height) // 2)) offset = blur_radius * 2 + offset mask = Image.new("L", pil_img.size, 0) draw = ImageDraw.Draw(mask) draw.ellipse((offset, offset, pil_img.size[0] - offset, pil_img.size[1] - offset), fill=255) mask = mask.filter(ImageFilter.GaussianBlur(blur_radius)) result = pil_img.copy() result.putalpha(mask) return result
class Artigence(QMainWindow): def __init__(self): super(Artigence, self).__init__() # Basic Settings self.setGeometry(300, 200, 682, 422) self.setMinimumSize(QSize(682, 422)) self.setMaximumSize(QSize(682, 422)) self.setWindowIcon(QIcon("arti.PNG")) self.setWindowTitle("Artigence Home") # Color Scheme self.palette = QPalette() self.palette.setColor(self.palette.Window, QColor('#000000')) self.palette.setColor(self.palette.WindowText, QColor('#FFFFFF')) self.setPalette(self.palette) self.light_palette = QPalette() self.light_palette.setColor(self.light_palette.Window, QColor('#FFFFFF')) self.light_palette.setColor(self.light_palette.WindowText, QColor('#000000')) # Setting MenuBar self.menubar = QMenuBar(self) self.menubar.setGeometry(0, 0, 682, 21) self.date_menu = QMenu(self.menubar) self.date_menu.setTitle(str(datetime.now().strftime('%d-%m-%Y'))) self.theme_menu = QMenu(self.menubar) self.theme_menu.setTitle('Theme') self.dark_theme = QAction('Dark Theme') self.dark_theme.setShortcut(QKeySequence('Ctrl+Shift+D')) self.theme_menu.addAction(self.dark_theme) self.dark_theme.triggered.connect(lambda: self.dark()) self.light_theme = QAction('Light Theme') self.light_theme.setShortcut(QKeySequence('Ctrl+Shift+L')) self.theme_menu.addAction(self.light_theme) self.light_theme.triggered.connect(lambda: self.light()) self.app_menu = QMenu(self.menubar) self.app_menu.setTitle('Apps') self.calculator_menu = QAction('Calculator') self.calculator_menu.setShortcut(QKeySequence('Alt+C')) self.app_menu.addAction(self.calculator_menu) self.calculator_menu.triggered.connect(lambda: self.calculator_func()) self.game_menu = QAction('GameHub') self.game_menu.setShortcut(QKeySequence('Alt+G')) self.app_menu.addAction(self.game_menu) self.game_menu.triggered.connect(lambda: self.games_func()) self.music_menu = QAction('Muse (Music)') self.music_menu.setShortcut(QKeySequence('Alt+M')) self.app_menu.addAction(self.music_menu) self.music_menu.triggered.connect(lambda: self.music_func()) self.news_menu = QAction('News') self.news_menu.setShortcut(QKeySequence('Alt+E')) self.app_menu.addAction(self.news_menu) self.news_menu.triggered.connect(lambda: self.news_func()) self.notepad_menu = QAction('Notepad') self.notepad_menu.setShortcut(QKeySequence('Alt+N')) self.app_menu.addAction(self.notepad_menu) self.notepad_menu.triggered.connect(lambda: self.notepad_func()) self.pronunciator = QAction('Pronunciator') self.pronunciator.setShortcut(QKeySequence('Alt+P')) self.app_menu.addAction(self.pronunciator) self.pronunciator.triggered.connect(lambda: self.pronunciator_func()) self.translate_menu = QAction('Translate') self.translate_menu.setShortcut(QKeySequence('Alt+T')) self.app_menu.addAction(self.translate_menu) self.translate_menu.triggered.connect(lambda: self.translate_func()) self.weather_menu = QAction('Weather') self.weather_menu.setShortcut(QKeySequence('Alt+W')) self.app_menu.addAction(self.weather_menu) self.weather_menu.triggered.connect(lambda: self.weather_func()) self.setMenuBar(self.menubar) self.menubar.addAction(self.date_menu.menuAction()) self.menubar.addAction(self.theme_menu.menuAction()) self.menubar.addAction(self.app_menu.menuAction()) # Creating Widgets self.query = QLineEdit(self) self.query.setGeometry(QRect(20, 30, 451, 41)) self.query.setMinimumSize(QSize(451, 41)) self.query.setMaximumSize(QSize(451, 41)) self.query.setPlaceholderText("Enter your Query Here:") self.query.setFont(QFont('Roboto', 16)) self.query.setClearButtonEnabled(True) self.update = QPushButton(self) self.update.setGeometry(QRect(491, 30, 171, 41)) self.update.setMinimumSize(QSize(1, 1)) self.update.setMaximumSize(QSize(171, 51)) self.update.setText("What's New in the Updates?") self.update.setCursor(QCursor(Qt.PointingHandCursor)) self.suggestions = QLabel(self) self.suggestions.setGeometry(QRect(20, 220, 111, 31)) self.suggestions.setMinimumSize(QSize(111, 31)) self.suggestions.setMaximumSize(QSize(111, 31)) self.suggestions.setText("Suggestions:") self.suggestions.setFont(QFont('Roboto', 14)) self.chrome = QPushButton(self) self.chrome.setGeometry(QRect(20, 260, 91, 31)) self.chrome.setCursor(QCursor(Qt.PointingHandCursor)) self.chrome.setText('Open Chrome') self.games = QPushButton(self) self.games.setGeometry(QRect(420, 260, 91, 31)) self.games.setCursor(QCursor(Qt.PointingHandCursor)) self.games.setText('Games') self.cmd = QPushButton(self) self.cmd.setGeometry(QRect(160, 260, 91, 31)) self.cmd.setCursor(QCursor(Qt.PointingHandCursor)) self.cmd.setText('Open Cmd') self.joke = QPushButton(self) self.joke.setGeometry(QRect(160, 310, 91, 31)) self.joke.setCursor(QCursor(Qt.PointingHandCursor)) self.joke.setText('Joke Please!!') self.music = QPushButton(self) self.music.setGeometry(QRect(290, 260, 91, 31)) self.music.setCursor(QCursor(Qt.PointingHandCursor)) self.music.setText('Music') self.youtube = QPushButton(self) self.youtube.setGeometry(QRect(290, 310, 91, 31)) self.youtube.setCursor(QCursor(Qt.PointingHandCursor)) self.youtube.setText('Youtube') self.time = QPushButton(self) self.time.setGeometry(QRect(20, 310, 91, 31)) self.time.setCursor(QCursor(Qt.PointingHandCursor)) self.time.setText('Tell Time') self.weather = QPushButton(self) self.weather.setGeometry(QRect(420, 310, 91, 31)) self.weather.setCursor(QCursor(Qt.PointingHandCursor)) self.weather.setText('Weather') self.calculator = QPushButton(self) self.calculator.setGeometry(QRect(550, 260, 101, 31)) self.calculator.setCursor(QCursor(Qt.PointingHandCursor)) self.calculator.setText('Calculator') self.wikipedia = QPushButton(self) self.wikipedia.setGeometry(QRect(550, 310, 101, 31)) self.wikipedia.setCursor(QCursor(Qt.PointingHandCursor)) self.wikipedia.setText('India Wikipedia') self.news = QPushButton(self) self.news.setGeometry(QRect(20, 360, 91, 31)) self.news.setCursor(QCursor(Qt.PointingHandCursor)) self.news.setText('Latest News') self.meaning = QPushButton(self) self.meaning.setGeometry(QRect(420, 360, 231, 31)) self.meaning.setCursor(QCursor(Qt.PointingHandCursor)) self.meaning.setText('Meaning of Obsolete (or any word)') self.harry_potter = QPushButton(self) self.harry_potter.setGeometry(QRect(290, 360, 91, 31)) self.harry_potter.setCursor(QCursor(Qt.PointingHandCursor)) self.harry_potter.setText('Harry Potter') self.translate = QPushButton(self) self.translate.setGeometry(QRect(160, 360, 91, 31)) self.translate.setCursor(QCursor(Qt.PointingHandCursor)) self.translate.setText('Open Translate') self.line = QFrame(self) self.line.setGeometry(QRect(20, 200, 661, 16)) self.line.setFrameShape(QFrame.HLine) self.line.setFrameShadow(QFrame.Sunken) self.label = QLabel(self) self.label.setGeometry(QRect(20, 100, 631, 91)) self.label.setFont(QFont('Roboto', 12)) self.label.setTextFormat(Qt.AutoText) self.label.setWordWrap(True) self.wish() # Making the Widgets Functional self.query.returnPressed.connect(lambda: self.on_enter()) self.query.returnPressed.connect(lambda: self.clear_text()) self.update.clicked.connect(lambda: self.update_func()) self.music.clicked.connect(lambda: self.music_func()) self.games.clicked.connect(lambda: self.games_func()) self.calculator.clicked.connect(lambda: self.calculator_func()) self.weather.clicked.connect(lambda: self.weather_func()) self.news.clicked.connect(lambda: self.news_func()) self.translate.clicked.connect(lambda: self.translate_func()) self.time.clicked.connect(lambda: self.time_func()) self.joke.clicked.connect(lambda: self.joke_func()) self.youtube.clicked.connect(lambda: self.youtube_func()) self.wikipedia.clicked.connect(lambda: self.wikipedia_func()) self.chrome.clicked.connect(lambda: self.chrome_func()) self.cmd.clicked.connect(lambda: self.cmd_func()) self.meaning.clicked.connect(lambda: self.meaning_func()) self.harry_potter.clicked.connect(lambda: self.potter_func()) def pronunciator_func(self): self.speak('Opening Pronunciator') from pronunciator import Pronunciator self.pronunciator_win = Pronunciator() self.pronunciator_win.show() def pong_func(self): import pong def notepad_func(self): self.speak('Opening Notepad') from notepad import Notepad self.notepad_win = Notepad() self.notepad_win.show() def update_func(self): os.startfile('Each Version Updates.txt') def translate_func(self): self.speak( 'Opening Translate\nPlease Wait as opening Translate may take up to 4-5 seconds' ) from translate import Translate self.translate_win = Translate() self.translate_win.show() def games_func(self): self.speak('Opening GameHub') from games import GameHub self.game_win = GameHub() self.game_win.show() def weather_func(self): self.speak('Opening Weather.') from weather import Weather self.weather_win = Weather() self.weather_win.show() def music_func(self): self.speak('Opening Muse') from music import Music self.music_win = Music() self.music_win.show() def calculator_func(self): self.speak('Opening Calculator.') from calculator import Calculator self.calculator_win = Calculator() self.calculator_win.show() def news_func(self): self.speak('Opening News.') from news import News self.news_win = News() self.news_win.show() self.speak( 'Welcome to News.\nThese are the latest international headlines according to BBC News Network.' ) def chrome_func(self): try: chrome_path = 'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe' os.startfile(chrome_path) self.speak('Opening Chrome.') except Exception: self.speak( 'No Google Chrome installation found on the host device.') def cmd_func(self): cmd_path = 'C:\\Windows\\system32\\cmd.exe' os.startfile(cmd_path) self.speak('Opening Command Prompt.') def time_func(self): question = 'time' app_id = 'LLQ4QY-A7K3LEL4T8' client = wolframalpha.Client(app_id) res = client.query(question) answer = next(res.results).text self.speak(answer) def joke_func(self): self.speak(pyjokes.get_joke()) def youtube_func(self): webbrowser.open('https://www.youtube.com') self.speak('Opening Youtube.') def wikipedia_func(self): try: self.speak('Searching Wikipedia. Please Wait...') query = 'India'.replace('wikipedia', '') result = wikipedia.summary(query, sentences=1) self.speak('According to Wikipedia...') self.speak(result) except Exception as e: self.speak(e) def meaning_func(self): question = 'obsolete' app_id = 'LLQ4QY-A7K3LEL4T8' client = wolframalpha.Client(app_id) res = client.query(question) answer = next(res.results).text self.speak(answer) def potter_func(self): new = 2 google_url = "http://google.com/?#q=" webbrowser.open(google_url + 'Harry Potter', new=new) def clear_text(self): self.query.clear() def on_enter(self): user_query = self.query.text().lower() if 'wikipedia' in user_query: try: self.speak('Searching Wikipedia. Please Wait...') user_query = user_query.replace('wikipedia', '') result = wikipedia.summary(user_query, sentences=1) self.speak('According to Wikipedia...') self.speak(result) except Exception as e: self.speak('Please try again later.') self.speak(e) elif 'youtube' in user_query: webbrowser.open('https://www.youtube.com') self.speak('Opening Youtube.') elif 'google' in user_query: webbrowser.open('https://www.google.com/') self.speak('Opening Google.') elif 'chrome' in user_query: # You'll have to download google chrome first on your desktop/pc. try: chrome_path = 'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe' os.startfile(chrome_path) self.speak('Opening Chrome') except Exception: self.speak( 'No Google Chrome installation found on the host device.') elif 'cmd' in user_query: cmd_path = 'C:\\Windows\\system32\\cmd.exe' os.startfile(cmd_path) self.speak('Opening Command Prompt.') elif 'control panel' in user_query: cp_path = 'C:\\Windows\\system32\\control.exe' os.startfile(cp_path) self.speak('Opening Control Panel.') elif 'bye' in user_query or 'goodbye' in user_query or 'good night' in user_query or 'see you later' in user_query: self.speak(random.choice(self.bye)) sys.exit() elif 'hello' in user_query or 'hi' in user_query: self.speak(random.choice(self.hello)) elif 'joke' in user_query: self.speak(pyjokes.get_joke()) elif 'who are you' in user_query: self.speak('I am Artigence, your artificial intelligence.') elif 'map' in user_query or 'maps' in user_query: self.speak('Opening Google Maps.') webbrowser.open("https://www.google.com/maps") elif 'open calculator' in user_query or 'calculator' in user_query: self.calculator_func() elif 'news' in user_query: self.news_func() self.speak( 'Welcome to News.\nThese are the latest international headlines according to BBC News Network.' ) elif 'weather' in user_query: self.weather_func() elif 'games' in user_query: self.games_func() elif 'pronunciator' in user_query or 'pronounce' in user_query: self.pronunciator_func() elif 'translate' in user_query: self.translate_func() elif 'music' in user_query: self.music_func() elif 'notepad' in user_query: self.notepad_func() else: try: question = user_query app_id = 'LLQ4QY-A7K3LEL4T8' client = wolframalpha.Client(app_id) res = client.query(question) answer = next(res.results).text self.label.setText(answer) self.label.adjustSize() except: new = 2 google_url = "http://google.com/?#q=" query = user_query webbrowser.open(google_url + query, new=new) # The A.I. will speak through this function def speak(self, audio): self.engine = pyttsx3.init('sapi5') voices = self.engine.getProperty('voices') self.engine.setProperty('voice', voices[1].id) self.engine.setProperty('rate', 165) self.label.setText(audio) self.engine.say(audio) self.engine.runAndWait() self.label.clear() def wish(self): hour = int(datetime.now().hour) if 0 <= hour < 12: self.speak('Good Morning.') elif 12 <= hour < 18: self.speak('Good Afternoon.') else: self.speak('Good Evening.') self.speak('I am Artigence.') self.speak('How may I help you today') hello = ['Kon\'nichiwa', 'Ciao', 'Hola', 'Bonjour', 'Hello', 'Hi', 'Hiya'] bye = [ 'Adios', 'Goodbye', 'Bye-Bye', 'See you next time.', 'Artigence Out.', 'It was nice talking to you sir. Have a nice day.' ] def dark(self): self.setPalette(self.palette) def light(self): self.setPalette(self.light_palette)
class FilterWidgetBase(QWidget): """Filter widget class.""" okPressed = Signal() cancelPressed = Signal() def __init__(self, parent): """Init class. Args: parent (QWidget) """ super().__init__(parent) # parameters self._filter_state = set() self._filter_empty_state = None self._search_text = '' self.search_delay = 200 # create ui elements self._ui_vertical_layout = QVBoxLayout(self) self._ui_list = QListView() self._ui_edit = QLineEdit() self._ui_edit.setPlaceholderText('Search') self._ui_edit.setClearButtonEnabled(True) self._ui_buttons = QDialogButtonBox(QDialogButtonBox.Cancel | QDialogButtonBox.Ok) self._ui_vertical_layout.addWidget(self._ui_edit) self._ui_vertical_layout.addWidget(self._ui_list) self._ui_vertical_layout.addWidget(self._ui_buttons) # add models self._search_timer = QTimer( ) # Used to limit search so it doesn't search when typing self._search_timer.setSingleShot(True) self._filter_model = None def connect_signals(self): self._ui_list.clicked.connect(self._filter_model._handle_index_clicked) self._search_timer.timeout.connect(self._filter_list) self._ui_edit.textChanged.connect(self._text_edited) self._ui_buttons.button(QDialogButtonBox.Ok).clicked.connect( self._apply_filter) self._ui_buttons.button(QDialogButtonBox.Cancel).clicked.connect( self._cancel_filter) def save_state(self): """Saves the state of the FilterCheckboxListModel.""" self._filter_state = self._filter_model.get_selected() if self._filter_model._show_empty: self._filter_empty_state = self._filter_model._empty_selected def reset_state(self): """Sets the state of the FilterCheckboxListModel to saved state.""" self._filter_model.set_selected(self._filter_state, self._filter_empty_state) def clear_filter(self): """Selects all items in FilterCheckBoxListModel.""" self._filter_model.reset_selection() self.save_state() def has_filter(self): """Returns true if any item is filtered in FilterCheckboxListModel false otherwise.""" return not self._filter_model._all_selected def set_filter_list(self, data): """Sets the list of items to filter.""" self._filter_state = list(data) self._filter_model.set_list(self._filter_state) def _apply_filter(self): """Apply current filter and save state.""" self._filter_model.apply_filter() self.save_state() self._ui_edit.setText('') self.okPressed.emit() def _cancel_filter(self): """Cancel current edit of filter and set the state to the stored state.""" self._filter_model.remove_filter() self.reset_state() self._ui_edit.setText('') self.cancelPressed.emit() def _filter_list(self): """Filter list with current text.""" self._filter_model.set_filter(self._search_text) def _text_edited(self, new_text): """Callback for edit text, starts/restarts timer. Start timer after text is edited, restart timer if text is edited before last time out. """ self._search_text = new_text self._search_timer.start(self.search_delay)
class Pronunciator(QMainWindow): def __init__(self): super(Pronunciator, self).__init__() self.setMaximumHeight(21) self.setMinimumWidth(200) self.setWindowTitle('Pronunciator') self.setWindowIcon(QIcon('arti.PNG')) palette = QPalette() palette.setColor(palette.Window, QColor('#000000')) palette.setColor(palette.WindowText, QColor('#FFFFFF')) self.setPalette(palette) centralWidget = QWidget() self.setCentralWidget(centralWidget) layout = QFormLayout(centralWidget) textLayout = QHBoxLayout() self.text = QLineEdit() self.text.setClearButtonEnabled(True) textLayout.addWidget(self.text) self.sayButton = QPushButton('Say') textLayout.addWidget(self.sayButton) self.text.returnPressed.connect(self.sayButton.animateClick) self.sayButton.clicked.connect(self.say) layout.addRow('Text:', textLayout) self.voiceCombo = QComboBox() layout.addRow('Voice:', self.voiceCombo) self.volumeSlider = QSlider(Qt.Horizontal) self.volumeSlider.setMinimum(0) self.volumeSlider.setMaximum(100) self.volumeSlider.setValue(100) layout.addRow('Volume:', self.volumeSlider) self.engine = None engineNames = QTextToSpeech.availableEngines() if len(engineNames) > 0: engineName = engineNames[0] self.engine = QTextToSpeech(engineName) self.engine.stateChanged.connect(self.stateChanged) self.voices = [] for voice in self.engine.availableVoices(): self.voices.append(voice) self.voiceCombo.addItem(voice.name()) else: self.setWindowTitle('No voices available') self.sayButton.setEnabled(False) def say(self): self.sayButton.setEnabled(False) self.engine.setVoice(self.voices[self.voiceCombo.currentIndex()]) self.engine.setVolume(float(self.volumeSlider.value()) / 100) self.engine.say(self.text.text()) def stateChanged(self, state): if state == QTextToSpeech.State.Ready: self.sayButton.setEnabled(True)
class MainWindow(QMainWindow): """Provides the parent window that includes the BookmarkWidget, BrowserTabWidget, and a DownloadWidget, to offer the complete web browsing experience.""" def __init__(self): super(MainWindow, self).__init__() self.setWindowTitle('PySide2 tabbed browser Example') self._tab_widget = BrowserTabWidget(create_main_window_with_browser) self._tab_widget.enabled_changed.connect(self._enabled_changed) self._tab_widget.download_requested.connect(self._download_requested) self.setCentralWidget(self._tab_widget) self.connect(self._tab_widget, QtCore.SIGNAL("url_changed(QUrl)"), self.url_changed) self._bookmark_dock = QDockWidget() self._bookmark_dock.setWindowTitle('Bookmarks') self._bookmark_widget = BookmarkWidget() self._bookmark_widget.open_bookmark.connect(self.load_url) self._bookmark_widget.open_bookmark_in_new_tab.connect(self.load_url_in_new_tab) self._bookmark_dock.setWidget(self._bookmark_widget) self.addDockWidget(Qt.LeftDockWidgetArea, self._bookmark_dock) self._find_tool_bar = None self._actions = {} self._create_menu() self._tool_bar = QToolBar() self.addToolBar(self._tool_bar) for action in self._actions.values(): if not action.icon().isNull(): self._tool_bar.addAction(action) self._addres_line_edit = QLineEdit() self._addres_line_edit.setClearButtonEnabled(True) self._addres_line_edit.returnPressed.connect(self.load) self._tool_bar.addWidget(self._addres_line_edit) self._zoom_label = QLabel() self.statusBar().addPermanentWidget(self._zoom_label) self._update_zoom_label() self._bookmarksToolBar = QToolBar() self.addToolBar(Qt.TopToolBarArea, self._bookmarksToolBar) self.insertToolBarBreak(self._bookmarksToolBar) self._bookmark_widget.changed.connect(self._update_bookmarks) self._update_bookmarks() def _update_bookmarks(self): self._bookmark_widget.populate_tool_bar(self._bookmarksToolBar) self._bookmark_widget.populate_other(self._bookmark_menu, 3) def _create_menu(self): file_menu = self.menuBar().addMenu("&File") exit_action = QAction(QIcon.fromTheme("application-exit"), "E&xit", self, shortcut = "Ctrl+Q", triggered=qApp.quit) file_menu.addAction(exit_action) navigation_menu = self.menuBar().addMenu("&Navigation") style_icons = ':/qt-project.org/styles/commonstyle/images/' back_action = QAction(QIcon.fromTheme("go-previous", QIcon(style_icons + 'left-32.png')), "Back", self, shortcut = QKeySequence(QKeySequence.Back), triggered = self._tab_widget.back) self._actions[QWebEnginePage.Back] = back_action back_action.setEnabled(False) navigation_menu.addAction(back_action) forward_action = QAction(QIcon.fromTheme("go-next", QIcon(style_icons + 'right-32.png')), "Forward", self, shortcut = QKeySequence(QKeySequence.Forward), triggered = self._tab_widget.forward) forward_action.setEnabled(False) self._actions[QWebEnginePage.Forward] = forward_action navigation_menu.addAction(forward_action) reload_action = QAction(QIcon(style_icons + 'refresh-32.png'), "Reload", self, shortcut = QKeySequence(QKeySequence.Refresh), triggered = self._tab_widget.reload) self._actions[QWebEnginePage.Reload] = reload_action reload_action.setEnabled(False) navigation_menu.addAction(reload_action) navigation_menu.addSeparator() new_tab_action = QAction("New Tab", self, shortcut = 'Ctrl+T', triggered = self.add_browser_tab) navigation_menu.addAction(new_tab_action) close_tab_action = QAction("Close Current Tab", self, shortcut = "Ctrl+W", triggered = self._close_current_tab) navigation_menu.addAction(close_tab_action) edit_menu = self.menuBar().addMenu("&Edit") find_action = QAction("Find", self, shortcut = QKeySequence(QKeySequence.Find), triggered = self._show_find) edit_menu.addAction(find_action) edit_menu.addSeparator() undo_action = QAction("Undo", self, shortcut = QKeySequence(QKeySequence.Undo), triggered = self._tab_widget.undo) self._actions[QWebEnginePage.Undo] = undo_action undo_action.setEnabled(False) edit_menu.addAction(undo_action) redo_action = QAction("Redo", self, shortcut = QKeySequence(QKeySequence.Redo), triggered = self._tab_widget.redo) self._actions[QWebEnginePage.Redo] = redo_action redo_action.setEnabled(False) edit_menu.addAction(redo_action) edit_menu.addSeparator() cut_action = QAction("Cut", self, shortcut = QKeySequence(QKeySequence.Cut), triggered = self._tab_widget.cut) self._actions[QWebEnginePage.Cut] = cut_action cut_action.setEnabled(False) edit_menu.addAction(cut_action) copy_action = QAction("Copy", self, shortcut = QKeySequence(QKeySequence.Copy), triggered = self._tab_widget.copy) self._actions[QWebEnginePage.Copy] = copy_action copy_action.setEnabled(False) edit_menu.addAction(copy_action) paste_action = QAction("Paste", self, shortcut = QKeySequence(QKeySequence.Paste), triggered = self._tab_widget.paste) self._actions[QWebEnginePage.Paste] = paste_action paste_action.setEnabled(False) edit_menu.addAction(paste_action) edit_menu.addSeparator() select_all_action = QAction("Select All", self, shortcut = QKeySequence(QKeySequence.SelectAll), triggered = self._tab_widget.select_all) self._actions[QWebEnginePage.SelectAll] = select_all_action select_all_action.setEnabled(False) edit_menu.addAction(select_all_action) self._bookmark_menu = self.menuBar().addMenu("&Bookmarks") add_bookmark_action = QAction("&Add Bookmark", self, triggered = self._add_bookmark) self._bookmark_menu.addAction(add_bookmark_action) add_tool_bar_bookmark_action = QAction("&Add Bookmark to Tool Bar", self, triggered = self._add_tool_bar_bookmark) self._bookmark_menu.addAction(add_tool_bar_bookmark_action) self._bookmark_menu.addSeparator() tools_menu = self.menuBar().addMenu("&Tools") download_action = QAction("Open Downloads", self, triggered = DownloadWidget.open_download_directory) tools_menu.addAction(download_action) window_menu = self.menuBar().addMenu("&Window") window_menu.addAction(self._bookmark_dock.toggleViewAction()) window_menu.addSeparator() zoom_in_action = QAction(QIcon.fromTheme("zoom-in"), "Zoom In", self, shortcut = QKeySequence(QKeySequence.ZoomIn), triggered = self._zoom_in) window_menu.addAction(zoom_in_action) zoom_out_action = QAction(QIcon.fromTheme("zoom-out"), "Zoom Out", self, shortcut = QKeySequence(QKeySequence.ZoomOut), triggered = self._zoom_out) window_menu.addAction(zoom_out_action) reset_zoom_action = QAction(QIcon.fromTheme("zoom-original"), "Reset Zoom", self, shortcut = "Ctrl+0", triggered = self._reset_zoom) window_menu.addAction(reset_zoom_action) about_menu = self.menuBar().addMenu("&About") about_action = QAction("About Qt", self, shortcut = QKeySequence(QKeySequence.HelpContents), triggered=qApp.aboutQt) about_menu.addAction(about_action) def add_browser_tab(self): return self._tab_widget.add_browser_tab() def _close_current_tab(self): if self._tab_widget.count() > 1: self._tab_widget.close_current_tab() else: self.close() def close_event(self, event): main_windows.remove(self) event.accept() def load(self): url_string = self._addres_line_edit.text().strip() if url_string: self.load_url_string(url_string) def load_url_string(self, url_s): url = QUrl.fromUserInput(url_s) if (url.isValid()): self.load_url(url) def load_url(self, url): self._tab_widget.load(url) def load_url_in_new_tab(self, url): self.add_browser_tab().load(url) def url_changed(self, url): self._addres_line_edit.setText(url.toString()) def _enabled_changed(self, web_action, enabled): action = self._actions[web_action] if action: action.setEnabled(enabled) def _add_bookmark(self): index = self._tab_widget.currentIndex() if index >= 0: url = self._tab_widget.url() title = self._tab_widget.tabText(index) icon = self._tab_widget.tabIcon(index) self._bookmark_widget.add_bookmark(url, title, icon) def _add_tool_bar_bookmark(self): index = self._tab_widget.currentIndex() if index >= 0: url = self._tab_widget.url() title = self._tab_widget.tabText(index) icon = self._tab_widget.tabIcon(index) self._bookmark_widget.add_tool_bar_bookmark(url, title, icon) def _zoom_in(self): new_zoom = self._tab_widget.zoom_factor() * 1.5 if (new_zoom <= WebEngineView.maximum_zoom_factor()): self._tab_widget.set_zoom_factor(new_zoom) self._update_zoom_label() def _zoom_out(self): new_zoom = self._tab_widget.zoom_factor() / 1.5 if (new_zoom >= WebEngineView.minimum_zoom_factor()): self._tab_widget.set_zoom_factor(new_zoom) self._update_zoom_label() def _reset_zoom(self): self._tab_widget.set_zoom_factor(1) self._update_zoom_label() def _update_zoom_label(self): percent = int(self._tab_widget.zoom_factor() * 100) self._zoom_label.setText("{}%".format(percent)) def _download_requested(self, item): # Remove old downloads before opening a new one for old_download in self.statusBar().children(): if type(old_download).__name__ == 'download_widget' and \ old_download.state() != QWebEngineDownloadItem.DownloadInProgress: self.statusBar().removeWidget(old_download) del old_download item.accept() download_widget = download_widget(item) download_widget.removeRequested.connect(self._remove_download_requested, Qt.QueuedConnection) self.statusBar().addWidget(download_widget) def _remove_download_requested(self): download_widget = self.sender() self.statusBar().removeWidget(download_widget) del download_widget def _show_find(self): if self._find_tool_bar is None: self._find_tool_bar = FindToolBar() self._find_tool_bar.find.connect(self._tab_widget.find) self.addToolBar(Qt.BottomToolBarArea, self._find_tool_bar) else: self._find_tool_bar.show() self._find_tool_bar.focus_find() def write_bookmarks(self): self._bookmark_widget.write_bookmarks()
class TestRunner(ToolInstance): def __init__(self, session, name): super().__init__(session, name) self.tool_window = MainToolWindow(self) self._build_ui() def _build_ui(self): """ ui should have: * table with a list of available tests and show results after they are done * way to filter tests * button to run tests """ layout = QVBoxLayout() # table to list test classes and the results self.table = QTableWidget() self.table.setColumnCount(2) self.table.setHorizontalHeaderLabels(["test", "result"]) self.table.horizontalHeader().setSectionResizeMode( 0, self.table.horizontalHeader().Interactive) self.table.setEditTriggers(QTableWidget.NoEditTriggers) self.table.horizontalHeader().setSectionResizeMode( 1, self.table.horizontalHeader().Stretch) self.table.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding) self.table.setSelectionBehavior(self.table.SelectRows) layout.insertWidget(0, self.table, 1) self.filter = QLineEdit() self.filter.setPlaceholderText("filter test names") self.filter.setClearButtonEnabled(True) self.filter.textChanged.connect(self.apply_filter) layout.insertWidget(1, self.filter, 0) self.run_button = QPushButton("run tests") self.run_button.clicked.connect(self.run_tests) self.run_button.setToolTip( "if no tests are selected on the table, run all tests\n" + "otherwise, run selected tests") layout.insertWidget(2, self.run_button) self.fill_table() self.table.resizeColumnToContents(0) self.tool_window.ui_area.setLayout(layout) self.tool_window.manage(None) def apply_filter(self, text=None): """filter table to only show tests matching text""" if text is None: text = self.filter.text() if text: text = text.replace("(", "\(") text = text.replace(")", "\)") m = QRegularExpression(text) m.setPatternOptions(QRegularExpression.CaseInsensitiveOption) if m.isValid(): m.optimize() filter = lambda row_num: m.match( self.table.item(row_num, 0).text()).hasMatch() else: return else: filter = lambda row: True for i in range(0, self.table.rowCount()): self.table.setRowHidden(i, not filter(i)) def fill_table(self): """adds test names to the table""" mgr = self.session.test_manager for name in mgr.tests.keys(): row = self.table.rowCount() self.table.insertRow(row) test_name = QTableWidgetItem() test_name.setData(Qt.DisplayRole, name) self.table.setItem(row, 0, test_name) def run_tests(self): """run the tests selected on the table and show the results""" from TestManager.commands.test import test test_list = [] use_selected = True for row in self.table.selectionModel().selectedRows(): if self.table.isRowHidden(row.row()): continue test_name = self.table.item(row.row(), 0).text() test_list.append(test_name) if not test_list: use_selected = False for i in range(0, self.table.rowCount()): if self.table.isRowHidden(i): continue test_name = self.table.item(i, 0).text() test_list.append(test_name) if not test_list: test_list = ["all"] results = test(self.session, test_list) cell_widgets = [] for name in results: widget = QWidget() widget_layout = QHBoxLayout(widget) widget_layout.setContentsMargins(0, 0, 0, 0) success_button = get_button("success") fail_button = get_button("fail") skip_button = get_button("skip") error_button = get_button("error") expected_fail_button = get_button("expected fail") unexpected_success_button = get_button("unexpected success") success_count = 0 fail_count = 0 error_count = 0 unexpected_success_count = 0 expected_fail_count = 0 skip_count = 0 success_tooltip = "Successes:\n" fail_tooltip = "Failed tests:\n" error_tooltip = "Errors during test:\n" unexpected_success_tooltip = "Unexpected successes:\n" expected_fail_tooltip = "Expected fails:\n" skip_tooltip = "Skipped tests:\n" for case in results[name]: result, msg = results[name][case] if result == "success": success_count += 1 success_tooltip += "%s.%s: %s\n" % ( case.__class__.__qualname__, case._testMethodName, msg) elif result == "fail": fail_count += 1 fail_tooltip += "%s.%s failed: %s\n" % ( case.__class__.__qualname__, case._testMethodName, msg) elif result == "error": error_count += 1 error_tooltip += "error during %s.%s: %s\n" % ( case.__class__.__qualname__, case._testMethodName, msg) elif result == "expected_failure": expected_fail_count += 1 expected_fail_tooltip += "intended failure during %s.%s: %s\n" % ( case.__class__.__qualname__, case._testMethodName, msg) elif result == "skip": skip_count += 1 skip_tooltip += "%s.%s\n" % (case.__class__.__qualname__, case._testMethodName) elif result == "unexpected_success": unexpected_success_count += 1 unexpected_success_tooltip += "%s.%s should not have worked, but did\n" % ( case.__class__.__qualname__, case._testMethodName) success_tooltip = success_tooltip.strip() fail_tooltip = fail_tooltip.strip() error_tooltip = error_tooltip.strip() expected_fail_tooltip = expected_fail_tooltip.strip() skip_tooltip = skip_tooltip.strip() unexpected_success_tooltip = unexpected_success_tooltip.strip() icon_count = 0 if success_count: success_button.setText("%i" % success_count) success_button.setToolTip(success_tooltip) success_button.clicked.connect( lambda *args, t_name=name, res=success_tooltip: self. tool_window.create_child_window( "successes for %s" % t_name, text=res, window_class=ResultsWindow, )) widget_layout.insertWidget(icon_count, success_button, 1) icon_count += 1 if fail_count: fail_button.setText("%i" % fail_count) fail_button.setToolTip(fail_tooltip) fail_button.clicked.connect( lambda *args, res=fail_tooltip: self.tool_window. create_child_window( "failures for %s" % name, text=res, window_class=ResultsWindow, )) widget_layout.insertWidget(icon_count, fail_button, 1) icon_count += 1 if error_count: error_button.setText("%i" % error_count) error_button.setToolTip(error_tooltip) error_button.clicked.connect( lambda *args, res=error_tooltip: self.tool_window. create_child_window( "errors for %s" % name, text=res, window_class=ResultsWindow, )) widget_layout.insertWidget(icon_count, error_button, 1) icon_count += 1 if unexpected_success_count: unexpected_success_button.setText("%i" % unexpected_success_count) unexpected_success_button.setToolTip( unexpected_success_tooltip) unexpected_success_button.clicked.connect( lambda *args, res=unexpected_success_tooltip: self. tool_window.create_child_window( "unexpected successes for %s" % name, text=res, window_class=ResultsWindow, )) widget_layout.insertWidget(icon_count, unexpected_success_button, 1) icon_count += 1 if expected_fail_count: expected_fail_button.setText("%i" % expected_fail_count) expected_fail_button.setToolTip(expected_fail_tooltip) expected_fail_button.clicked.connect( lambda *args, res=expected_fail_tooltip: self.tool_window. create_child_window( "expected failures for %s" % name, text=res, window_class=ResultsWindow, )) widget_layout.insertWidget(icon_count, expected_fail_button, 1) icon_count += 1 if skip_count: skip_button.setText("%i" % skip_count) skip_button.setToolTip(skip_tooltip) skip_button.clicked.connect( lambda *args, res=skip_tooltip: self.tool_window. create_child_window( "skipped tests for %s" % name, text=res, window_class=ResultsWindow, )) widget_layout.insertWidget(icon_count, skip_button, 1) cell_widgets.append(widget) widget_count = 0 if use_selected: for row in self.table.selectionModel().selectedRows(): if self.table.isRowHidden(row.row()): continue self.table.setCellWidget(row.row(), 1, cell_widgets[widget_count]) self.table.resizeRowToContents(row.row()) widget_count += 1 else: for i in range(0, self.table.rowCount()): if self.table.isRowHidden(i): continue self.table.setCellWidget(i, 1, cell_widgets[widget_count]) # self.table.resizeRowToContents(i) widget_count += 1
class ConnectDatabase(QDialog): def __init__(self, parent): super().__init__(parent) self.setWindowTitle("Connect Database") self.user_label = QLabel("Database User:"******"e.g. root") self.user.setDragEnabled(True) self.user.setFocus() self.user.setClearButtonEnabled(True) self.pw_label = QLabel("Database Password:"******"e.g. toor") self.password.setClearButtonEnabled(True) self.password.setEchoMode(QLineEdit.Password) self.db_name_label = QLabel("Database Name:") self.db_name = QLineEdit() self.db_name.setPlaceholderText("e.g. bank-db") self.db_name.setDragEnabled(True) self.db_name.setClearButtonEnabled(True) self.host_name_label = QLabel("Host Name:") self.host_name = QLineEdit() self.host_name.setPlaceholderText("e.g. localhost") self.host_name.setDragEnabled(True) self.host_name.setClearButtonEnabled(True) self.connect_bttn = QPushButton("Connect") self.connect_bttn.clicked.connect(self.connect) layout = QGridLayout() layout.addWidget(self.user_label, 0, 0) layout.addWidget(self.user, 0, 1) layout.addWidget(self.pw_label, 1, 0) layout.addWidget(self.password, 1, 1) layout.addWidget(self.db_name_label, 2, 0) layout.addWidget(self.db_name, 2, 1) layout.addWidget(self.host_name_label, 3, 0) layout.addWidget(self.host_name, 3, 1) layout.addWidget(self.connect_bttn, 4, 1) self.setLayout(layout) self.setModal(True) def connect(self): if os.path.exists("model/session/connected_dbs"): with open("model/session/connected_dbs", "rb") as sessions: db_sessions = pickle.load(sessions) else: db_sessions = [] is_connected = False for db in db_sessions: if db["db"] == self.db_name.text(): is_connected = True QMessageBox.critical(self, "Database Already Connected", "Database with the same name is already connected.", QMessageBox.Close) break if not is_connected: if len(self.user.text()) > 0 and len(self.password.text()) > 0 and len(self.db_name.text()) > 0 and len(self.host_name.text()) > 0: try: connection = mysql.connect( host=self.host_name.text(), user=self.user.text(), password=self.password.text(), db=self.db_name.text(), charset="utf8mb4", cursorclass=mysql.cursors.DictCursor ) try: db_sessions.append( { "index": len(db_sessions) + 1, "host": self.host_name.text(), "user": self.user.text(), "password": self.password.text(), "db": self.db_name.text() } ) QMessageBox.information(self, "Database connected. You rock!", "Hooray! Database successfully connected with Zephyrus.\n\nHint: Press Ctrl+R to refresh databases", QMessageBox.Ok) with open("model/session/connected_dbs", "wb") as sessions: pickle.dump(db_sessions, sessions) self.close() except mysql.Error as e: QMessageBox.critical(self, "Warning", "Incorrect database credentials, you might be bad at typing...", QMessageBox.Close) finally: connection.close() except RuntimeError as e: QMessageBox.critical(self, "Connection Error", "There are no active MySQL servers!", QMessageBox.Close) except mysql.OperationalError as e: QMessageBox.critical(self, "Connection Error", "Incorrect Credentials!", QMessageBox.Close) else: QMessageBox.critical(self, "Warning", "You didn't fill out all fields!", QMessageBox.Close)
class CommandWidget(TabWidgetExtension, QWidget): """Output for running queue""" # log state __log = False insertTextSignal = Signal(str, dict) updateCommandSignal = Signal(str) cliButtonsSateSignal = Signal(bool) cliValidateSignal = Signal(bool) resetSignal = Signal() def __init__(self, parent=None, proxyModel=None, controlQueue=None, log=None): super(CommandWidget, self).__init__(parent=parent, tabWidgetChild=self) self.__log = log self.__output = None self.__rename = None self.__tab = None # self.oCommand = MKVCommand() self.algorithm = None self.oCommand = MKVCommandParser() self.controlQueue = controlQueue self.parent = parent self.proxyModel = proxyModel self.model = proxyModel.sourceModel() self.outputWindow = QOutputTextWidget(self) self.log = log self._initControls() self._initUI() self._initHelper() def _initControls(self): # # command line # self.frmCmdLine = QFormLayout() btnPasteClipboard = QPushButtonWidget( Text.txt0164, function=lambda: qtRunFunctionInThread(self.pasteClipboard), margins=" ", toolTip=Text.txt0165, ) self.cmdLine = QLineEdit() self.cmdLine.setValidator( ValidateCommand(self, self.cliValidateSignal, log=self.log) ) self.frmCmdLine.addRow(btnPasteClipboard, self.cmdLine) self.frmCmdLine.setFieldGrowthPolicy(QFormLayout.AllNonFixedFieldsGrow) self.command = QWidget() self.command.setLayout(self.frmCmdLine) # # Button group definition # self.btnGroup = QGroupBox() self.btnGrid = QGridLayout() btnAddCommand = QPushButtonWidget( Text.txt0160, function=lambda: self.addCommand(JobStatus.Waiting), margins=" ", toolTip=Text.txt0161, ) btnRename = QPushButtonWidget( Text.txt0182, function=self.parent.renameWidget.setAsCurrentTab, margins=" ", toolTip=Text.txt0183, ) btnAddQueue = QPushButtonWidget( Text.txt0166, function=lambda: self.addCommand(JobStatus.AddToQueue), margins=" ", toolTip=Text.txt0167, ) btnStartQueue = QPushButtonWidget( Text.txt0126, function=self.parent.jobsQueue.run, margins=" ", toolTip=Text.txt0169, ) btnAnalysis = QPushButtonWidget( Text.txt0170, function=lambda: qtRunFunctionInThread( runAnalysis, command=self.cmdLine.text(), output=self.output, log=self.log, ), margins=" ", toolTip=Text.txt0171, ) btnShowCommands = QPushButtonWidget( Text.txt0172, function=lambda: qtRunFunctionInThread( showCommands, output=self.output, command=self.cmdLine.text(), oCommand=self.oCommand, log=self.log, ), margins=" ", toolTip=Text.txt0173, ) btnCheckFiles = QPushButtonWidget( Text.txt0174, function=lambda: qtRunFunctionInThread( checkFiles, output=self.output, command=self.cmdLine.text(), oCommand=self.oCommand, log=self.log, ), margins=" ", toolTip=Text.txt0175, ) btnClear = QPushButtonWidget( Text.txt0176, function=self.clearOutputWindow, margins=" ", toolTip=Text.txt0177, ) btnReset = QPushButtonWidget( Text.txt0178, function=self.reset, margins=" ", toolTip=Text.txt0179, ) self.btnGrid.addWidget(btnAddCommand, 0, 0) self.btnGrid.addWidget(btnRename, 0, 1) self.btnGrid.addWidget(btnAddQueue, 1, 0) self.btnGrid.addWidget(btnStartQueue, 1, 1) self.btnGrid.addWidget(HorizontalLine(), 2, 0, 1, 2) self.btnGrid.addWidget(btnAnalysis, 3, 0) self.btnGrid.addWidget(btnShowCommands, 3, 1) self.btnGrid.addWidget(btnCheckFiles, 4, 0) self.btnGrid.addWidget(HorizontalLine(), 5, 0, 1, 2) self.btnGrid.addWidget(btnClear, 6, 0) self.btnGrid.addWidget(btnReset, 6, 1) self.btnGroup.setLayout(self.btnGrid) self.btnGroupBox = QGroupBox() self.btnHBox = QHBoxLayout() self.lblAlgorithm = QLabelWidget( Text.txt0094, textSuffix=": ", ) self.rbZero = QRadioButton("0", self) self.rbOne = QRadioButton("1", self) self.rbTwo = QRadioButton("2", self) btnDefaultAlgorithm = QPushButtonWidget( Text.txt0092, function=self.setDefaultAlgorithm, margins=" ", toolTip=Text.txt0093, ) self.radioButtons = [self.rbZero, self.rbOne, self.rbTwo] self.btnHBox.addWidget(self.lblAlgorithm) self.btnHBox.addWidget(self.rbZero) self.btnHBox.addWidget(self.rbOne) self.btnHBox.addWidget(self.rbTwo) self.btnHBox.addWidget(btnDefaultAlgorithm) self.btnGroupBox.setLayout(self.btnHBox) def _initUI(self): grid = QGridLayout() grid.addWidget(self.command, 0, 0, 1, 2) grid.addWidget(self.btnGroupBox, 1, 0) grid.addWidget(self.btnGroup, 2, 0) grid.addWidget(self.outputWindow, 2, 1, 10, 1) self.setLayout(grid) def _initHelper(self): # # Signal interconnections # # local button state connect to related state self.parent.jobsQueue.addQueueItemSignal.connect( lambda: self.jobStartQueueState(True) ) self.parent.jobsQueue.queueEmptiedSignal.connect( lambda: self.jobStartQueueState(False) ) # job related self.parent.jobsQueue.runJobs.startSignal.connect(lambda: self.jobStatus(True)) self.parent.jobsQueue.runJobs.finishedSignal.connect( lambda: self.jobStatus(False) ) # map insertText signal to outputWidget one self.insertText = self.outputWindow.insertTextSignal # command self.updateCommandSignal.connect(self.updateCommand) self.cliButtonsSateSignal.connect(self.cliButtonsState) self.cliValidateSignal.connect(self.cliValidate) # # button state # # Command related # self.frmCmdLine.itemAt(0, QFormLayout.LabelRole).widget().setEnabled(False) self.cliButtonsState(False) self.btnGrid.itemAt(_Button.ANALYSIS).widget().setEnabled(False) # Clear buttons related self.btnGrid.itemAt(_Button.CLEAR).widget().setEnabled(False) self.btnGrid.itemAt(_Button.RESET).widget().setEnabled(False) # connect text windows textChanged to clearButtonState function self.outputWindow.textChanged.connect(self.clearButtonState) # connect command line textChanged to analysisButtonState function self.cmdLine.textChanged.connect(self.analysisButtonState) # Job Queue related self.btnGrid.itemAt(_Button.STARTQUEUE).widget().setEnabled(False) # Job Added to Queue self.parent.jobsQueue.addQueueItemSignal.connect(self.printJobIDAdded) # # Misc # self.cmdLine.setClearButtonEnabled(True) # button at end of line to clear it # Algorithm radio buttons self.rbZero.toggled.connect(lambda: self.toggledRadioButton(self.rbZero)) self.rbOne.toggled.connect(lambda: self.toggledRadioButton(self.rbOne)) self.rbTwo.toggled.connect(lambda: self.toggledRadioButton(self.rbTwo)) self.setDefaultAlgorithm() @classmethod def classLog(cls, setLogging=None): """ get/set logging at class level every class instance will log unless overwritten Args: setLogging (bool): - True class will log - False turn off logging - None returns current Value Returns: bool: returns the current value set """ if setLogging is not None: if isinstance(setLogging, bool): cls.__log = setLogging return cls.__log @property def log(self): """ class property can be used to override the class global logging setting Returns: bool: True if logging is enable False otherwise """ if self.__log is not None: return self.__log return CommandWidget.classLog() @log.setter def log(self, value): """set instance log variable""" if isinstance(value, bool) or value is None: self.__log = value # No variable used so for now use class log ValidateCommand.classLog(value) self.outputWindow.log = value @property def output(self): return self.__output @output.setter def output(self, value): self.__output = value @property def rename(self): return self.__rename @rename.setter def rename(self, value): if isinstance(value, object): self.__rename = value @Slot(list) def applyRename(self, renameFiles): if self.oCommand: self.oCommand.renameOutputFiles(renameFiles) @Slot(bool) def cliButtonsState(self, validateOK): """ cliButtonsState change enabled status for buttons related with command line Args: validateOK (bool): True to enable, False to disable """ for b in [ _Button.ADDCOMMAND, _Button.RENAME, _Button.ADDQUEUE, _Button.SHOWCOMMANDS, _Button.CHECKFILES, ]: button = self.btnGrid.itemAt(b).widget() button.setEnabled(validateOK) @Slot(bool) def cliValidate(self, validateOK): """ cliValidate Slot used by ValidateCommnad Args: validateOK (bool): True if command line is Ok. False otherwise. """ if validateOK: self.output.command.emit( "Command looks ok.\n", {LineOutput.AppendEnd: True} ) else: if self.cmdLine.text() != "": self.output.command.emit("Bad command.\n", {LineOutput.AppendEnd: True}) self.cliButtonsState(validateOK) self.updateObjCommnad(validateOK) @Slot(bool) def jobStartQueueState(self, state): if state and not isThreadRunning(config.WORKERTHREADNAME): self.btnGrid.itemAt(_Button.STARTQUEUE).widget().setEnabled(True) else: self.btnGrid.itemAt(_Button.STARTQUEUE).widget().setEnabled(False) @Slot(bool) def updateObjCommnad(self, valid): """Update the command object""" if valid: self.oCommand.command = self.cmdLine.text() if self.rename is not None: self.rename.setFilesSignal.emit(self.oCommand) self.rename.applyFileRenameSignal.connect(self.applyRename) else: self.oCommand.command = "" if self.rename is not None: self.rename.clear() @Slot(str) def updateCommand(self, command): """Update command input widget""" self.cmdLine.clear() self.cmdLine.setText(command) self.cmdLine.setCursorPosition(0) @Slot(int) def updateAlgorithm(self, algorithm): if 0 <= algorithm < len(self.radioButtons): self.radioButtons[algorithm].setChecked(True) @Slot(bool) def jobStatus(self, running): """ jobStatus receive Signals for job start/end Args: running (bool): True if job started. False if ended. """ if running: self.jobStartQueueState(False) palette = QPalette() color = checkColor( QColor(42, 130, 218), config.data.get(config.ConfigKey.DarkMode) ) palette.setColor(QPalette.WindowText, color) self.parent.jobsLabel.setPalette(palette) else: palette = QPalette() color = checkColor(None, config.data.get(config.ConfigKey.DarkMode)) palette.setColor(QPalette.WindowText, color) self.parent.jobsLabel.setPalette(palette) def addCommand(self, status): """ addCommand add command row in jobs table Args: status (JobStatus): Status for job to be added should be either JobStatus.Waiting or JobStatus.AddToQueue """ totalJobs = self.model.rowCount() command = self.cmdLine.text() # [cell value, tooltip, obj] data = [ ["", "", self.algorithm], [status, "Status code", None], [command, command, self.oCommand], ] self.model.insertRows(totalJobs, 1, data=data) self.cmdLine.clear() def analysisButtonState(self): """Set clear button state""" if self.cmdLine.text() != "": self.btnGrid.itemAt(_Button.ANALYSIS).widget().setEnabled(True) else: self.btnGrid.itemAt(_Button.ANALYSIS).widget().setEnabled(False) def clearButtonState(self): """Set clear button state""" if self.outputWindow.toPlainText() != "": self.btnGrid.itemAt(_Button.CLEAR).widget().setEnabled(True) else: self.btnGrid.itemAt(_Button.CLEAR).widget().setEnabled(False) def clearOutputWindow(self): """ clearOutputWindow clear the command output window """ language = config.data.get(config.ConfigKey.Language) bAnswer = False # Clear output window? title = "Clear output" msg = "¿" if language == "es" else "" msg += "Clear output window" + "?" bAnswer = yesNoDialog(self, msg, title) if bAnswer: self.outputWindow.clear() def printJobIDAdded(self, index): jobID = self.model.dataset[index.row(), index.column()] self.output.command.emit( f"Job: {jobID} added to Queue...\n", {LineOutput.AppendEnd: True} ) def pasteClipboard(self): """Paste clipboard to command QLineEdit""" clip = QApplication.clipboard().text() if clip: self.output.command.emit( "Checking command...\n", {LineOutput.AppendEnd: True} ) self.update() self.updateCommandSignal.emit(clip) def reset(self): """ reset program status """ language = config.data.get(config.ConfigKey.Language) if not isThreadRunning(config.WORKERTHREADNAME): language = config.data.get(config.ConfigKey.Language) bAnswer = False # Clear output window? title = "Reset" msg = "¿" if language == "es" else "" msg += "Reset Application" + "?" bAnswer = yesNoDialog(self, msg, title) if bAnswer: self.cmdLine.clear() self.outputWindow.clear() self.output.jobOutput.clear() self.output.errorOutput.clear() self.resetSignal.emit() else: messageBox(self, "Reset", "Jobs are running..") def resetButtonState(self): """Set clear button state""" if self.output.jobOutput.toPlainText() != "": self.btnGrid.itemAt(_Button.RESET).widget().setEnabled(True) else: self.btnGrid.itemAt(_Button.RESET).widget().setEnabled(False) def setDefaultAlgorithm(self): # # Algorithm # if config.data.get(config.ConfigKey.Algorithm) is not None: currentAlgorithm = config.data.get(config.ConfigKey.Algorithm) self.radioButtons[currentAlgorithm].setChecked(True) def setLanguage(self): """ setLanguage language use in buttons/labels to be called by MainWindow """ for index in range(self.frmCmdLine.rowCount()): widget = self.frmCmdLine.itemAt(index, QFormLayout.LabelRole).widget() if isinstance(widget, QPushButtonWidget): widget.setLanguage() # widget.setText(" " + _(widget.originalText) + " ") # widget.setToolTip(_(widget.toolTip)) for index in range(self.btnHBox.count()): widget = self.btnHBox.itemAt(index).widget() if isinstance( widget, ( QLabelWidget, QPushButtonWidget, ), ): widget.setLanguage() for index in range(self.btnGrid.count()): widget = self.btnGrid.itemAt(index).widget() if isinstance(widget, QPushButtonWidget): widget.setLanguage() # widget.setText(" " + _(widget.originalText) + " ") # widget.setToolTip(_(widget.toolTip)) def toggledRadioButton(self, rButton): for index, rb in enumerate(self.radioButtons): if rb.isChecked(): self.algorithm = index
class MainWindow(QMainWindow): def __init__(self): super(MainWindow, self).__init__() self.setWindowTitle('Voice Player') self.words = [] centralWidget = QWidget() self.setCentralWidget(centralWidget) layout = QFormLayout(centralWidget) self.readButton = QPushButton('Read CSV') self.readButton.clicked.connect(self.readCSV) layout.addRow('CSV File:', self.readButton) textLayout = QHBoxLayout() self.text = QLineEdit('') self.text.setClearButtonEnabled(True) textLayout.addWidget(self.text) self.sayButton = QPushButton('Say') textLayout.addWidget(self.sayButton) self.text.returnPressed.connect(self.sayButton.animateClick) self.sayButton.clicked.connect(self.say) layout.addRow('Text:', textLayout) self.voiceCombo = QComboBox() self.voiceCombo.currentIndexChanged.connect(self.indexChange) layout.addRow('Voice:', self.voiceCombo) self.volumeSlider = QSlider(Qt.Horizontal) self.volumeSlider.setMinimum(0) self.volumeSlider.setMaximum(100) self.volumeSlider.setValue(100) layout.addRow('Volume:', self.volumeSlider) self.engine = None engineNames = QTextToSpeech.availableEngines() if len(engineNames) > 0: engineName = engineNames[0] self.engine = QTextToSpeech(engineName) self.engine.stateChanged.connect(self.stateChanged) #self.setWindowTitle('QTextToSpeech Example ({})'.format(engineName)) #self.voices = [] #for voice in self.engine.availableVoices(): # self.voices.append(voice) # self.voiceCombo.addItem(voice.name()) self.voice = self.engine.availableVoices()[0] else: self.setWindowTitle('QTextToSpeech Example (no engines available)') self.sayButton.setEnabled(False) def indexChange(self, index): word = self.words[self.voiceCombo.currentIndex()] self.text.setText(word) def readCSV(self): fileDialog = QFileDialog(self) fileDialog.setNameFilters(['CSV File (*.csv)']) if fileDialog.exec_() == QDialog.Accepted: csvPath = fileDialog.selectedFiles()[0] self.words = [] with open(csvPath) as fi: for line in fi: line = line.strip() if not line: continue self.words.append(line) self.voiceCombo.clear() for word in self.words: self.voiceCombo.addItem(word) if len(self.words): word = self.words[self.voiceCombo.currentIndex()] self.text.setText(word) def say(self): self.sayButton.setEnabled(False) self.engine.setVoice(self.voice) self.engine.setVolume(float(self.volumeSlider.value()) / 100) text = self.text.text().strip() if not text: self.sayButton.setEnabled(True) else: self.engine.say(self.text.text()) def stateChanged(self, state): if (state == QTextToSpeech.State.Ready): self.sayButton.setEnabled(True) currIdx = self.voiceCombo.currentIndex() count = self.voiceCombo.count() if currIdx + 1 < count: self.voiceCombo.setCurrentIndex(currIdx + 1) word = self.words[self.voiceCombo.currentIndex()] self.text.setText(word) else: self.text.setText('')
class AudioInfoDialog(QDialog): def __init__(self, audios_name, audios_delay, audios_language, audios_track_name, audios_set_default, audios_set_forced, audios_default_value_delay, audios_default_value_language, audios_default_value_track_name, audios_default_value_set_default, audios_default_value_set_forced, audio_set_default_disabled=False, audio_set_forced_disabled=False, disable_edit=False, parent=None): super().__init__(parent) self.window_title = "Audio Info" self.state = "no" self.audios_count = len(audios_delay) self.messageIcon = QLabel() self.audio_tab_comboBox = InfoCellDialogTabComboBox( hint="Audios Groups") for i in range(self.audios_count): self.audio_tab_comboBox.addItem("Audio #" + str(i + 1)) self.audio_tab_comboBox.setCurrentIndex(0) self.audio_tab_comboBox.currentIndexChanged.connect( self.update_current_audio_index) self.current_audio_index = 0 self.disable_edit = disable_edit self.current_audio_name = audios_name self.current_audio_language = audios_language self.current_audio_delay = audios_delay self.current_audio_track_name = audios_track_name self.current_audio_set_default = audios_set_default self.current_audio_set_forced = audios_set_forced self.default_audio_language = audios_default_value_language self.default_audio_delay = audios_default_value_delay self.default_audio_track_name = audios_default_value_track_name self.default_audio_set_default = audios_default_value_set_default self.default_audio_set_forced = audios_default_value_set_forced self.audio_set_default_disabled = audio_set_default_disabled self.audio_set_forced_disabled = audio_set_forced_disabled self.audio_name_label = QLabel("Audio Name:") self.audio_name_value = QLabel( str(self.current_audio_name[self.current_audio_index])) width_to_be_fixed = 0 for i in range(len(self.current_audio_name)): width_to_be_fixed = max( width_to_be_fixed, self.audio_name_value.fontMetrics().boundingRect( self.current_audio_name[i]).width()) self.audio_name_value.setFixedWidth(width_to_be_fixed + 10) self.audio_delay_label = QLabel("Audio Delay:") self.audio_delay_spin = QDoubleSpinBox() self.setup_audio_delay_spin() self.audio_language_label = QLabel("Audio Language:") self.audio_language_comboBox = QComboBox() self.setup_audio_language_comboBox() self.audio_track_name_label = QLabel("Audio Track Name:") self.audio_track_name_lineEdit = QLineEdit() self.setup_audio_track_name_lineEdit() self.audio_set_forced_label = QLabel("Audio Forced State:") self.audio_set_forced_checkBox = QCheckBox() self.setup_audio_set_forced_checkBox() self.audio_set_default_label = QLabel("Audio Default State:") self.audio_set_default_checkBox = QCheckBox() self.setup_audio_set_default_checkBox() self.yes_button = QPushButton("OK") self.no_button = QPushButton("Cancel") self.reset_button = QPushButton("Reset To Default") self.buttons_layout = QHBoxLayout() self.audio_delay_layout = QHBoxLayout() self.audio_language_layout = QHBoxLayout() self.audio_track_name_layout = QHBoxLayout() self.audio_set_default_layout = QHBoxLayout() self.audio_set_forced_layout = QHBoxLayout() self.buttons_layout.addWidget(QLabel(""), stretch=3) self.buttons_layout.addWidget(self.reset_button, stretch=2) self.buttons_layout.addWidget(self.yes_button, stretch=2) self.buttons_layout.addWidget(self.no_button, stretch=2) self.buttons_layout.addWidget(QLabel(""), stretch=3) self.audio_setting_layout = QGridLayout() self.audio_editable_setting_layout = QFormLayout() self.audio_editable_setting_layout.addRow(self.audio_name_label, self.audio_name_value) self.audio_editable_setting_layout.addRow( self.audio_track_name_label, self.audio_track_name_lineEdit) self.audio_editable_setting_layout.addRow(self.audio_language_label, self.audio_language_comboBox) self.audio_editable_setting_layout.addRow(self.audio_delay_label, self.audio_delay_spin) self.audio_editable_setting_layout.addRow( self.audio_set_default_label, self.audio_set_default_checkBox) self.audio_editable_setting_layout.addRow( self.audio_set_forced_label, self.audio_set_forced_checkBox) self.audio_setting_layout.addWidget(self.audio_tab_comboBox, 0, 0) self.audio_setting_layout.addLayout(self.audio_editable_setting_layout, 1, 0, 5, 2) self.audio_setting_layout.addWidget(self.messageIcon, 1, 3, 5, -1) self.main_layout = QGridLayout() self.main_layout.addLayout(self.audio_setting_layout, 0, 0, 2, 3) self.main_layout.addLayout(self.buttons_layout, 2, 0, 1, -1) self.main_layout.setContentsMargins(20, 20, 20, 20) self.setLayout(self.main_layout) self.setup_ui() self.signal_connect() def setup_ui(self): self.disable_question_mark_window() self.messageIcon.setPixmap( QtGui.QPixmap(GlobalFiles.AudioIconPath).scaledToHeight(100)) self.set_dialog_values() self.set_default_buttons() if self.audio_set_default_disabled: self.audio_set_default_disable() if self.audio_set_forced_disabled: self.audio_set_forced_disable() if self.disable_edit: self.audio_track_name_lineEdit.setEnabled(False) self.audio_language_comboBox.setEnabled(False) self.audio_delay_spin.setEnabled(False) self.audio_set_default_checkBox.setEnabled(False) self.audio_set_forced_checkBox.setEnabled(False) self.reset_button.setEnabled(False) self.setup_tool_tip_hint_audio_set_default() self.setup_tool_tip_hint_audio_set_forced() def signal_connect(self): self.audio_track_name_lineEdit.textEdited.connect( self.update_current_audio_track_name) self.audio_delay_spin.editingFinished.connect( self.update_current_audio_delay) self.audio_language_comboBox.currentTextChanged.connect( self.update_current_audio_language) self.audio_set_default_checkBox.stateChanged.connect( self.update_current_audio_set_default) self.audio_set_forced_checkBox.stateChanged.connect( self.update_current_audio_set_forced) self.yes_button.clicked.connect(self.click_yes) self.no_button.clicked.connect(self.click_no) self.reset_button.clicked.connect(self.reset_audio_setting) def click_yes(self): self.state = "yes" self.close() def click_no(self): self.state = "no" self.close() def set_dialog_values(self): self.setWindowTitle(self.window_title) self.setWindowIcon(GlobalFiles.InfoSettingIcon) def disable_question_mark_window(self): self.setWindowFlag(Qt.WindowContextHelpButtonHint, on=False) def increase_message_font_size(self, value): message_font = self.message.font() message_font.setPointSize(self.message.fontInfo().pointSize() + value) self.message.setFont(message_font) def set_default_buttons(self): self.yes_button.setDefault(True) self.yes_button.setFocus() def showEvent(self, a0: QtGui.QShowEvent) -> None: super().showEvent(a0) self.setFixedSize(self.size()) def setup_audio_track_name_lineEdit(self): self.audio_track_name_lineEdit.setClearButtonEnabled(True) self.audio_track_name_lineEdit.setText( self.current_audio_track_name[self.current_audio_index]) def setup_audio_language_comboBox(self): self.audio_language_comboBox.addItems(AllAudiosLanguages) self.audio_language_comboBox.setCurrentIndex( AllAudiosLanguages.index( self.current_audio_language[self.current_audio_index])) self.audio_language_comboBox.setMaxVisibleItems(8) self.audio_language_comboBox.setStyleSheet( "QComboBox { combobox-popup: 0; }") def setup_audio_delay_spin(self): # self.audio_delay_spin.setMaximumWidth(screen_size.width() // 16) self.audio_delay_spin.setDecimals(3) self.audio_delay_spin.setMinimum(-9999.0) self.audio_delay_spin.setMaximum(9999.0) self.audio_delay_spin.setSingleStep(0.5) self.audio_delay_spin.setValue( float(self.current_audio_delay[self.current_audio_index])) def setup_audio_set_default_checkBox(self): self.audio_set_default_checkBox.setText("Set Default") self.audio_set_default_checkBox.setChecked( bool(self.current_audio_set_default[self.current_audio_index])) def setup_audio_set_forced_checkBox(self): self.audio_set_forced_checkBox.setText("Set Forced") self.audio_set_forced_checkBox.setChecked( bool(self.current_audio_set_forced[self.current_audio_index])) def update_current_audio_track_name(self): self.current_audio_track_name[self.current_audio_index] = str( self.audio_track_name_lineEdit.text()) def update_current_audio_delay(self): self.current_audio_delay[self.current_audio_index] = round( self.audio_delay_spin.value(), 5) def update_current_audio_language(self): self.current_audio_language[self.current_audio_index] = str( self.audio_language_comboBox.currentText()) def update_current_audio_set_default(self): new_state = self.audio_set_default_checkBox.checkState() == Qt.Checked self.current_audio_set_default[self.current_audio_index] = new_state if new_state: for i in range(len(self.current_audio_set_default)): if i != self.current_audio_index: self.current_audio_set_default[i] = False def update_current_audio_set_forced(self): new_state = self.audio_set_forced_checkBox.checkState() == Qt.Checked self.current_audio_set_forced[self.current_audio_index] = new_state if new_state: for i in range(len(self.current_audio_set_forced)): if i != self.current_audio_index: self.current_audio_set_forced[i] = False def reset_audio_setting(self): self.current_audio_language[ self.current_audio_index] = self.default_audio_language[ self.current_audio_index] self.current_audio_delay[ self.current_audio_index] = self.default_audio_delay[ self.current_audio_index] self.current_audio_track_name[ self.current_audio_index] = self.default_audio_track_name[ self.current_audio_index] self.current_audio_set_default[ self.current_audio_index] = self.default_audio_set_default[ self.current_audio_index] self.current_audio_set_forced[ self.current_audio_index] = self.default_audio_set_forced[ self.current_audio_index] self.audio_language_comboBox.setCurrentIndex( AllAudiosLanguages.index( self.current_audio_language[self.current_audio_index])) self.audio_delay_spin.setValue( float(self.current_audio_delay[self.current_audio_index])) self.audio_track_name_lineEdit.setText( self.current_audio_track_name[self.current_audio_index]) self.audio_set_default_checkBox.setChecked( bool(self.current_audio_set_default[self.current_audio_index])) self.audio_set_forced_checkBox.setChecked( bool(self.current_audio_set_forced[self.current_audio_index])) def audio_set_default_disable(self): self.audio_set_default_checkBox.setDisabled(True) def audio_set_forced_disable(self): self.audio_set_forced_checkBox.setDisabled(True) def setup_tool_tip_hint_audio_set_default(self): if self.audio_set_default_checkBox.isEnabled(): self.audio_set_default_checkBox.setToolTip( "<nobr>set this audio to be the default audio track " "when play") self.audio_set_default_checkBox.setToolTipDuration(12000) else: self.audio_set_default_checkBox.setToolTip( "<nobr>set this audio to be the default audio track when play<br><b>Disabled</b> because " "option " "<b>make this audio default</b> is enabled on mux setting tab " ) self.audio_set_default_checkBox.setToolTipDuration(12000) def setup_tool_tip_hint_audio_set_forced(self): if self.audio_set_forced_checkBox.isEnabled(): self.audio_set_forced_checkBox.setToolTip( "<nobr>set this audio to be the forced audio track when " "play") self.audio_set_forced_checkBox.setToolTipDuration(12000) else: self.audio_set_forced_checkBox.setToolTip( "<nobr>set this audio to be the forced audio track when play<br><b>Disabled</b> because " "option " "<b>make this audio default and forced</b> is enabled on mux setting tab " ) self.audio_set_forced_checkBox.setToolTipDuration(12000) def update_current_audio_index(self, new_index): self.current_audio_index = new_index self.audio_delay_spin.setValue( float(self.current_audio_delay[self.current_audio_index])) self.audio_set_default_checkBox.setChecked( bool(self.current_audio_set_default[self.current_audio_index])) self.audio_set_forced_checkBox.setChecked( bool(self.current_audio_set_forced[self.current_audio_index])) self.audio_language_comboBox.setCurrentIndex( AllAudiosLanguages.index( self.current_audio_language[self.current_audio_index])) self.audio_track_name_lineEdit.setText( self.current_audio_track_name[self.current_audio_index]) self.audio_name_value.setText( str(self.current_audio_name[self.current_audio_index])) def execute(self): self.exec_()
class MuxSettingTab(QWidget): tab_clicked_signal = Signal() start_muxing_signal = Signal() update_task_bar_progress_signal = Signal(int) update_task_bar_paused_signal = Signal() update_task_bar_clear_signal = Signal() def __init__(self): super().__init__() self.create_widgets() self.setup_widgets() self.connect_signals() def connect_signals(self): self.tab_clicked_signal.connect(self.tab_clicked) self.destination_path_button.clicked.connect(self.open_select_destination_folder_dialog) self.only_keep_those_audios_checkBox.stateChanged.connect( self.only_keep_those_audios_multi_choose_comboBox.check_box_state_changed) self.only_keep_those_subtitles_checkBox.stateChanged.connect( self.only_keep_those_subtitles_multi_choose_comboBox.check_box_state_changed) self.make_this_audio_default_checkBox.disable_combo_box.connect(self.disable_make_this_audio_default_comboBox) self.make_this_subtitle_default_checkBox.disable_combo_box.connect( self.disable_make_this_subtitle_default_comboBox) self.control_queue_button.add_to_queue_clicked_signal.connect(self.add_to_queue_button_clicked) self.control_queue_button.start_multiplexing_clicked_signal.connect(self.start_multiplexing_button_clicked) self.control_queue_button.pause_multiplexing_clicked_signal.connect(self.pause_multiplexing_button_clicked) self.clear_job_queue_button.clicked.connect(self.clear_job_queue_button_clicked) self.only_keep_those_audios_multi_choose_comboBox.closeList.connect(self.only_keep_those_audios_close_list) self.only_keep_those_subtitles_multi_choose_comboBox.closeList.connect( self.only_keep_those_subtitles_close_list) self.make_this_audio_default_comboBox.currentTextChanged.connect( self.make_this_audio_default_comboBox_text_changed) self.make_this_subtitle_default_comboBox.currentTextChanged.connect( self.make_this_subtitle_default_comboBox_text_changed) self.abort_on_errors_checkBox.stateChanged.connect(self.abort_on_errors_state_changed) self.keep_log_file_checkBox.stateChanged.connect(self.keep_log_file_state_changed) self.job_queue_layout.update_task_bar_progress_signal.connect(self.update_task_bar_progress) self.job_queue_layout.paused_done_signal.connect(self.paused_done) self.job_queue_layout.cancel_done_signal.connect(self.cancel_done) self.job_queue_layout.finished_all_jobs_signal.connect(self.finished_all_jobs) self.job_queue_layout.pause_from_error_occurred_signal.connect(self.pause_multiplexing_button_clicked) def setup_widgets(self): self.setup_mux_setting_groupBox() self.setup_job_queue_groupBox() self.setup_destination_path_label() self.setup_destination_path_lineEdit() self.setup_destination_path_button() self.setup_abort_on_errors_checkBox() self.setup_discard_old_attachments_checkBox() self.setup_keep_log_file_checkBox() self.setup_clear_job_queue_button() self.setup_tool_tip_hint() self.setup_layouts() def setup_layouts(self): self.setup_MainLayout() self.mux_setting_groupBox.setLayout(self.mux_setting_layout) self.job_queue_groupBox.setLayout(self.job_queue_layout) self.setup_mux_tools_layout_first_row() self.setup_mux_tools_layout_second_row() self.setup_mux_setting_layout() self.setLayout(self.MainLayout) # noinspection PyAttributeOutsideInit def create_widgets(self): self.MainLayout = QVBoxLayout() self.mux_setting_groupBox = QGroupBox(self) self.job_queue_groupBox = QGroupBox(self) self.mux_setting_layout = QGridLayout() self.job_queue_layout = JobQueueLayout() self.destination_path_label = QLabel() self.destination_path_lineEdit = QLineEdit() self.destination_path_button = QPushButton() self.only_keep_those_audios_checkBox = OnlyKeepThoseAudiosCheckBox() self.only_keep_those_subtitles_checkBox = OnlyKeepThoseSubtitlesCheckBox() self.only_keep_those_audios_multi_choose_comboBox = AudioTracksCheckableComboBox() self.only_keep_those_subtitles_multi_choose_comboBox = SubtitleTracksCheckableComboBox() self.make_this_audio_default_checkBox = MakeThisAudioDefaultCheckBox() self.make_this_subtitle_default_checkBox = MakeThisSubtitleDefaultCheckBox() self.make_this_audio_default_comboBox = MakeThisTrackDefaultComboBox() self.make_this_subtitle_default_comboBox = MakeThisTrackDefaultComboBox() self.abort_on_errors_checkBox = QCheckBox() self.discard_old_attachments_checkBox = QCheckBox() self.keep_log_file_checkBox = QCheckBox() self.control_queue_button = ControlQueueButton() self.clear_job_queue_button = QPushButton() self.mux_tools_layout_first_row = QHBoxLayout() self.mux_tools_layout_second_row = QHBoxLayout() self.job_queue_tools_layout = QHBoxLayout() def setup_mux_setting_layout(self): self.mux_setting_layout.addWidget(self.destination_path_label, 0, 0) self.mux_setting_layout.addWidget(self.destination_path_lineEdit, 0, 1) self.mux_setting_layout.addWidget(self.destination_path_button, 0, 2) self.mux_setting_layout.addWidget(self.only_keep_those_audios_checkBox, 1, 0) self.mux_setting_layout.addWidget(self.only_keep_those_subtitles_checkBox, 2, 0) self.mux_setting_layout.addLayout(self.mux_tools_layout_first_row, 1, 1) self.mux_setting_layout.addLayout(self.mux_tools_layout_second_row, 2, 1) def setup_mux_tools_layout_first_row(self): self.mux_tools_layout_first_row.addWidget(self.only_keep_those_audios_multi_choose_comboBox, 2) self.mux_tools_layout_first_row.addWidget(self.make_this_audio_default_checkBox, 1) self.mux_tools_layout_first_row.addWidget(self.make_this_audio_default_comboBox, 2) self.mux_tools_layout_first_row.addWidget(self.abort_on_errors_checkBox, 1) self.mux_tools_layout_first_row.addWidget(self.keep_log_file_checkBox) def setup_mux_tools_layout_second_row(self): self.mux_tools_layout_second_row.addWidget(self.only_keep_those_subtitles_multi_choose_comboBox, 2) self.mux_tools_layout_second_row.addWidget(self.make_this_subtitle_default_checkBox, 1) self.mux_tools_layout_second_row.addWidget(self.make_this_subtitle_default_comboBox, 2) self.mux_tools_layout_second_row.addWidget(self.control_queue_button, 1) self.mux_tools_layout_second_row.addWidget(self.clear_job_queue_button, 1) def setup_clear_job_queue_button(self): self.clear_job_queue_button.setText("Clear All") self.clear_job_queue_button.setIcon(GlobalFiles.CleanIcon) self.clear_job_queue_button.setDisabled(True) def setup_keep_log_file_checkBox(self): self.keep_log_file_checkBox.setText("Keep Log File") self.keep_log_file_checkBox.setToolTip("log file will located in the source folder after finished muxing") def setup_discard_old_attachments_checkBox(self): self.discard_old_attachments_checkBox.setText("Discard Old Attachments ") def setup_abort_on_errors_checkBox(self): self.abort_on_errors_checkBox.setText("Abort On Errors") self.abort_on_errors_checkBox.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred) def setup_destination_path_button(self): self.destination_path_button.setIcon(GlobalFiles.SelectFolderIcon) def setup_destination_path_lineEdit(self): self.destination_path_lineEdit.setPlaceholderText("Enter Destination Folder Path") self.destination_path_lineEdit.setClearButtonEnabled(True) def setup_destination_path_label(self): self.destination_path_label.setText("Videos Destination Folder :") def setup_MainLayout(self): self.MainLayout.addWidget(self.mux_setting_groupBox) self.MainLayout.addWidget(self.job_queue_groupBox) def setup_job_queue_groupBox(self): self.job_queue_groupBox.setTitle("Job Queue") def setup_mux_setting_groupBox(self): self.mux_setting_groupBox.setTitle("Mux Setting") def paintEvent(self, event: QPaintEvent): self.update_widgets_size() super().paintEvent(event) def resizeEvent(self, event: QResizeEvent): self.job_queue_layout.update_layout() super().resizeEvent(event) def update_widgets_size(self): self.only_keep_those_subtitles_multi_choose_comboBox.resize( self.only_keep_those_audios_multi_choose_comboBox.width(), self.only_keep_those_audios_multi_choose_comboBox.height(), ) self.make_this_subtitle_default_checkBox.resize( self.make_this_audio_default_checkBox.width(), self.make_this_audio_default_checkBox.height(), ) self.make_this_subtitle_default_checkBox.move( self.make_this_audio_default_checkBox.x(), self.make_this_subtitle_default_checkBox.y(), ) self.make_this_subtitle_default_comboBox.resize( self.make_this_audio_default_comboBox.width(), self.make_this_audio_default_comboBox.height(), ) self.make_this_subtitle_default_comboBox.move( self.make_this_audio_default_comboBox.x(), self.make_this_subtitle_default_comboBox.y(), ) self.control_queue_button.move( self.abort_on_errors_checkBox.x(), self.control_queue_button.y(), ) self.clear_job_queue_button.move( self.control_queue_button.x() + self.control_queue_button.width() + 5, self.clear_job_queue_button.y(), ) def open_select_destination_folder_dialog(self): temp_folder_path = QFileDialog.getExistingDirectory(self, caption="Choose Destination Folder", dir=GlobalSetting.LAST_DIRECTORY_PATH, ) if temp_folder_path == "" or temp_folder_path.isspace(): return elif Path(temp_folder_path) == Path(GlobalSetting.VIDEO_SOURCE_PATH): invalid_dialog = InvalidPathDialog( error_message="Source and destination videos can't be in the same folder") invalid_dialog.execute() return else: self.destination_path_lineEdit.setText(str(Path(temp_folder_path))) GlobalSetting.LAST_DIRECTORY_PATH = self.destination_path_lineEdit.text() GlobalSetting.DESTINATION_FOLDER_PATH = self.destination_path_lineEdit.text() def check_destination_path(self): temp_destination_path = self.destination_path_lineEdit.text() try: if temp_destination_path == "" or temp_destination_path.isspace(): temp_destination_path = "[Empty Path]" raise Exception( "[WinError 998] Empty path is Not a valid path : " + temp_destination_path) # check if system is windows so path must have # SOME_LETTER:\ if os.name == 'nt': if temp_destination_path[1:3] != ":\\" and self.destination_path_lineEdit.text()[ 1:3] != ":/": raise Exception("[WinError 999] Not a valid path : " + temp_destination_path) makedirs(temp_destination_path, exist_ok=True) ## test if i can write into this path: test_file_name = str(time.time()) + ".txt" test_file_name_absolute = os.path.join(Path(temp_destination_path), Path(test_file_name)) try: with open(test_file_name_absolute, 'w+') as test_file: test_file.write("Test") os.remove(test_file_name_absolute) except Exception as e: write_to_log_file(e) invaild_dialog = InvalidPathDialog(window_title="Permission Denied", error_message="MKV Muxing Batch GUI lacks write " "permissions on Destination folder") invaild_dialog.execute() self.destination_path_lineEdit.setText(GlobalSetting.DESTINATION_FOLDER_PATH) return False except Exception as e: write_to_log_file(e) error_message = "" if temp_destination_path == "[Empty Path]": error_message = "Enter a valid destination path" else: error_message = temp_destination_path + "\nisn't a valid path!" invalid_dialog = InvalidPathDialog(error_message=error_message) invalid_dialog.execute() self.destination_path_lineEdit.setText(GlobalSetting.DESTINATION_FOLDER_PATH) return False if Path(temp_destination_path) == Path(GlobalSetting.VIDEO_SOURCE_PATH): invalid_dialog = InvalidPathDialog( error_message="Source and destination videos can't be in the same folder") invalid_dialog.execute() self.destination_path_lineEdit.setText(GlobalSetting.DESTINATION_FOLDER_PATH) return False GlobalSetting.DESTINATION_FOLDER_PATH = temp_destination_path return True def setup_tool_tip_hint(self): self.only_keep_those_subtitles_multi_choose_comboBox.set_tool_tip_hint() self.only_keep_those_audios_multi_choose_comboBox.set_tool_tip_hint() self.make_this_subtitle_default_checkBox.set_tool_tip_hint_no_check() self.make_this_audio_default_checkBox.set_tool_tip_hint_no_check() def add_to_queue_button_clicked(self): self.job_queue_layout.setup_queue() self.enable_muxing_setting() if not GlobalSetting.JOB_QUEUE_EMPTY: self.disable_editable_widgets() self.control_queue_button.set_state_start_multiplexing() self.clear_job_queue_button.setDisabled(False) change_global_LogFilePath() else: self.enable_editable_widgets() self.setup_enable_options_for_mkv_only_options() def tab_clicked(self): self.job_queue_layout.show_necessary_table_columns() self.setup_enable_options_for_mkv_only_options() def setup_enable_options_for_mkv_only_options(self): if GlobalSetting.JOB_QUEUE_EMPTY: if GlobalSetting.VIDEO_SOURCE_MKV_ONLY: self.only_keep_those_audios_checkBox.setEnabled(True) self.only_keep_those_subtitles_checkBox.setEnabled(True) self.make_this_audio_default_checkBox.setEnabled(True) self.make_this_subtitle_default_checkBox.setEnabled(True) self.only_keep_those_audios_checkBox.setToolTip("") self.only_keep_those_subtitles_checkBox.setToolTip("") self.make_this_audio_default_comboBox.setToolTip("") self.make_this_subtitle_default_comboBox.setToolTip("") self.setup_tool_tip_hint() else: self.only_keep_those_subtitles_checkBox.setCheckState(Qt.Unchecked) self.only_keep_those_audios_checkBox.setCheckState(Qt.Unchecked) self.make_this_audio_default_checkBox.setCheckState(Qt.Unchecked) self.make_this_subtitle_default_checkBox.setCheckState(Qt.Unchecked) self.only_keep_those_audios_checkBox.setEnabled(False) self.only_keep_those_subtitles_checkBox.setEnabled(False) self.make_this_audio_default_checkBox.setEnabled(False) self.make_this_subtitle_default_checkBox.setEnabled(False) self.only_keep_those_audios_checkBox.setToolTip("<b>[Disabled]</b> Only works when video files " "are Mkv only") self.only_keep_those_subtitles_checkBox.setToolTip("<b>[Disabled]</b> Only works when video files " "are Mkv only") self.make_this_audio_default_checkBox.setToolTip("<b>[Disabled]</b> Only works when video files " "are Mkv only") self.make_this_subtitle_default_checkBox.setToolTip("<b>[Disabled]</b> Only works when video files " "are Mkv only") self.make_this_audio_default_comboBox.setToolTip("<b>[Disabled]</b> Only works when video files " "are Mkv only") self.make_this_subtitle_default_comboBox.setToolTip("<b>[Disabled]</b> Only works when video files " "are Mkv only") self.only_keep_those_audios_multi_choose_comboBox.setToolTip( "<b>[Disabled]</b> Only works when video files " "are Mkv only") self.only_keep_those_subtitles_multi_choose_comboBox.setToolTip( "<b>[Disabled]</b> Only works when video files " "are Mkv only") def clear_job_queue_button_clicked(self): self.job_queue_layout.clear_queue() self.control_queue_button.set_state_add_to_queue() self.clear_job_queue_button.setDisabled(True) self.control_queue_button.setDisabled(False) self.enable_editable_widgets() self.enable_muxing_setting() self.setup_enable_options_for_mkv_only_options() self.update_task_bar_clear_signal.emit() def disable_editable_widgets(self): self.only_keep_those_subtitles_checkBox.setEnabled(False) self.only_keep_those_subtitles_multi_choose_comboBox.setEnabled(False) self.only_keep_those_audios_checkBox.setEnabled(False) self.only_keep_those_audios_multi_choose_comboBox.setEnabled(False) self.make_this_subtitle_default_checkBox.setEnabled(False) self.make_this_subtitle_default_comboBox.setEnabled(False) self.make_this_audio_default_checkBox.setEnabled(False) self.make_this_audio_default_comboBox.setEnabled(False) def enable_editable_widgets(self): self.only_keep_those_subtitles_checkBox.setEnabled(True) self.only_keep_those_subtitles_multi_choose_comboBox.setEnabled( self.only_keep_those_subtitles_checkBox.isChecked()) self.only_keep_those_audios_checkBox.setEnabled(True) self.only_keep_those_audios_multi_choose_comboBox.setEnabled(self.only_keep_those_audios_checkBox.isChecked()) self.make_this_subtitle_default_checkBox.setEnabled(True) self.make_this_subtitle_default_comboBox.setEnabled(self.make_this_subtitle_default_checkBox.isChecked()) self.make_this_audio_default_checkBox.setEnabled(True) self.make_this_audio_default_comboBox.setEnabled(self.make_this_audio_default_checkBox.isChecked()) def only_keep_those_audios_close_list(self): GlobalSetting.MUX_SETTING_ONLY_KEEP_THOSE_AUDIOS_LANGUAGES = self.only_keep_those_audios_multi_choose_comboBox.languages GlobalSetting.MUX_SETTING_ONLY_KEEP_THOSE_AUDIOS_TRACKS = self.only_keep_those_audios_multi_choose_comboBox.tracks def only_keep_those_subtitles_close_list(self): GlobalSetting.MUX_SETTING_ONLY_KEEP_THOSE_SUBTITLES_LANGUAGES = self.only_keep_those_subtitles_multi_choose_comboBox.languages GlobalSetting.MUX_SETTING_ONLY_KEEP_THOSE_SUBTITLES_TRACKS = self.only_keep_those_subtitles_multi_choose_comboBox.tracks def disable_make_this_subtitle_default_comboBox(self, state): self.make_this_subtitle_default_comboBox.setDisabled(state) if state: self.make_this_subtitle_default_comboBox.setCurrentIndex(-1) def disable_make_this_audio_default_comboBox(self, state): self.make_this_audio_default_comboBox.setDisabled(state) if state: self.make_this_audio_default_comboBox.setCurrentIndex(-1) def make_this_audio_default_comboBox_text_changed(self): GlobalSetting.MUX_SETTING_MAKE_THIS_AUDIO_DEFAULT_TRACK = str( self.make_this_audio_default_comboBox.currentText()) def make_this_subtitle_default_comboBox_text_changed(self): GlobalSetting.MUX_SETTING_MAKE_THIS_SUBTITLE_DEFAULT_TRACK = str( self.make_this_subtitle_default_comboBox.currentText()) def update_task_bar_progress(self, new_progress): self.update_task_bar_progress_signal.emit(new_progress) def enable_muxing_setting(self): self.destination_path_lineEdit.setEnabled(True) self.destination_path_button.setEnabled(True) self.abort_on_errors_checkBox.setEnabled(True) self.keep_log_file_checkBox.setEnabled(True) def disable_muxing_setting(self): self.destination_path_lineEdit.setEnabled(False) self.destination_path_button.setEnabled(False) self.abort_on_errors_checkBox.setEnabled(False) self.keep_log_file_checkBox.setEnabled(False) @staticmethod def abort_on_errors_state_changed(state): GlobalSetting.MUX_SETTING_ABORT_ON_ERRORS = bool(state) @staticmethod def keep_log_file_state_changed(state): GlobalSetting.MUX_SETTING_KEEP_LOG_FILE = bool(state) def start_multiplexing_button_clicked(self): at_least_one_muxing_setting_has_been_selected = check_if_at_least_one_muxing_setting_has_been_selected() if at_least_one_muxing_setting_has_been_selected: destination_path_valid = self.check_destination_path() if destination_path_valid: self.setup_log_file() self.control_queue_button.set_state_pause_multiplexing() self.disable_muxing_setting() self.job_queue_layout.start_muxing() self.start_muxing_signal.emit() self.clear_job_queue_button.setDisabled(True) def pause_multiplexing_button_clicked(self): self.job_queue_layout.pause_muxing() self.control_queue_button.setDisabled(True) self.control_queue_button.set_state_pausing_multiplexing() def paused_done(self): self.control_queue_button.set_state_resume_multiplexing() self.clear_job_queue_button.setDisabled(False) self.control_queue_button.setDisabled(False) self.update_task_bar_paused_signal.emit() def cancel_done(self): self.disable_editable_widgets() self.enable_muxing_setting() self.control_queue_button.set_state_start_multiplexing() self.clear_job_queue_button.setDisabled(False) change_global_LogFilePath() def finished_all_jobs(self): self.enable_editable_widgets() self.enable_muxing_setting() self.setup_enable_options_for_mkv_only_options() self.control_queue_button.set_state_start_multiplexing() self.control_queue_button.setDisabled(True) self.clear_job_queue_button.setDisabled(False) self.update_task_bar_clear_signal.emit() GlobalSetting.JOB_QUEUE_EMPTY = True check_if_want_to_keep_log_file() def setup_log_file(self): if self.control_queue_button.state == "START": open(GlobalFiles.LogFilePath, 'w+').close()
class App(QMainWindow): # ------------------------------------------ # -- Initialize the Class # ------------------------------------------ def __init__(self): super(App, self).__init__() # -- Home Directory self.home_path = expanduser("~") # -- Base Path # self.app_path = os.path.abspath(__file__) self.app_path = sys.argv[0] self.base_path_split = self.app_path.split('/') self.base_path = str('/').join(self.base_path_split[0:-1]) # -- Files under config self.config_file = self.base_path + '/config/config.json' self.connect_file = self.base_path + '/config/connect.sh' self.table_file = self.base_path + '/config/table.csv' self.delimiter_table_row = ';' # -- Data of Config File (with Default Values) self.config_data_file = { 'Message': { 'FileReadSuccess': True, 'FileReadFailed': True, 'ColorRedefinition': True }, 'Window': { 'Left': 100, 'Top': 100, 'Width': 640, 'Height': 480 }, 'Margin': { 'Left': 11, 'Top': 11, 'Right': 11, 'Bottom': 11 }, 'Title': { 'Label': 'SLT - Simple Login Tool - ' + __version__ }, 'Button': { 'Connect': { 'Label': 'Connect' }, 'Message': { 'Label': 'X' } }, 'Table': { 'Header': [], 'Column': { 'Color': 1, # -- Number of column which used for text matching 'Connect': [ 9, 1 ] # -- These are those column numbers which data picked from the selected row and will be used as connection parameters }, 'Cell': { 'Color': {} } } } # -- Data of Table File self.table_data_file = [] # -- Dictionary of Colors with QColor Elements self.color_data = {} # -- Central Widget self.central_widget = None # -- Main Layout self.main_layout = None # -- Input Box self.input_box = None # -- Connect Button self.connect_button = None # -- Message Button self.message_button = None # -- Message Label self.message_label = None self.message_label_count = None # -- Message List self.message_list = [] # -- Main Table self.main_table = None # -- Status Bar self.status_bar = None # -- Messages self.messages = { 'FileReadSuccess': 'INFO: File is opened for read: ', 'FileReadFailed': 'ERROR: File could not be opened for read: ', 'ColorRedefinition': 'WARN: Color Redefinition: ' } # -- Initialize the UI self.initUI() # ------------------------------------------ # -- Create Main Window # ------------------------------------------ # def createMainWindow(self): # if (self.main_window is None): # self.main_window = QMainWindow(self) # ------------------------------------------ # -- Create Central Widget # ------------------------------------------ def createCentralWidget(self): if (self.central_widget is None): self.central_widget = QWidget() # -- Set Central Widget for QMainWindow self.setCentralWidget(self.central_widget) # ------------------------------------------ # -- Create Layout # ------------------------------------------ def createLayout(self): if (self.main_layout is None): self.main_layout = QGridLayout() # ------------------------------------------ # -- Create Input Box # ------------------------------------------ def createInputBox(self): if (self.input_box is None): self.input_box = QLineEdit() # -- Enable Clear Button self.input_box.setClearButtonEnabled(True) # -- Create Event if enter pressed # self.input_box.editingFinished.connect(self.eventSearchInTable) self.input_box.editingFinished.connect(self.eventConnectButtonClicked) # -- Create Event if text changed self.input_box.textChanged.connect(self.eventSearchInTable) # ------------------------------------------ # -- Search Event # ------------------------------------------ def eventSearchInTable(self): # print('Search Event. Look up text is: %s' %(self.input_box.text())) # -- Debug self.refreshTable(self.input_box.text().strip()) # ------------------------------------------ # -- Create Connect Button # ------------------------------------------ def createConnectButton(self, text): if (self.connect_button is None): self.connect_button = QPushButton(text) # -- Add Clicked Event self.connect_button.clicked.connect(self.eventConnectButtonClicked) # ------------------------------------------ # -- Event for Connect Button Clicked # ------------------------------------------ def eventConnectButtonClicked(self): connectParameters = '' row = 0 # print('Connect Button Pressed') # -- Debug if (self.main_table.rowCount() == 1): for column in self.config_data_file['Table']['Column']['Connect']: cellTable = self.main_table.item(row, column) connectParameters += ' ' + str(cellTable.text()) self.connectExecute(connectParameters) # else: # print('More than one item in table, therefore cannot decide which one to choose') # -- Debug # ------------------------------------------ # -- Create Status Bar # ------------------------------------------ def createStatusBar(self): if (self.status_bar is None): self.status_bar = QStatusBar() self.status_bar.addWidget(self.message_label_count, 0) self.status_bar.addWidget(VLine(), 0) self.status_bar.addWidget(self.message_label, 1) self.status_bar.addWidget(self.message_button, 0) # ------------------------------------------ # -- Update Status Bar # ------------------------------------------ def updateStatusBar(self): if (self.status_bar is not None): # -- Hide the Status Bar if the Message List is empty if (len(self.message_list) > 0): self.status_bar.show() else: self.status_bar.hide() # ------------------------------------------ # -- Get Latest Message # ------------------------------------------ def getLatestMessage(self): result = None length = len(self.message_list) if (length > 0): result = self.message_list[length - 1] return result # ------------------------------------------ # -- Create Message Label # ------------------------------------------ def createMessageLabel(self): if (self.message_label is None): self.message_label = QLabel(self) # -- Add a Text to Message Label # self.message_label.setText(self.getLatestMessage()) self.updateMessageLabel() # ------------------------------------------ # -- Update Message Label with Latest Entry # ------------------------------------------ def updateMessageLabel(self): message = '' length = len(self.message_list) if (self.message_label is not None): if (length > 0): message = self.message_list[-1] self.message_label.setText(message) # ------------------------------------------ # -- Create Message Label Count # ------------------------------------------ def createMessageLabelCount(self): if (self.message_label_count is None): self.message_label_count = QLabel(self) self.updateMessageLabelCount() # ------------------------------------------ # -- Update Message Label Count # ------------------------------------------ def updateMessageLabelCount(self): message = '' length = len(self.message_list) if (self.message_label_count is not None): if (length > 0): message = ' ' + str(length) self.message_label_count.setText(message) # ------------------------------------------ # -- Create Message Button # ------------------------------------------ def createMessageButton(self, text): if (self.message_button is None): self.message_button = QPushButton(text) # -- Add Clicked Event self.message_button.clicked.connect(self.eventMessageButtonClicked) # ------------------------------------------ # -- Event for Message Button Clicked # ------------------------------------------ def eventMessageButtonClicked(self): length = len(self.message_list) # -- Remove the latest item in Message List if (length > 0): del self.message_list[length - 1] # -- Update Message Labels self.updateMessageLabel() self.updateMessageLabelCount() # -- Update the Status Bar self.updateStatusBar() # ------------------------------------------ # -- Add a New Message to List # ------------------------------------------ def addNewMessage(self, message): self.message_list.append(message) self.updateMessageLabel() # -- Update Status Bar self.updateStatusBar() # ------------------------------------------ # -- Check Table Header # ------------------------------------------ def checkMainTableHeader(self, hostsList): # -- Length of the Header defined in the config file lengthHeaderconfig_file = len(self.config_data_file['Table']['Header']) # -- Length of the First Record if (len(hostsList) > 0): lengthFirstRecord = len(hostsList[0]) else: lengthFirstRecord = 0 # -- Append the Header if the list in config file is too short if (lengthHeaderconfig_file < lengthFirstRecord): for header in range(lengthHeaderconfig_file + 1, lengthFirstRecord + 1): self.config_data_file['Table']['Header'].append(str(header)) # ------------------------------------------ # -- Create Table # ------------------------------------------ def createMainTable(self, hostsList): numCell = 0 maxTableRow = len(hostsList) maxTableColumn = len(self.config_data_file['Table']['Header']) if (self.main_table is None): self.main_table = QTableWidget() # -- Set the Maximum Size of Table self.main_table.setColumnCount(maxTableColumn) self.main_table.setRowCount(maxTableRow) # -- Create the Horizontal Header of Table headerTableWidget = self.main_table.horizontalHeader() # -- Set the Table Header self.main_table.setHorizontalHeaderLabels( self.config_data_file['Table']['Header']) # -- Hide The Horizontal Table Header # self.main_table.horizontalHeader().setVisible(False) # -- Set the Cells to Read Only self.main_table.setEditTriggers(QTableWidget.NoEditTriggers) # -- Set Table Header Properties for numCell in range(0, len(self.config_data_file['Table']['Header'])): headerTableWidget.setSectionResizeMode( numCell, QHeaderView.ResizeToContents) # -- Set the First Column to Resizeable # headerTableWidget.setSectionResizeMode(0, QHeaderView.Stretch) # -- Set the Last Column to Resizeable headerTableWidget.setSectionResizeMode(maxTableColumn - 1, QHeaderView.Stretch) # -- Add Double Clicked Event on Table self.main_table.itemDoubleClicked.connect( self.eventMainTableDoubleClickedOnCell) # -- Insert Data into Table self.insertDataIntoTable(self.main_table, hostsList) # ------------------------------------------ # -- Double Clicked on Cell Event # ------------------------------------------ def eventMainTableDoubleClickedOnCell(self): connectParameters = '' row = self.main_table.currentRow() # print('Double Clicked on a Table Cell') # -- Debug for column in self.config_data_file['Table']['Column']['Connect']: cellTable = self.main_table.item(row, column) connectParameters += ' ' + str(cellTable.text()) self.connectExecute(connectParameters) # ------------------------------------------ # -- Insert Data into the Table # ------------------------------------------ def insertDataIntoTable(self, inputTable, inputRecords): maxHeaderColumn = len(self.config_data_file['Table']['Header']) maxTableColumn = len(self.config_data_file['Table']['Header']) maxTableRow = len(inputRecords) colorColumn = self.config_data_file['Table']['Column']['Color'] numRecord = 0 numCell = 0 # -- Set the Maximum size of Table inputTable.setColumnCount(maxTableColumn) inputTable.setRowCount(maxTableRow) for record in inputRecords: # print('Record : %s' %(str(record))) # -- Debug for cell in record: # print('Cell : %s' %(cell)) # -- Debug if (numCell < maxHeaderColumn): inputTable.setItem(numRecord, numCell, QTableWidgetItem(cell)) # -- Set the Background Color of Cells # if (record[colorColumn] in self.colorCell): # inputTable.item(numRecord, numCell).setBackground(self.colorCell[record[colorColumn]]) if (record[colorColumn] in self.config_data_file['Table'] ['Cell']['Color']): nameColor = self.config_data_file['Table']['Cell'][ 'Color'][record[colorColumn]] # print('Cell: %s, %s, %s' %(record[colorColumn], nameColor, self.color_data[nameColor])) # -- Debug inputTable.item(numRecord, numCell).setBackground( self.color_data[nameColor]) numCell += 1 numCell = 0 numRecord += 1 inputTable.move(0, 0) # ------------------------------------------ # -- Refresh Table # ------------------------------------------ def refreshTable(self, searchText): found = False filteredHostsList = [] # print('Refresh table data.') # -- Debug # -- Clean the Table for row in range(self.main_table.rowCount() - 1, -1, -1): # print('Remove row: %s' %(str(row))) # -- Debug self.main_table.removeRow(row) self.main_table.show() # -- Update the table_data_file with searchText for record in self.table_data_file: found = False for cell in record: if (searchText == '' or cell.lower().find(searchText.lower()) != -1): # print('Found: %s' %(str(cell))) # -- Debug found = True if (found): filteredHostsList.append(record) # -- Recreate Table Data with filtered Values self.insertDataIntoTable(self.main_table, filteredHostsList) # -- Refresh the QTableWidget (required due to screen artifact) self.main_table.hide() self.main_table.show() # ------------------------------------------ # -- Read Config File # ------------------------------------------ def readConfigFile(self, filename): fileHandle = None message = '' try: fileHandle = open(filename, 'r') except IOError: message = self.messages['FileReadFailed'] + filename print(message) # -- Message if (self.config_data_file['Message']['FileReadFailed'] is True): self.addNewMessage(message) else: message = self.messages['FileReadSuccess'] + filename print(message) # -- Update the Default Data Values with the New Ones # self.config_data_file = json.load(fileHandle) self.config_data_file.update(json.load(fileHandle)) # -- Add Colors to the List for key, value in self.config_data_file['Color'].items(): # -- Check the Length of Value (must have 3 elements [R,G,B]) if (len(value) == 3): self.addColor(key, value[0], value[1], value[2]) # -- Message if (self.config_data_file['Message']['FileReadSuccess'] is True): self.addNewMessage(message) # print('JSON: %s' %(self.config_data_file)) # -- Debug finally: if (fileHandle): fileHandle.close() # ------------------------------------------ # -- Add Color to the Dictionary # ------------------------------------------ def addColor(self, name, red, green, blue): # print('Add Color: %s [%d,%d,%d]' %(name, red, green, blue)) # -- Debug # -- Check Red if (type(red) is int): if (red < 0 or red > 255): red = 255 else: red = 255 # -- Check Green if (type(green) is int): if (green < 0 or green > 255): green = 255 else: green = 255 # -- Check Blue if (type(blue) is int): if (blue < 0 or blue > 255): blue = 255 else: blue = 255 # print('Add Color: %s [%d,%d,%d]' %(name, red, green, blue)) # -- Debug # -- Add the Color to the Dictionary if (name not in self.color_data): self.color_data[name] = QColor(red, green, blue) else: # -- Message if (self.config_data_file['Message']['ColorRedefinition'] is True): self.addNewMessage(self.messages['ColorRedefinition'] + name) # ------------------------------------------ # -- Read CSV File # ------------------------------------------ def readCsvFile(self, filename): fileHandle = None result = [] message = '' try: fileHandle = open(filename, 'r') except IOError: message = self.messages['FileReadFailed'] + filename print(message) # -- Message if (self.config_data_file['Message']['FileReadFailed'] is True): self.addNewMessage(message) else: message = self.messages['FileReadSuccess'] + filename print(message) # -- Message if (self.config_data_file['Message']['FileReadSuccess'] is True): self.addNewMessage(message) fileContent = fileHandle.readlines() for line in fileContent: strippedLine = line.strip() if (strippedLine != ''): if (strippedLine[0] != '#'): # result.append(list(strippedLine.split(self.delimiter_table_row))) # -- List Items are not Stripped result.append( list(item.strip() for item in strippedLine.split( self.delimiter_table_row)) ) # -- List Items are Stripped # print(strippedLine) # -- Debug # -- Debug # for line in result: # for column in line: # print('[\'%s\']' %(str(column)), end='') # print('') finally: if (fileHandle): fileHandle.close() return result # ------------------------------------------ # -- Execute the Connect Command in Shell # ------------------------------------------ def connectExecute(self, parameters): # print('Run: %s %s' %(self.connect_file, parameters)) # -- Debug os.system(self.connect_file + ' ' + parameters) # ------------------------------------------ # -- UI Initialization # ------------------------------------------ def initUI(self): # -- Read the Config File self.readConfigFile(self.config_file) # -- Read the Table CSV File self.table_data_file = self.readCsvFile(self.table_file) # -- Create GUI Elements self.createCentralWidget() self.createLayout() self.createInputBox() self.createConnectButton( self.config_data_file['Button']['Connect']['Label']) self.checkMainTableHeader(self.table_data_file) self.createMainTable(self.table_data_file) self.createMessageLabel() self.createMessageLabelCount() self.createMessageButton( self.config_data_file['Button']['Message']['Label']) self.createStatusBar() self.updateStatusBar() # -- Set Window Title self.setWindowTitle(self.config_data_file['Title']['Label']) # -- Set Window Geometry self.setGeometry(self.config_data_file['Window']['Left'], self.config_data_file['Window']['Top'], self.config_data_file['Window']['Width'], self.config_data_file['Window']['Height']) # -- Set Layout Margins self.main_layout.setContentsMargins( self.config_data_file['Margin']['Left'], self.config_data_file['Margin']['Top'], self.config_data_file['Margin']['Right'], self.config_data_file['Margin']['Bottom']) # -- Set Layout self.central_widget.setLayout(self.main_layout) # -- Add Widgets to Layout self.main_layout.addWidget(self.input_box, 0, 0, 1, 1) self.main_layout.addWidget(self.connect_button, 0, 1, 1, 1) self.main_layout.addWidget(self.main_table, 1, 0, 1, 2) # -- Set Status Bar for QMainWindow self.setStatusBar(self.status_bar)
class Window(QWidget): def __init__(self): super(Window, self).__init__() self.proxyModel = QSortFilterProxyModel() self.proxyModel.setDynamicSortFilter(True) self.sourceGroupBox = QGroupBox("Original Model") self.proxyGroupBox = QGroupBox("Sorted/Filtered Model") self.sourceView = QTreeView() self.sourceView.setRootIsDecorated(False) self.sourceView.setAlternatingRowColors(True) self.proxyView = QTreeView() self.proxyView.setRootIsDecorated(False) self.proxyView.setAlternatingRowColors(True) self.proxyView.setModel(self.proxyModel) self.proxyView.setSortingEnabled(True) self.sortCaseSensitivityCheckBox = QCheckBox("Case sensitive sorting") self.filterCaseSensitivityCheckBox = QCheckBox("Case sensitive filter") self.filterPatternLineEdit = QLineEdit() self.filterPatternLineEdit.setClearButtonEnabled(True) self.filterPatternLabel = QLabel("&Filter pattern:") self.filterPatternLabel.setBuddy(self.filterPatternLineEdit) self.filterSyntaxComboBox = QComboBox() self.filterSyntaxComboBox.addItem("Regular expression", REGULAR_EXPRESSION) self.filterSyntaxComboBox.addItem("Wildcard", WILDCARD) self.filterSyntaxComboBox.addItem("Fixed string", FIXED_STRING) self.filterSyntaxLabel = QLabel("Filter &syntax:") self.filterSyntaxLabel.setBuddy(self.filterSyntaxComboBox) self.filterColumnComboBox = QComboBox() self.filterColumnComboBox.addItem("Subject") self.filterColumnComboBox.addItem("Sender") self.filterColumnComboBox.addItem("Date") self.filterColumnLabel = QLabel("Filter &column:") self.filterColumnLabel.setBuddy(self.filterColumnComboBox) self.filterPatternLineEdit.textChanged.connect(self.filterRegExpChanged) self.filterSyntaxComboBox.currentIndexChanged.connect(self.filterRegExpChanged) self.filterColumnComboBox.currentIndexChanged.connect(self.filterColumnChanged) self.filterCaseSensitivityCheckBox.toggled.connect(self.filterRegExpChanged) self.sortCaseSensitivityCheckBox.toggled.connect(self.sortChanged) sourceLayout = QHBoxLayout() sourceLayout.addWidget(self.sourceView) self.sourceGroupBox.setLayout(sourceLayout) proxyLayout = QGridLayout() proxyLayout.addWidget(self.proxyView, 0, 0, 1, 3) proxyLayout.addWidget(self.filterPatternLabel, 1, 0) proxyLayout.addWidget(self.filterPatternLineEdit, 1, 1, 1, 2) proxyLayout.addWidget(self.filterSyntaxLabel, 2, 0) proxyLayout.addWidget(self.filterSyntaxComboBox, 2, 1, 1, 2) proxyLayout.addWidget(self.filterColumnLabel, 3, 0) proxyLayout.addWidget(self.filterColumnComboBox, 3, 1, 1, 2) proxyLayout.addWidget(self.filterCaseSensitivityCheckBox, 4, 0, 1, 2) proxyLayout.addWidget(self.sortCaseSensitivityCheckBox, 4, 2) self.proxyGroupBox.setLayout(proxyLayout) mainLayout = QVBoxLayout() mainLayout.addWidget(self.sourceGroupBox) mainLayout.addWidget(self.proxyGroupBox) self.setLayout(mainLayout) self.setWindowTitle("Basic Sort/Filter Model") self.resize(500, 450) self.proxyView.sortByColumn(1, Qt.AscendingOrder) self.filterColumnComboBox.setCurrentIndex(1) self.filterPatternLineEdit.setText("Andy|Grace") self.filterCaseSensitivityCheckBox.setChecked(True) self.sortCaseSensitivityCheckBox.setChecked(True) def setSourceModel(self, model): self.proxyModel.setSourceModel(model) self.sourceView.setModel(model) def filterRegExpChanged(self): syntax_nr = self.filterSyntaxComboBox.currentData() pattern = self.filterPatternLineEdit.text() if syntax_nr == WILDCARD: pattern = QRegularExpression.wildcardToRegularExpression(pattern) elif syntax_nr == FIXED_STRING: pattern = QRegularExpression.escape(pattern) regExp = QRegularExpression(pattern) if not self.filterCaseSensitivityCheckBox.isChecked(): options = regExp.patternOptions() options |= QRegularExpression.CaseInsensitiveOption regExp.setPatternOptions(options) self.proxyModel.setFilterRegularExpression(regExp) def filterColumnChanged(self): self.proxyModel.setFilterKeyColumn(self.filterColumnComboBox.currentIndex()) def sortChanged(self): if self.sortCaseSensitivityCheckBox.isChecked(): caseSensitivity = Qt.CaseSensitive else: caseSensitivity = Qt.CaseInsensitive self.proxyModel.setSortCaseSensitivity(caseSensitivity)
class MainWindow(QMainWindow): """Main class for interacting with the applications GUI.""" @Slot(str) def startProgress(self, message): """Shows that the window is busy doing some work.""" self.translateButton.setEnabled(False) self.dictsCombo.setEnabled(False) label = QLabel(message) label.setAlignment(Qt.AlignHCenter) progressBar = QProgressBar() progressBar.setRange(0, 0) self.statusBar().clearMessage() self.statusBar().addWidget(label, 1) self.statusBar().addWidget(progressBar, 1) QGuiApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) @Slot() def endProgress(self): """Shows that the window is ready for the interaction.""" self.statusBar().setParent(None) self.statusBar().showMessage(self.tr("Done"), 2000) QGuiApplication.restoreOverrideCursor() self.translateButton.setEnabled(True) self.dictsCombo.setEnabled(True) @Slot() def about(self): """Shows the "about" message box.""" title = self.tr("About FreeLingvo") text = self.tr( "<b>FreeLingvo</b> is a dictionary search application" " that can work with over 140 dictionaries without" " Internet connection and is available under the" " GNU General Public License v3.<br><br>" "To search for translations in the selected" " dictionary, you must enter the desired word" " in the field and press \"Enter\" or click the" " \"translate\" button. You can also enter a sentence." " In this case, the sentence will be divided into" " separate words and possible combinations of these words." " The program will search for each word and combination" " in the selected dictionary, in which case the" " output can be large.<br><br>" "<b>Homepage:</b>" "<a href=\"https://github.com/ache2014/FreeLingvo\">" " GitHub</a><br>" "<b>Contact:</b>" "<a href=\"mailto:[email protected]\">" " [email protected]</a>") QMessageBox.about(self, title, text) @Slot(list) def showTranslations(self, translations): """Shows translations in the translations browser""" if translations: text = "<hr>".join(translations) else: text = self.tr("Sorry, no translations were found for:") text += "<br><b>{}</b>".format(self.searchText.text()) cursor = self.translationsBrowser.textCursor() cursor.select(QTextCursor.Document) cursor.insertHtml(text) @Slot(str) def changeFontSize(self, size): """Changes the font size for the translations browser.""" self.translationsBrowser.setFont(QFont(self.font().family(), int(size))) self.update() def __init__(self, parent=None, *args, appctxt=None, **kwargs): """Initializes all GUI elements and a separate thread.""" super().__init__(parent) self.appctxt = appctxt self.worker = Worker(self) self.workerThread = QThread() self.worker.moveToThread(self.workerThread) self.workerThread.finished.connect(self.worker.deleteLater) self.worker.started.connect(self.startProgress) self.worker.ended.connect(self.endProgress) self.worker.translationsReady.connect(self.showTranslations) self.workerThread.start() screenGeometry = QGuiApplication.screens()[0].geometry() self.setGeometry(screenGeometry.width() / 5, screenGeometry.height() / 5, screenGeometry.width() / 3, screenGeometry.height() / 3) self.setMinimumSize(screenGeometry.width() / 4, screenGeometry.height() / 4) self.fileMenu = self.menuBar().addMenu(self.tr("&File")) self.exitAct = self.fileMenu.addAction(self.tr("&Exit")) self.exitAct.setStatusTip(self.tr("Exit from FreeLingvo")) self.exitAct.triggered.connect(self.close) self.helpMenu = self.menuBar().addMenu(self.tr("&Help")) self.aboutAct = self.helpMenu.addAction(self.tr("&About")) self.aboutAct.setStatusTip( self.tr("Show information about FreeLingvo")) self.aboutAct.triggered.connect(self.about) self.searchText = QLineEdit() self.searchText.setPlaceholderText( self.tr("What word(s) to look for?")) self.searchText.setClearButtonEnabled(True) self.searchText.returnPressed.connect(self.worker.findTranslations) self.translateButton = QPushButton(self.tr("Translate")) self.translateButton.clicked.connect(self.worker.findTranslations) self.dictsCombo = QComboBox() self.dictsCombo.setInsertPolicy(QComboBox.InsertAlphabetically) self.dictsCombo.setToolTip( self.tr("Dictionary used to search for words")) dictNames = os.listdir(self.appctxt.get_resource("dictionaries")) dictNames = [name.replace(".tei", "") for name in dictNames] self.dictsCombo.addItems(dictNames) self.dictsCombo.currentIndexChanged[str].connect(self.worker.loadDict) self.dictsCombo.setCurrentIndex(1) self.translationsBrowser = QTextBrowser() self.translationsBrowser.setUndoRedoEnabled(True) self.undoButton = QPushButton( QIcon(self.appctxt.get_resource("images/arrow_back.png")), "") self.undoButton.setEnabled(False) self.undoButton.setToolTip(self.tr("Show previous translation")) self.undoButton.clicked.connect(self.translationsBrowser.undo) self.translationsBrowser.undoAvailable.connect( self.undoButton.setEnabled) self.redoButton = QPushButton( QIcon(self.appctxt.get_resource("images/arrow_forward.png")), "") self.redoButton.setEnabled(False) self.redoButton.setToolTip(self.tr("Show next translation")) self.redoButton.clicked.connect(self.translationsBrowser.redo) self.translationsBrowser.redoAvailable.connect( self.redoButton.setEnabled) self.sizeLabel = QLabel(self.tr("Font size:")) self.sizeCombo = QComboBox() for size in QFontDatabase.standardSizes(): self.sizeCombo.addItem(str(size)) self.sizeCombo.setCurrentText(str(self.font().pointSize())) self.sizeCombo.currentIndexChanged[str].connect(self.changeFontSize) self.controlsLayout = QHBoxLayout() self.controlsLayout.addWidget(self.searchText) self.controlsLayout.addWidget(self.translateButton) self.controlsLayout.addWidget(self.dictsCombo) self.browserToolsLayout = QHBoxLayout() self.browserToolsLayout.addWidget(self.undoButton) self.browserToolsLayout.addWidget(self.redoButton) self.browserToolsLayout.addStretch(1) self.browserToolsLayout.addWidget(self.sizeLabel) self.browserToolsLayout.addWidget(self.sizeCombo) self.centralLayout = QVBoxLayout() self.centralLayout.addLayout(self.controlsLayout) self.centralLayout.addLayout(self.browserToolsLayout) self.centralLayout.addWidget(self.translationsBrowser) centralWidget = QWidget() centralWidget.setLayout(self.centralLayout) self.setCentralWidget(centralWidget) self.statusBar().showMessage(self.tr("Ready"), 2000) def closeEvent(self, event): """Destroys the thread properly before exiting.""" self.workerThread.quit() self.workerThread.wait() super().closeEvent(event)
class SubtitleInfoDialog(QDialog): def __init__(self, subtitle_name="Test", subtitle_delay=0.0, subtitle_language=Default_Subtitle_Language, subtitle_track_name="Test", subtitle_set_default=False, subtitle_set_forced=False, subtitle_default_value_delay=0.0, subtitle_default_value_language=Default_Subtitle_Language, subtitle_default_value_track_name="Test", subtitle_default_value_set_default=False, subtitle_default_value_set_forced=False, subtitle_set_default_disabled=False, subtitle_set_forced_disabled=False, disable_edit=False, parent=None): super().__init__(parent) self.window_title = "Subtitle Info" self.state = "no" self.messageIcon = QLabel() self.disable_edit = disable_edit self.current_subtitle_language = str(subtitle_language) self.current_subtitle_delay = str(subtitle_delay) self.current_subtitle_track_name = str(subtitle_track_name) self.current_subtitle_set_default = subtitle_set_default self.current_subtitle_set_forced = subtitle_set_forced self.default_subtitle_language = str(subtitle_default_value_language) self.default_subtitle_delay = str(subtitle_default_value_delay) self.default_subtitle_track_name = str( subtitle_default_value_track_name) self.default_subtitle_set_default = subtitle_default_value_set_default self.default_subtitle_set_forced = subtitle_default_value_set_forced self.subtitle_set_default_disabled = subtitle_set_default_disabled self.subtitle_set_forced_disabled = subtitle_set_forced_disabled self.subtitle_name_label = QLabel("Subtitle Name:") self.subtitle_name_value = QLabel(str(subtitle_name)) self.subtitle_delay_label = QLabel("Subtitle Delay:") self.subtitle_delay_spin = QDoubleSpinBox() self.setup_subtitle_delay_spin() self.subtitle_language_label = QLabel("Subtitle Language:") self.subtitle_language_comboBox = QComboBox() self.setup_subtitle_language_comboBox() self.subtitle_track_name_label = QLabel("Subtitle Track Name:") self.subtitle_track_name_lineEdit = QLineEdit() self.setup_subtitle_track_name_lineEdit() self.subtitle_set_forced_label = QLabel("Subtitle Forced State:") self.subtitle_set_forced_checkBox = QCheckBox() self.setup_subtitle_set_forced_checkBox() self.subtitle_set_default_label = QLabel("Subtitle Default State:") self.subtitle_set_default_checkBox = QCheckBox() self.setup_subtitle_set_default_checkBox() self.yes_button = QPushButton("OK") self.no_button = QPushButton("Cancel") self.reset_button = QPushButton("Reset To Default") self.buttons_layout = QHBoxLayout() self.subtitle_delay_layout = QHBoxLayout() self.subtitle_language_layout = QHBoxLayout() self.subtitle_track_name_layout = QHBoxLayout() self.subtitle_set_default_layout = QHBoxLayout() self.subtitle_set_forced_layout = QHBoxLayout() self.buttons_layout.addWidget(QLabel(""), stretch=3) self.buttons_layout.addWidget(self.reset_button, stretch=2) self.buttons_layout.addWidget(self.yes_button, stretch=2) self.buttons_layout.addWidget(self.no_button, stretch=2) self.buttons_layout.addWidget(QLabel(""), stretch=3) self.subtitle_setting_layout = QGridLayout() self.subtitle_changeble_setting_layout = QFormLayout() self.subtitle_changeble_setting_layout.addRow(self.subtitle_name_label, self.subtitle_name_value) self.subtitle_changeble_setting_layout.addRow( self.subtitle_track_name_label, self.subtitle_track_name_lineEdit) self.subtitle_changeble_setting_layout.addRow( self.subtitle_language_label, self.subtitle_language_comboBox) self.subtitle_changeble_setting_layout.addRow( self.subtitle_delay_label, self.subtitle_delay_spin) self.subtitle_changeble_setting_layout.addRow( self.subtitle_set_default_label, self.subtitle_set_default_checkBox) self.subtitle_changeble_setting_layout.addRow( self.subtitle_set_forced_label, self.subtitle_set_forced_checkBox) self.subtitle_setting_layout.addLayout( self.subtitle_changeble_setting_layout, 0, 0, 5, 2) self.subtitle_setting_layout.addWidget(self.messageIcon, 0, 3, 5, -1) self.main_layout = QGridLayout() self.main_layout.addLayout(self.subtitle_setting_layout, 0, 0, 2, 3) self.main_layout.addLayout(self.buttons_layout, 2, 0, 1, -1) self.main_layout.setContentsMargins(20, 20, 20, 20) self.setLayout(self.main_layout) self.setup_ui() self.signal_connect() def setup_ui(self): self.disable_question_mark_window() self.messageIcon.setPixmap( QtGui.QPixmap(GlobalFiles.SubtitleIconPath).scaledToHeight(100)) self.set_dialog_values() self.set_default_buttons() if self.subtitle_set_default_disabled: self.subtitle_set_default_disable() if self.subtitle_set_forced_disabled: self.subtitle_set_forced_disable() if self.disable_edit: self.subtitle_track_name_lineEdit.setEnabled(False) self.subtitle_language_comboBox.setEnabled(False) self.subtitle_delay_spin.setEnabled(False) self.subtitle_set_default_checkBox.setEnabled(False) self.subtitle_set_forced_checkBox.setEnabled(False) self.reset_button.setEnabled(False) self.setup_tool_tip_hint_subtitle_set_default() self.setup_tool_tip_hint_subtitle_set_forced() def signal_connect(self): self.subtitle_track_name_lineEdit.textEdited.connect( self.update_current_subtitle_track_name) self.subtitle_delay_spin.editingFinished.connect( self.update_current_subtitle_delay) self.subtitle_language_comboBox.currentTextChanged.connect( self.update_current_subtitle_language) self.subtitle_set_default_checkBox.stateChanged.connect( self.update_current_subtitle_set_default) self.subtitle_set_forced_checkBox.stateChanged.connect( self.update_current_subtitle_set_forced) self.yes_button.clicked.connect(self.click_yes) self.no_button.clicked.connect(self.click_no) self.reset_button.clicked.connect(self.reset_subtitle_setting) def click_yes(self): self.state = "yes" self.close() def click_no(self): self.close() def set_dialog_values(self): self.setWindowTitle(self.window_title) self.setWindowIcon(GlobalFiles.InfoSettingIcon) def disable_question_mark_window(self): self.setWindowFlag(Qt.WindowContextHelpButtonHint, on=False) def increase_message_font_size(self, value): message_font = self.message.font() message_font.setPointSize(self.message.fontInfo().pointSize() + value) self.message.setFont(message_font) def set_default_buttons(self): self.yes_button.setDefault(True) self.yes_button.setFocus() def showEvent(self, a0: QtGui.QShowEvent) -> None: super().showEvent(a0) self.setFixedSize(self.size()) def setup_subtitle_track_name_lineEdit(self): self.subtitle_track_name_lineEdit.setClearButtonEnabled(True) self.subtitle_track_name_lineEdit.setText( self.current_subtitle_track_name) def setup_subtitle_language_comboBox(self): self.subtitle_language_comboBox.addItems(AllSubtitlesLanguages) self.subtitle_language_comboBox.setCurrentIndex( AllSubtitlesLanguages.index(self.current_subtitle_language)) self.subtitle_language_comboBox.setMaxVisibleItems(8) self.subtitle_language_comboBox.setStyleSheet( "QComboBox { combobox-popup: 0; }") def setup_subtitle_delay_spin(self): # self.subtitle_delay_spin.setMaximumWidth(screen_size.width() // 16) self.subtitle_delay_spin.setDecimals(3) self.subtitle_delay_spin.setMinimum(-9999.0) self.subtitle_delay_spin.setMaximum(9999.0) self.subtitle_delay_spin.setSingleStep(0.5) self.subtitle_delay_spin.setValue(float(self.current_subtitle_delay)) def setup_subtitle_set_default_checkBox(self): self.subtitle_set_default_checkBox.setText("Set Default") self.subtitle_set_default_checkBox.setChecked( bool(self.current_subtitle_set_default)) def setup_subtitle_set_forced_checkBox(self): self.subtitle_set_forced_checkBox.setText("Set Forced") self.subtitle_set_forced_checkBox.setChecked( bool(self.current_subtitle_set_forced)) def update_current_subtitle_track_name(self): self.current_subtitle_track_name = str( self.subtitle_track_name_lineEdit.text()) def update_current_subtitle_delay(self): self.current_subtitle_delay = round(self.subtitle_delay_spin.value(), 5) def update_current_subtitle_language(self): self.current_subtitle_language = str( self.subtitle_language_comboBox.currentText()) def update_current_subtitle_set_default(self): self.current_subtitle_set_default = ( self.subtitle_set_default_checkBox.checkState() == Qt.Checked) def update_current_subtitle_set_forced(self): self.current_subtitle_set_forced = ( self.subtitle_set_forced_checkBox.checkState() == Qt.Checked) def reset_subtitle_setting(self): self.current_subtitle_language = self.default_subtitle_language self.current_subtitle_delay = self.default_subtitle_delay self.current_subtitle_track_name = self.default_subtitle_track_name self.current_subtitle_set_default = self.default_subtitle_set_default self.current_subtitle_set_forced = self.default_subtitle_set_forced self.subtitle_language_comboBox.setCurrentIndex( AllSubtitlesLanguages.index(self.current_subtitle_language)) self.subtitle_delay_spin.setValue(float(self.current_subtitle_delay)) self.subtitle_track_name_lineEdit.setText( self.current_subtitle_track_name) self.subtitle_set_default_checkBox.setChecked( bool(self.current_subtitle_set_default)) self.subtitle_set_forced_checkBox.setChecked( bool(self.current_subtitle_set_forced)) def subtitle_set_default_disable(self): self.subtitle_set_default_checkBox.setDisabled(True) def subtitle_set_forced_disable(self): self.subtitle_set_forced_checkBox.setDisabled(True) def setup_tool_tip_hint_subtitle_set_default(self): if self.subtitle_set_default_checkBox.isEnabled(): self.subtitle_set_default_checkBox.setToolTip( "<nobr>set this subtitle to be the default subtitle track " "when play") self.subtitle_set_default_checkBox.setToolTipDuration(12000) else: self.subtitle_set_default_checkBox.setToolTip( "<nobr>set this subtitle to be the default subtitle track when play<br><b>Disabled</b> because " "option " "<b>make this subtitle default</b> is enabled on mux setting tab " ) self.subtitle_set_default_checkBox.setToolTipDuration(12000) def setup_tool_tip_hint_subtitle_set_forced(self): if self.subtitle_set_forced_checkBox.isEnabled(): self.subtitle_set_forced_checkBox.setToolTip( "<nobr>set this subtitle to be the forced subtitle track when " "play") self.subtitle_set_forced_checkBox.setToolTipDuration(12000) else: self.subtitle_set_forced_checkBox.setToolTip( "<nobr>set this subtitle to be the forced subtitle track when play<br><b>Disabled</b> because " "option " "<b>make this subtitle default and forced</b> is enabled on mux setting tab " ) self.subtitle_set_forced_checkBox.setToolTipDuration(12000) def execute(self): self.exec_()
class MainWindow(QMainWindow): # noinspection PyUnresolvedReferences def __init__(self, dbpath): super(MainWindow, self).__init__() # 'Add to collection' window self.addWindow = None # 'Import games' window self.importWindow = None # Side panel self.sidePanel = SidePanel() # Tables and their databases db = QSqlDatabase.addDatabase("QSQLITE") db.setDatabaseName(dbpath) if not db.open(): logger.critical(f"Couldn't open database: {db.lastError().text()}") QMessageBox.critical(None, "Database Error", db.lastError().text()) self.gamesTableView = Table("games", db) self.gamesTableView.doubleClick.connect(self.sidePanel.showDetails) self.consolesTableView = Table("consoles", db) self.consolesTableView.doubleClick.connect(self.sidePanel.showDetails) self.accessoriesTableView = Table("accessories", db) self.accessoriesTableView.doubleClick.connect(self.sidePanel.showDetails) self.tableViewList = [self.gamesTableView, self.consolesTableView, self.accessoriesTableView] self.allPlatforms = set() self.allRegions = set() self.allGenres = set() self.allYears = set() for table in self.tableViewList: for row in table.ownedItems(): self.allPlatforms.add(row["platform"]) self.allRegions.add(row["region"]) self.allYears.add(row["year"]) # Split multi-genre entries for genre in row["genre"].split(", "): self.allGenres.add(genre) self.filterDock = FilterDock(sorted(self.allPlatforms, key=str.lower), sorted(self.allRegions, key=str.lower), sorted(self.allGenres, key=str.lower), sorted(self.allYears, key=str.lower)) # Overview tab self.overview = Overview(self.tableViewList) # Randomizer tab self.randomizer = Randomizer(self.gamesTableView.ownedItems(), sorted(self.allPlatforms, key=str.lower), sorted(self.allGenres, key=str.lower)) self.randomizer.consoleList.itemClicked.connect(self.updateStatusbar) self.randomizer.genreList.itemClicked.connect(self.updateStatusbar) self.randomizer.genreMatchExclusiveCB.stateChanged.connect(self.updateStatusbar) self.randomizer.btnAll.clicked.connect(self.updateStatusbar) self.randomizer.btnNone.clicked.connect(self.updateStatusbar) ## MainWindow layout # Widgets self.centralWidget = QWidget() self.setCentralWidget(self.centralWidget) self.tab = QTabWidget() self.toolbar = self.addToolBar("Exit") self.toolbar.addAction(self.buttonActions("exit")) self.toolbar.addAction(self.buttonActions("add")) self.toolbar.addAction(self.buttonActions("import")) self.fileMenu = self.menuBar().addMenu(self.tr("&File")) self.fileMenu.addAction(self.buttonActions("add")) self.fileMenu.addAction(self.buttonActions("export")) self.fileMenu.addAction(self.buttonActions("import")) self.fileMenu.addAction(self.buttonActions("steam")) self.fileMenu.addAction(self.buttonActions("fetch")) self.fileMenu.insertSeparator(self.buttonActions("exit")) self.fileMenu.addAction(self.buttonActions("exit")) self.viewMenu = self.menuBar().addMenu(self.tr("&View")) self.viewMenu.addAction(self.buttonActions("owned")) self.viewMenu.addAction(self.buttonActions("delnotowned")) self.viewMenu.addAction(self.buttonActions("value")) self.helpMenu = self.menuBar().addMenu(self.tr("&Help")) self.helpMenu.addAction(self.buttonActions("about")) self.statusProgressBar = QProgressBar() self.statusProgressBar.setMaximumSize(100, 15) self.statusProgressBar.setRange(0, 0) self.statusProgressBar.setVisible(False) self.statusBar().addPermanentWidget(self.statusProgressBar) # Search stuff self.searchLabel = QLabel("Search") self.searchLabel.setVisible(False) self.searchBox = QLineEdit() self.searchBox.setVisible(False) self.searchBox.setClearButtonEnabled(True) # self.searchBox.textChanged.connect(self.search) self.searchBox.returnPressed.connect(self.search) self.searchBtn = QPushButton("Search") self.searchBtn.clicked.connect(self.search) self.searchBtn.setVisible(False) self.filterBtn = QPushButton("Filter") self.filterBtn.clicked.connect(self.filterDock.toggleVisibility) self.filterBtn.setVisible(False) # Tab layout. self.tab.addTab(self.overview.widget, "Overview") self.tab.addTab(self.gamesTableView, "Games") self.tab.addTab(self.consolesTableView, "Consoles") self.tab.addTab(self.accessoriesTableView, "Accessories") self.tab.addTab(self.randomizer.widget, "Randomizer") self.tab.currentChanged.connect(self.search) self.tab.currentChanged.connect(self.sidePanel.hideDetails) # Connect sidePanel's saved signal to corresponding table's updateData() # TODO: Update the sets of platforms and genres properly self.sidePanel.saved.connect(self.tableViewList[self.tab.currentIndex()].updateData) self.sidePanel.saved.connect(lambda: self.randomizer.updateLists(self.gamesTableView.ownedItems(), sorted(self.allPlatforms, key=str.lower), sorted(self.allGenres, key=str.lower))) # Main layout self.tabHbox = QHBoxLayout() self.tabHbox.addWidget(self.tab, 1) self.tabHbox.addWidget(self.sidePanel, 1) self.advSearchHbox = QHBoxLayout() self.advSearchHbox.addWidget(self.filterDock, 0) self.searchHbox = QHBoxLayout() self.searchHbox.addWidget(self.searchLabel, 0) self.searchHbox.addWidget(self.searchBox, 1) self.searchHbox.addWidget(self.filterBtn, 0) self.searchHbox.addWidget(self.searchBtn, 0) self.mainLayout = QVBoxLayout() self.mainLayout.addLayout(self.tabHbox, 1) self.mainLayout.addLayout(self.advSearchHbox, 0) self.mainLayout.addLayout(self.searchHbox, 0) self.centralWidget.setLayout(self.mainLayout) # Make sure screen geometry is big enough. Otherwise set window to maximized. gSize = QApplication.desktop().availableGeometry() if gSize.width() <= 1280 or gSize.height() <= 768: logger.info("Screen geometry smaller than 1280x768. Setting window to maximized mode.") self.showMaximized() else: self.resize(1280, 768) self.center() self.setWindowTitle(f"Game Collection Manager v{_VERSION}") self.statusBar().showMessage("") def about(self): aboutMsg = QMessageBox() aboutMsg.setIcon(QMessageBox.Information) aboutMsg.setWindowTitle("About") aboutMsg.setText("<h2>Game Collection Manager</h2>") aboutMsg.setInformativeText(f"Version {_VERSION}\n") aboutMsg.exec_() self.search() def addToCollection(self): """ Adds data to the collection using InputWindow """ # Loop until user enters valid data while True: self.addWindow = InputWindow(self.allPlatforms) if self.addWindow.exec_() == QDialog.Accepted: data = self.addWindow.returnData() if data['platform'].isspace() or data['name'] == "": msgBox = QMessageBox() msgBox.setIcon(QMessageBox.Information) msgBox.setWindowTitle("Invalid entry") msgBox.setText("Platform and name cannot be empty") msgBox.exec_() continue # Update Platform, Region, Genre, and Year in filter dock if necessary if data["platform"] not in self.allPlatforms: self.allPlatforms.add(data["platform"]) self.filterDock.updatePlatforms(sorted(self.allPlatforms, key=str.lower)) if data["region"] not in self.allRegions: self.allRegions.add(data["region"]) self.filterDock.updateRegions(sorted(self.allRegions, key=str.lower)) if data["genre"] not in self.allGenres: self.allGenres.add(data["genre"]) self.filterDock.updateGenres(sorted(self.allGenres, key=str.lower)) if data["year"] not in self.allYears: self.allYears.add(data["year"]) self.filterDock.updateYears(sorted(self.allYears, key=str.lower)) if "game" in data.keys(): self.gamesTableView.addData(data) self.overview.updateData(self.gamesTableView) self.randomizer.updateLists(self.gamesTableView.ownedItems(), sorted(self.allPlatforms, key=str.lower), sorted(self.allGenres, key=str.lower)) elif "console" in data.keys(): self.consolesTableView.addData(data) self.overview.updateData(self.consolesTableView) elif "accessory" in data.keys(): self.accessoriesTableView.addData(data) self.overview.updateData(self.accessoriesTableView) self.search() else: break def deleteFromCollection(self): currentTab = self.tab.currentIndex() if 0 < currentTab < 4: msgBox = QMessageBox() msgBox.setWindowTitle("Delete items") msgBox.setText("Are you sure?") msgBox.setIcon(QMessageBox.Warning) msgBox.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel) msgBox.setDefaultButton(QMessageBox.Cancel) ok = msgBox.exec_() if ok == QMessageBox.Ok: rows = [] indexes = self.tableViewList[currentTab-1].selectedIndexes() for index in indexes: rows.append(index.row()) self.tableViewList[currentTab-1].deleteData(rows) self.overview.updateData(self.tableViewList[currentTab-1]) if currentTab == 1: self.randomizer.updateLists(self.gamesTableView.ownedItems(), sorted(self.allPlatforms, key=str.lower), sorted(self.allGenres, key=str.lower)) self.search() def deleteNotOwned(self): """ Deletes items in table that are not owned. Not owned items are items that don't have either the item itself, the box, or the manual. """ currentTab = self.tab.currentIndex() if 0 < currentTab < 4: msgBox = QMessageBox() msgBox.setWindowTitle("Remove not owned items") msgBox.setText("Are you sure?") msgBox.setIcon(QMessageBox.Warning) msgBox.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel) msgBox.setDefaultButton(QMessageBox.Cancel) ok = msgBox.exec_() if ok == QMessageBox.Ok: self.tableViewList[currentTab-1].deleteNotOwned() self.search() def fetchInfo(self): """ Fetches info for all games from MobyGames. Sleeps for 5 seconds between game so we don't annoy their servers. :return: """ msgBox = QMessageBox() msgBox.setWindowTitle("Fetch info for all games") msgBox.setText("This will take a long time. Are you sure?") msgBox.setIcon(QMessageBox.Warning) msgBox.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel) msgBox.setDefaultButton(QMessageBox.Cancel) ok = msgBox.exec_() if ok == QMessageBox.Ok: games = self.gamesTableView.ownedItems() for game in games: info = getMobyRelease(game["name"], game["platform"], game["region"]) price = getPriceData(game["name"], game["platform"], game["region"]) paidPrice = game["price"].split(",")[0] info["price"] = ",".join((paidPrice, price["loose"], price["cib"], price["new"])) info["id"] = game["id"] self.gamesTableView.updateData(info) if "image" in info.keys() and info["image"] != "": coverDir = path.join("data", "images", "covers") id = str(game["id"]) + ".jpg" imageData = requests.get(info["image"]).content if not path.exists(path.join(coverDir, id)): with open(path.join(coverDir, id), "wb") as f: f.write(imageData) sleep(5) # Be nice def importToDatabase(self): """ Imports all games from selected platforms into database as not owned. This is to make it easier for the user to quickly go through the games in a platform and check which games they own. """ self.importWindow = ImportWindow() if self.importWindow.exec_() == QDialog.Accepted: data, platforms, regions = self.importWindow.returnData() self.statusProgressBar.setVisible(True) self.gamesTableView.addData(data) self.statusProgressBar.setVisible(False) for platform in platforms: if platform not in self.allPlatforms: self.allPlatforms.add(platform) self.filterDock.updatePlatforms(sorted(self.allPlatforms, key=str.lower)) for region in regions: if region not in self.allRegions: self.allRegions.add(region) self.filterDock.updateRegions(sorted(self.allRegions, key=str.lower)) self.search() def importSteamLibrary(self): apiKey, ok = QInputDialog.getText(self, "Import Steam Library", "Enter Steam API Key:") if ok and not (apiKey.isspace() or apiKey == ""): steamID, ok = QInputDialog.getText(self, "Import Steam Library", "Enter Steam User ID:") if ok and not (steamID.isspace() or steamID == ""): try: games = getSteamLibrary(apiKey, steamID) except (PermissionError, ValueError) as e: msgBox = QMessageBox(QMessageBox.Critical, "Error", "An error occured.") msgBox.setInformativeText(str(e)) msgBox.exec_() else: if "Steam" not in self.allPlatforms: self.allPlatforms.add("Steam") self.allRegions.add("Steam") self.filterDock.updatePlatforms(sorted(self.allPlatforms, key=str.lower)) self.filterDock.updateRegions(sorted(self.allRegions, key=str.lower)) self.gamesTableView.addData(games) else: # Only add games not already in collection existingGames = [] query = QSqlQuery() query.exec_("SELECT Name from games WHERE Region='Steam'") while query.next(): existingGames.append(query.value(0)) for game in games: if game["name"] not in existingGames: self.gamesTableView.addData(game) self.overview.updateData(self.gamesTableView) self.randomizer.updateLists(self.gamesTableView.ownedItems(), sorted(self.allPlatforms, key=str.lower), sorted(self.allGenres, key=str.lower)) self.search() def exportToCSV(self): def doexport(): filetype = filetypes.currentText() exportTables = [] db = self.consolesTableView.model.database() if tablesBox.currentIndex() == 0: for table in tables[1:]: exportTables.append(table.lower()) elif tablesBox.currentIndex() == 1: exportTables.append("games") elif tablesBox.currentIndex() == 2: exportTables.append("consoles") elif tablesBox.currentIndex() == 3: exportTables.append("accessories") sql2csv(db, exportTables, filetype) exportWindow.close() exportWindow = QDialog() tables = ["All", "Games", "Consoles", "Accessories"] tablesLabel = QLabel("Tables to export") tablesBox = QComboBox() # tablesBox.addItem(None, text="All") tablesBox.addItems(tables) tablesLayout = QHBoxLayout() tablesLayout.addWidget(tablesLabel) tablesLayout.addWidget(tablesBox) filetypesLabel = QLabel("Filetype") filetypes = QComboBox() filetypes.addItems(["csv", "tsv"]) filetypesLayout = QHBoxLayout() filetypesLayout.addWidget(filetypesLabel) filetypesLayout.addWidget(filetypes) # filenameLabel = QLabel("Filename") # filename = QLineEdit() # filesLayout = QHBoxLayout() # filesLayout.addWidget(filenameLabel) # filesLayout.addWidget(filename) ok = QPushButton("Ok") ok.clicked.connect(doexport) cancel = QPushButton("Cancel") cancel.clicked.connect(exportWindow.close) buttonLayout = QHBoxLayout() buttonLayout.addWidget(ok) buttonLayout.addWidget(cancel) layout = QVBoxLayout() layout.addLayout(tablesLayout) # layout.addLayout(filesLayout) layout.addLayout(filetypesLayout) layout.addLayout(buttonLayout) exportWindow.setLayout(layout) exportWindow.exec_() # noinspection PyCallByClass,PyTypeChecker def buttonActions(self, action: str) -> QAction: addAct = QAction(QIcon().fromTheme("list-add"), "&Add to collection...", self) addAct.setShortcut("Ctrl+A") addAct.setToolTip("Add to collection") addAct.triggered.connect(self.addToCollection) delText = "&Delete row" currentTab = self.tab.currentIndex() if 0 < currentTab < 4: if len(self.tableViewList[currentTab-1].selectedIndexes()) > 1: delText += "s" delAct = QAction(QIcon().fromTheme("edit-delete"), delText, self) delAct.setToolTip("Delete from collection") delAct.triggered.connect(self.deleteFromCollection) detAct = QAction(QIcon.fromTheme("text-x-generic-template"), "Details...", self) detAct.setToolTip("Open details side-panel") detAct.triggered.connect(self.tableViewList[currentTab-1].rowData) expAct = QAction(QIcon.fromTheme("text-x-generic-template"), "&Export as csv...", self) expAct.setShortcut("Ctrl+E") expAct.setToolTip("Export table as CSV file") expAct.triggered.connect(self.exportToCSV) impAct = QAction(QIcon.fromTheme("insert-object"), "&Import platform template...", self) impAct.setShortcut("Ctrl+I") impAct.setToolTip("Import games to database") impAct.triggered.connect(self.importToDatabase) stmAct = QAction(QIcon.fromTheme("insert-object"), "Import Steam Library...", self) stmAct.triggered.connect(self.importSteamLibrary) ownAct = QAction("Hide games not in collection", self) ownAct.setCheckable(True) ownAct.setChecked(True) ownAct.triggered.connect(self.toggleOwnedFilter) delNotOwned = QAction(QIcon().fromTheme("edit-delete"), "Remove items not in collection", self) delNotOwned.setToolTip("Remove items that are not owned from database") delNotOwned.triggered.connect(self.deleteNotOwned) aboutAct = QAction(QIcon.fromTheme("help-about"), "Abou&t", self) aboutAct.setToolTip("About Game Collection Manager") aboutAct.triggered.connect(self.about) exitAct = QAction(QIcon.fromTheme("application-exit"), "&Exit", self) exitAct.setShortcut("Ctrl+Q") exitAct.setToolTip("Exit application") exitAct.triggered.connect(self.close) infoAct = QAction("Debug: Print row info", self) infoAct.triggered.connect(self.info) fetchAct = QAction("Fetch info for all games...", self) fetchAct.setToolTip("Tries to fetch info for all games from MobyGames") fetchAct.triggered.connect(self.fetchInfo) valAct = QAction("Total value of collection", self) valAct.setToolTip("Rough estimate of the total value of collection") valAct.triggered.connect(self.totalValue) act = {"add": addAct, "del": delAct, "det": detAct, "export": expAct, "import": impAct, "steam": stmAct, "owned": ownAct, "delnotowned": delNotOwned, "about": aboutAct, "exit": exitAct, "info": infoAct, "fetch": fetchAct, "value": valAct} return act.get(action) def center(self): """Centers window on screen""" qr = self.frameGeometry() cp = QDesktopWidget().availableGeometry().center() qr.moveCenter(cp) self.move(qr.topLeft()) def contextMenuEvent(self, event): """Re-implements context menu functionality for our needs.""" cmenu = QMenu(self) currentTab = self.tab.currentIndex() if 0 < currentTab < 4: cmenu.addAction(self.buttonActions("det")) cmenu.addAction(self.buttonActions("del")) cmenu.addAction(self.buttonActions("info")) cmenu.exec_(self.mapToGlobal(event.pos())) self.search() def info(self): currentTab = self.tab.currentIndex() indexes = self.tableViewList[currentTab-1].selectedIndexes() rows = [index.row() for index in indexes] for _ in rows: self.tableViewList[currentTab-1].rowInfo() def search(self): """Filters table contents based on user input""" currentTab = self.tab.currentIndex() if 0 < currentTab < 4: searchText = self.searchBox.text() self.searchLabel.setVisible(True) self.searchBox.setVisible(True) self.filterBtn.setVisible(True) self.searchBtn.setVisible(True) self.filterDock.setItemType(currentTab) itemCount = self.tableViewList[currentTab - 1].filterTable(searchText, self.filterDock.getSelections()) if searchText != "" or len(self.filterDock.getSelections()) > 0: self.statusBar().showMessage("Found {} {}.".format(itemCount, self.tableViewList[currentTab-1].model.tableName())) else: self.updateStatusbar() else: self.searchLabel.setVisible(False) self.searchBox.setVisible(False) self.filterBtn.setVisible(False) self.searchBtn.setVisible(False) if self.filterDock.isVisible(): self.filterDock.toggleVisibility() def toggleOwnedFilter(self): for table in self.tableViewList: table.setHideNotOwned(False) if table.hideNotOwned\ else table.setHideNotOwned(True) currentTab = self.tab.currentIndex() if 0 < currentTab < 4: self.tableViewList[currentTab-1].filterTable(self.searchBox.text(), self.filterDock.getSelections()) self.search() def totalValue(self): value = 0.0 items = [] for table in self.tableViewList: items.extend(table.ownedItems()) for item in items: if item["item"] == "Yes" and item["box"] == "Yes" and item["manual"] == "Yes": price = item["price"].split(",")[2] else: price = item["price"].split(",")[1] if price == "N/A": value += 0 continue value += float(price.lstrip("$")) displayMsgBox("Collection value", "Rough estimate of collection's value.", f"<h2>${str(value)}</h2>", "information") def updateStatusbar(self): currentTab = self.tab.currentIndex() itemType = ["games", "consoles", "accessories"] if currentTab == 0: self.statusBar().showMessage("") elif 0 < currentTab < 4: numItems = self.tableViewList[currentTab-1].ownedCount if self.tableViewList[currentTab-1].hideNotOwned: self.statusBar().showMessage(f"{numItems} {itemType[currentTab-1]} in collection.") else: self.statusBar().showMessage("Showing {} {} ({} {} in collection).".format( self.tableViewList[currentTab-1].allCount, itemType[currentTab-1], numItems, itemType[currentTab-1])) elif currentTab == 4: platforms = self.randomizer.consoleList.selectedItems() genres = self.randomizer.genreList.selectedItems() self.statusBar().showMessage("Select platforms or genre to randomize from.") if len(platforms) > 0 or len(genres) > 0: self.statusBar().showMessage(f"Selecting from {self.randomizer.gameCount()} games.") return