class MainWindow(QMainWindow): def __init__(self): QMainWindow.__init__(self) self.resize(800, 600) self.setWindowTitle('PDF Merger') about = QAction('About', self) self.connect(about, SIGNAL('triggered()'), self.show_about) exit = QAction('Exit', self) exit.setShortcut('Ctrl+Q') self.connect(exit, SIGNAL('triggered()'), SLOT('close()')) self.statusBar() menubar = self.menuBar() file = menubar.addMenu('File') file.addAction(about) file.addAction(exit) self.main_widget = QWidget(self) self.setCentralWidget(self.main_widget) self.up_down_widget = QWidget(self) self.options_widget = QWidget(self) input_files_label = QLabel( "Input PDFs\nThis is the order in which the files will be merged too" ) self.files_list = QListWidget() self.files_list.setSelectionMode(QAbstractItemView.ExtendedSelection) add_button = QPushButton("Add PDF(s) to merge...") add_button.clicked.connect(self.clicked_add) up_button = QPushButton("Up") up_button.clicked.connect(self.move_file_up) down_button = QPushButton("Down") down_button.clicked.connect(self.move_file_down) remove_button = QPushButton("Remove PDF") remove_button.clicked.connect(self.remove_file) select_path_label = QLabel("Output PDF") self.dest_path_edit = QLineEdit() self.dest_path_edit.setReadOnly(True) select_path = QPushButton("Select...") select_path.clicked.connect(self.select_save_path) start = QPushButton("Start") start.clicked.connect(self.merge_pdf) up_down_vbox = QVBoxLayout(self.up_down_widget) up_down_vbox.addWidget(up_button) up_down_vbox.addWidget(down_button) up_down_vbox.addWidget(remove_button) self.up_down_widget.setLayout(up_down_vbox) group_input = QGroupBox() grid_input = QGridLayout() grid_input.addWidget(add_button, 0, 0) grid_input.addWidget(input_files_label, 1, 0) grid_input.addWidget(self.files_list, 2, 0) grid_input.addWidget(self.up_down_widget, 2, 1) group_input.setLayout(grid_input) group_output = QGroupBox() grid_output = QGridLayout() grid_output.addWidget(select_path_label, 0, 0) grid_output.addWidget(self.dest_path_edit, 1, 0) grid_output.addWidget(select_path, 1, 1) group_output.setLayout(grid_output) vbox_options = QVBoxLayout(self.options_widget) vbox_options.addWidget(group_input) vbox_options.addWidget(group_output) vbox_options.addWidget(start) self.options_widget.setLayout(vbox_options) splitter_filelist = QSplitter() splitter_filelist.setOrientation(Qt.Vertical) splitter_filelist.addWidget(self.options_widget) vbox_main = QVBoxLayout(self.main_widget) vbox_main.addWidget(splitter_filelist) vbox_main.setContentsMargins(0, 0, 0, 0) def show_about(self): #TODO add hyperlinks and create simple base website #TODO versioning system QMessageBox.about( self, 'About', 'PDF Merger\n2013 Nikola Peric\n\n' + 'http://www.example.com/\nhttps://github.com/nikolap/pdfmerger/\n\n' + 'Licensed under The MIT License\nhttp://opensource.org/licenses/MIT' ) def clicked_add(self): fname, _ = QFileDialog.getOpenFileNames( self, 'Select two or more PDFs to merge', QDir.homePath(), "*.pdf") self.files_list.addItems(fname) def move_file_up(self): sorted_selected_items = self.get_sorted_selected_items() if 0 not in sorted_selected_items: for row in sorted_selected_items: item = self.files_list.takeItem(row) self.files_list.insertItem(row - 1, item) def move_file_down(self): sorted_selected_items = self.get_sorted_selected_items(descending=True) if (self.files_list.count() - 1) not in sorted_selected_items: for row in sorted_selected_items: item = self.files_list.takeItem(row) self.files_list.insertItem(row + 1, item) def get_sorted_selected_items(self, descending=False): items_list = [] for item in self.files_list.selectedItems(): items_list.append(self.files_list.row(item)) return sorted(items_list, key=int, reverse=descending) def remove_file(self): for item in self.files_list.selectedItems(): row = self.files_list.row(item) self.files_list.takeItem(row) def select_save_path(self): fname, _ = QFileDialog.getSaveFileName(self, 'Save file', QDir.homePath(), "*.pdf") self.dest_path_edit.setText(fname) def merge_pdf(self): save_path = self.dest_path_edit.text() if save_path is '': raise Exception( QMessageBox.warning( self, 'Warning!', 'No location to save file selected.\n' + 'Cannot proceed with merger.')) input_files = [] for i in range(0, self.files_list.count()): file_path = self.files_list.item(i).text() if '.pdf' not in file_path and '.PDF' not in file_path: QMessageBox.warning( self, 'Warning!', 'Some files not PDFs\n' + 'Please examine' + file_path) raise Exception("PDF file error!") else: input_files.append(file_path) if len(input_files) >= 2: merge_pdf(destination=save_path, pdf_files=input_files) else: QMessageBox.warning( self, 'Warning!', 'Not enough PDFs selected.\n' + 'Please choose 2 or more files to merge.')
class Form(QDialog): def __init__(self, state, info, parent=None): super().__init__(parent) Lib.prepareModalDialog(self) self.state = state ListWidgetItem.info = info ListWidgetItem.refresh = self.refresh self.info = info self.helpPage = info.help self.createWidgets() self.layoutWidgets() self.setWindowTitle("{} — {}".format(self.info.name, QApplication.applicationName())) self.refresh() settings = QSettings() self.updateToolTips( bool( int( settings.value(Gopt.Key.ShowDialogToolTips, Gopt.Default.ShowDialogToolTips)))) def createWidgets(self): self.listWidget = QListWidget() self.buttonLayout = QVBoxLayout() for icon, text, slot, tip in ((":/add.svg", "&Add", self.add, """\ <p><b>Add</b></p><p>Add an item to the {} list.</p>""".format(self.info.name)), (":/edit.svg", "&Edit", self.edit, """\ <p><b>Edit</b></p><p>Edit the {} list's current item.</p>""".format(self.info.name)), (":/delete.svg", "&Remove...", self.remove, """\ <p><b>Remove</b></p><p>Remove the {} list's current item.</p>""".format(self.info.name)), (":/help.svg", "Help", self.help, """\ Help on the {} dialog""".format(self.info.name)), (":/dialog-close.svg", "&Close", self.accept, """\ <p><b>Close</b></p><p>Close the dialog.</p>""")): button = QPushButton(QIcon(icon), text) button.setFocusPolicy(Qt.NoFocus) if text in {"&Close", "Help"}: self.buttonLayout.addStretch() self.buttonLayout.addWidget(button) button.clicked.connect(slot) self.tooltips.append((button, tip)) self.tooltips.append((self.listWidget, self.info.desc)) def layoutWidgets(self): layout = QHBoxLayout() layout.addWidget(self.listWidget) layout.addLayout(self.buttonLayout) self.setLayout(layout) def refresh(self, *, text=None): self.listWidget.clear() cursor = self.info.db.cursor() for row, record in enumerate(cursor.execute(self.info.SELECT)): item = ListWidgetItem(record[0]) item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEditable | Qt.ItemIsEnabled) item.setBackground(self.palette().base() if row % 2 else self.palette().alternateBase()) self.listWidget.addItem(item) if self.listWidget.count(): self.listWidget.setCurrentRow(0) if text is not None: for i in range(self.listWidget.count()): if self.listWidget.item(i).text() == text: self.listWidget.setCurrentRow(i) break def add(self): item = ListWidgetItem(ADDING) item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEditable | Qt.ItemIsEnabled) self.listWidget.insertItem(0, item) self.listWidget.setCurrentRow(0) self.listWidget.editItem(item) def edit(self): item = self.listWidget.currentItem() if item is not None: self.listWidget.editItem(item) def remove(self): # No need to restore focus widget row = self.listWidget.currentRow() item = self.listWidget.item(row) if item is None: return with Lib.Qt.DisableUI(self, forModalDialog=True): reply = QMessageBox.question( self, "Remove {} — {}".format(self.info.name, QApplication.applicationName()), "Remove {} “{}”?".format(self.info.name, item.text()), QMessageBox.Yes | QMessageBox.No) if reply == QMessageBox.Yes: cursor = self.info.db.cursor() cursor.execute(self.info.DELETE, (item.text(), )) item = self.listWidget.takeItem(row) del item def help(self): self.state.help(self.helpPage)
class MainWindow(QMainWindow): def __init__(self): QMainWindow.__init__(self) self.resize(800,600) self.setWindowTitle('PDF Merger') about = QAction('About', self) self.connect(about, SIGNAL('triggered()'), self.show_about) exit = QAction('Exit', self) exit.setShortcut('Ctrl+Q') self.connect(exit, SIGNAL('triggered()'), SLOT('close()')) self.statusBar() menubar = self.menuBar() file = menubar.addMenu('File') file.addAction(about) file.addAction(exit) self.main_widget = QWidget(self) self.setCentralWidget(self.main_widget) self.up_down_widget = QWidget(self) self.options_widget = QWidget(self) input_files_label = QLabel("Input PDFs\nThis is the order in which the files will be merged too") self.files_list = QListWidget() self.files_list.setSelectionMode(QAbstractItemView.ExtendedSelection) add_button = QPushButton("Add PDF(s) to merge...") add_button.clicked.connect(self.clicked_add) up_button = QPushButton("Up") up_button.clicked.connect(self.move_file_up) down_button = QPushButton("Down") down_button.clicked.connect(self.move_file_down) remove_button = QPushButton("Remove PDF") remove_button.clicked.connect(self.remove_file) select_path_label = QLabel("Output PDF") self.dest_path_edit = QLineEdit() self.dest_path_edit.setReadOnly(True) select_path = QPushButton("Select...") select_path.clicked.connect(self.select_save_path) start = QPushButton("Start") start.clicked.connect(self.merge_pdf) up_down_vbox = QVBoxLayout(self.up_down_widget) up_down_vbox.addWidget(up_button) up_down_vbox.addWidget(down_button) up_down_vbox.addWidget(remove_button) self.up_down_widget.setLayout(up_down_vbox) group_input = QGroupBox() grid_input = QGridLayout() grid_input.addWidget(add_button, 0, 0) grid_input.addWidget(input_files_label, 1, 0) grid_input.addWidget(self.files_list, 2, 0) grid_input.addWidget(self.up_down_widget, 2, 1) group_input.setLayout(grid_input) group_output = QGroupBox() grid_output = QGridLayout() grid_output.addWidget(select_path_label, 0, 0) grid_output.addWidget(self.dest_path_edit, 1, 0) grid_output.addWidget(select_path, 1, 1) group_output.setLayout(grid_output) vbox_options = QVBoxLayout(self.options_widget) vbox_options.addWidget(group_input) vbox_options.addWidget(group_output) vbox_options.addWidget(start) self.options_widget.setLayout(vbox_options) splitter_filelist = QSplitter() splitter_filelist.setOrientation(Qt.Vertical) splitter_filelist.addWidget(self.options_widget) vbox_main = QVBoxLayout(self.main_widget) vbox_main.addWidget(splitter_filelist) vbox_main.setContentsMargins(0,0,0,0) def show_about(self): #TODO add hyperlinks and create simple base website #TODO versioning system QMessageBox.about(self, 'About', 'PDF Merger\n2013 Nikola Peric\n\n' + 'http://www.example.com/\nhttps://github.com/nikolap/pdfmerger/\n\n' + 'Licensed under The MIT License\nhttp://opensource.org/licenses/MIT' ) def clicked_add(self): fname, _ = QFileDialog.getOpenFileNames(self, 'Select two or more PDFs to merge', QDir.homePath(), "*.pdf") self.files_list.addItems(fname) def move_file_up(self): sorted_selected_items = self.get_sorted_selected_items() if 0 not in sorted_selected_items: for row in sorted_selected_items: item = self.files_list.takeItem(row) self.files_list.insertItem(row - 1, item) def move_file_down(self): sorted_selected_items = self.get_sorted_selected_items(descending=True) if (self.files_list.count() - 1) not in sorted_selected_items: for row in sorted_selected_items: item = self.files_list.takeItem(row) self.files_list.insertItem(row + 1, item) def get_sorted_selected_items(self, descending=False): items_list = [] for item in self.files_list.selectedItems(): items_list.append(self.files_list.row(item)) return sorted(items_list, key=int, reverse = descending) def remove_file(self): for item in self.files_list.selectedItems(): row = self.files_list.row(item) self.files_list.takeItem(row) def select_save_path(self): fname, _ = QFileDialog.getSaveFileName(self, 'Save file', QDir.homePath(), "*.pdf") self.dest_path_edit.setText(fname) def merge_pdf(self): save_path = self.dest_path_edit.text() if save_path is '': raise Exception(QMessageBox.warning(self, 'Warning!', 'No location to save file selected.\n' + 'Cannot proceed with merger.')) input_files = [] for i in range(0, self.files_list.count()): file_path = self.files_list.item(i).text() if '.pdf' not in file_path and '.PDF' not in file_path: QMessageBox.warning(self, 'Warning!', 'Some files not PDFs\n' + 'Please examine' + file_path) raise Exception("PDF file error!") else: input_files.append(file_path) if len(input_files) >= 2: merge_pdf(destination=save_path, pdf_files=input_files) else: QMessageBox.warning(self, 'Warning!', 'Not enough PDFs selected.\n' + 'Please choose 2 or more files to merge.')
class Form(QDialog): def __init__(self, term, state, parent=None): super().__init__(parent) Lib.prepareModalDialog(self) self.addingText = term self.state = state ListWidgetItem.model = state.model ListWidgetItem.refresh = self.refresh self.buttons = [] self.createWidgets() self.layoutWidgets() self.createConnections() self.setWindowTitle("Edit Groups — {}".format( QApplication.applicationName())) self.refresh() settings = QSettings() self.updateToolTips( bool( int( settings.value(Gopt.Key.ShowDialogToolTips, Gopt.Default.ShowDialogToolTips)))) def createWidgets(self): self.listWidget = QListWidget() self.buttonLayout = QVBoxLayout() self.linkButton = None for icon, text, slot, tip in ((":/add.svg", "&Add", self.add, """\ <p><b>Add</b> (Alt+A)</p><p>Add a new Normal Group.</p>"""), (":/edit.svg", "&Rename", self.rename, """\ <p><b>Rename</b> (Alt+R)</p><p>Rename the current Group.</p>"""), (":/grouplink.svg", "&Link", self.link, """\ <p><b>Link</b> (Alt+L)</p><p>Change the current group into a Linked Group.</p> <p>This means that the pages of every entry in this group will be merged and synchronized, and any future changes to the pages of any entries in this group will be propagated to all the other entries in this group to keep them all synchronized.</p>"""), (":/groups.svg", "&Unlink", self.unlink, """\ <p><b>Unlink</b> (Alt+U)</p><p>Change the current group into a Normal (unlinked) Group. If the linked group has entries, the <b>Delete Linked Group</b> dialog will pop up.</p>"""), (":/groupset.svg", "&View", self.viewGroup, """\ <p><b>View</b> (Alt+V)</p><p>View the current group in the Filtered View.</p>"""), (":/delete.svg", "&Delete", self.delete, """\ <p><b>Delete</b> (Alt+D)</p><p>Remove entries from the current normal group and then delete the group. If the current group is a linked group that has entries, the <b>Delete Linked Group</b> dialog will pop up.</p>"""), (":/help.svg", "Help", self.help, """\ Help on the Groups dialog"""), (":/dialog-close.svg", "&Close", self.accept, """\ <p><b>Close</b></p><p>Close the dialog.</p>""")): button = QPushButton(QIcon(icon), text) button.setFocusPolicy(Qt.NoFocus) if text in {"&Close", "Help"}: self.buttonLayout.addStretch() else: self.buttons.append(button) self.buttonLayout.addWidget(button) button.clicked.connect(slot) self.tooltips.append((button, tip)) if text == "&Link": self.linkButton = button button.setEnabled(False) elif text == "&Unlink": self.unlinkButton = button button.setEnabled(False) self.tooltips.append((self.listWidget, "List of Groups")) def layoutWidgets(self): layout = QHBoxLayout() layout.addWidget(self.listWidget) layout.addLayout(self.buttonLayout) self.setLayout(layout) def createConnections(self): self.listWidget.currentRowChanged.connect(self.updateUi) def updateUi(self): for button in self.buttons: button.setEnabled(True) item = self.listWidget.currentItem() if item is not None: gid = item.data(Qt.UserRole) self.linkButton.setEnabled(self.state.model.safeToLinkGroup(gid)) self.unlinkButton.setEnabled(self.state.model.isLinkedGroup(gid)) def refresh(self, *, text=None, index=None): self.listWidget.clear() for row, (gid, name, linked) in enumerate(self.state.model.allGroups()): item = ListWidgetItem(name) item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEditable | Qt.ItemIsEnabled) item.setBackground(self.palette().base() if row % 2 else self.palette().alternateBase()) item.setData(Qt.UserRole, gid) item.setIcon( QIcon(":/grouplink.svg" if linked else ":/groups.svg")) self.listWidget.addItem(item) if self.listWidget.count(): self.listWidget.setCurrentRow(0) if index is not None: self.listWidget.setCurrentRow(index) elif text is not None: for i in range(self.listWidget.count()): if self.listWidget.item(i).text() == text: self.listWidget.setCurrentRow(i) break self.updateUi() def add(self): item = ListWidgetItem(self.addingText, adding=True) item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEditable | Qt.ItemIsEnabled) self.listWidget.insertItem(0, item) self.listWidget.setCurrentRow(0) for button in self.buttons: button.setEnabled(False) self.listWidget.editItem(item) def rename(self): item = self.listWidget.currentItem() if item is not None: for button in self.buttons: button.setEnabled(False) self.listWidget.editItem(item) def link(self): item = self.listWidget.currentItem() if item is not None: gid = item.data(Qt.UserRole) if not self.state.model.safeToLinkGroup(gid): return # Should never happen self.state.model.linkGroup(gid) self.refresh(index=self.listWidget.currentRow()) def unlink(self): item = self.listWidget.currentItem() if item is not None: gid = item.data(Qt.UserRole) if not self.state.model.groupMemberCount(gid): self.state.model.unlinkGroup(gid) else: with Lib.Qt.DisableUI(self, forModalDialog=True): form = Forms.DeleteOrUnlink.Form("Unlink", gid, self.state, self) form.exec_() self.refresh(index=self.listWidget.currentRow()) def viewGroup(self): item = self.listWidget.currentItem() if item: self.state.viewFilteredPanel.setGroup(item) def delete(self): # No need to restore focus widget row = self.listWidget.currentRow() item = self.listWidget.item(row) if item is None: return gid = item.data(Qt.UserRole) for button in self.buttons: button.setEnabled(False) deleteItem = False if (not self.state.model.isLinkedGroup(gid) or not self.state.model.groupMemberCount(gid)): with Lib.Qt.DisableUI(self, forModalDialog=True): reply = QMessageBox.question( self, "Delete Group — {}".format(QApplication.applicationName()), "<p>Delete Group “{}”?</p>".format(item.text()), QMessageBox.Yes | QMessageBox.No) if reply == QMessageBox.Yes: self.state.model.deleteGroup(gid) deleteItem = True else: with Lib.Qt.DisableUI(self, forModalDialog=True): form = Forms.DeleteOrUnlink.Form("Delete", gid, self.state, self) deleteItem = form.exec_() if deleteItem: item = self.listWidget.takeItem(row) del item self.updateUi() def help(self): self.state.help("xix_ref_dlg_groups.html")