Beispiel #1
0
    def __init__(self):
        super().__init__()
        self.ui = uic.loadUi('gui/qtchordii/mainwindow.ui', self)

        settings.set_up_settings()
        self.app_name = settings.APPLICATION_NAME

        args = parse_arguments()

        self.file_name = None
        self.songbook = Songbook()
        if args.project:
            self.project_file = os.path.abspath(args.project)
        else:
            self.project_file = settings.load_project_file()
            if not self.project_file:
                self.project_file, new_file = WelcomeDialog.get_project(self)
                if not self.project_file:
                    sys.exit(0)
                if new_file:
                    self.save_project()

        self.load_project(self.project_file)

        self.temp_dir = tempfile.mkdtemp()

        self.setup_file_widget()
        self.setup_editor()
        self.setup_file_menu()
        self.setup_geometry()
        self.dirty = False
Beispiel #2
0
class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.ui = uic.loadUi('gui/qtchordii/mainwindow.ui', self)

        settings.set_up_settings()
        self.app_name = settings.APPLICATION_NAME

        args = parse_arguments()

        self.file_name = None
        self.songbook = Songbook()
        if args.project:
            self.project_file = os.path.abspath(args.project)
        else:
            self.project_file = settings.load_project_file()
            if not self.project_file:
                self.project_file, new_file = WelcomeDialog.get_project(self)
                if not self.project_file:
                    sys.exit(0)
                if new_file:
                    self.save_project()

        self.load_project(self.project_file)

        self.temp_dir = tempfile.mkdtemp()

        self.setup_file_widget()
        self.setup_editor()
        self.setup_file_menu()
        self.setup_geometry()
        self.dirty = False

    def setup_file_menu(self):
        new_file_act = self.ui.actionNew
        new_file_act.setShortcut(QKeySequence.New)
        new_file_act.triggered.connect(self.new_file)

        import_files_act = self.ui.actionImport
        import_files_act.setShortcut(QKeySequence.Open)
        import_files_act.triggered.connect(self.import_chordpro_files)

        save_file_act = self.ui.actionSave
        save_file_act.setShortcut(QKeySequence.Save)
        save_file_act.triggered.connect(self.save_file)

        save_file_as_act = self.ui.actionSave_As
        save_file_as_act.setShortcut(QKeySequence.SaveAs)
        save_file_as_act.triggered.connect(self.save_file_as)

        load_project_file_act = self.ui.actionLoad_Project
        load_project_file_act.triggered.connect(self.select_project)

        save_project_file_act = self.ui.actionSave_Project
        save_project_file_act.triggered.connect(self.save_project)

        update_preview_act = self.ui.actionPreview
        update_preview_act.triggered.connect(self.update_preview)

        compile_songbook_act = self.ui.actionCompile_Songbook
        compile_songbook_act.triggered.connect(self.run_chordii)

        exit_act = self.ui.actionExit
        exit_act.setShortcut(QKeySequence.Quit)
        exit_act.triggered.connect(qApp.quit)

    def setup_file_widget(self):
        # TODO: Fix this
        # self.ui.fileWidget.resizeColumnsToContents()
        self.ui.fileWidget.itemSelectionChanged.connect(self.selection_changed)

    def setup_editor(self):
        self.ui.textEdit.set_main(self)
        self.ui.textEdit.textChanged.connect(self.set_dirty)

    def setup_geometry(self):
        geometries = settings.load_window_geometry()
        size = geometries[settings.key_size]
        if not size:
            size = QDesktopWidget().availableGeometry().size() * 4 / 5
        self.resize(size)

        is_full_screen = geometries[settings.key_is_full_screen]
        if is_full_screen:
            self.showFullScreen()

        splitter_sizes = geometries[settings.key_splitter_sizes]
        if not splitter_sizes:
            width = size.width()
            splitter_size = width * .2
            splitter_sizes = [splitter_size, (width - splitter_size) / 2, (width - splitter_size) / 2]
        self.ui.splitter.setSizes(splitter_sizes)

        pos = geometries[settings.key_pos]
        if not pos:
            center_point = QDesktopWidget().availableGeometry().center()
            geometry = self.frameGeometry()
            geometry.moveCenter(center_point)
            pos = geometry.topLeft()
        self.move(pos)

    def selection_changed(self):
        if len(self.ui.fileWidget.selectedItems()) == 2:
            if self.ok_to_continue():
                self.open_file(self.ui.fileWidget.selectedItems()[0].data(Qt.UserRole))

    def closeEvent(self, event):
        """
        Ask to save ChordPro file, save window geometry, and remove temp_dir
        """
        if self.ok_to_continue():
            settings.save_window_geometry(self.size(), self.pos(), self.isFullScreen(), self.ui.splitter.sizes())
            shutil.rmtree(self.temp_dir)
            event.accept()
        else:
            event.ignore()

    def set_dirty(self):
        """
        On change of text in textEdit window, set the flag "dirty" to True
        """
        if self.file_name:
            if self.dirty:
                return True
            self.dirty = True
            self.update_status('')

    def clear_dirty(self):
        """Clear the dirty."""
        self.dirty = False
        self.update_status('')

    def update_status(self, message):
        """
        Keep status current.
        """
        if self.file_name:
            file_base = os.path.basename(self.file_name)
            self.setWindowTitle(file_base + ' [*]' + ' — ' + self.app_name)
            self.ui.statusBar.showMessage(message, 5000)
            self.setWindowModified(self.dirty)

    def ok_to_continue(self):
        """
        Boolean result invocation method.
        """
        if self.dirty:
            reply = QMessageBox.question(self,
                                         self.app_name + " - Unsaved Changes",
                                         "Save unsaved changes?",
                                         QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel)
            if reply == QMessageBox.Cancel:
                return False
            elif reply == QMessageBox.Yes:
                return self.save_file()
        return True

    def new_file(self):
        if not self.ok_to_continue():
            return
        self.file_name = QFileDialog.getSaveFileName(self, self.tr("New file"), QDir.homePath(),
                                                     self.tr(CHORDPRO_FILTER))
        if not self.file_name[1]:
            return
        self.save_file()
        if self.ui.textEdit.isReadOnly():
            self.ui.textEdit.setReadOnly(False)
        self.ui.textEdit.setText("{t:}\n{st:}")
        self.ui.textEdit.setFocus()
        self.statusBar().showMessage('New file', 5000)

    def open_file(self, path=None):
        if self.ui.textEdit.isReadOnly():
            self.ui.textEdit.setReadOnly(False)

        self.file_name = path

        if self.file_name:
            in_stream = codecs.open(self.file_name, "r", "ISO-8859-1")
            if in_stream:
                self.ui.textEdit.setPlainText(in_stream.read())
        self.clear_dirty()

        self.update_preview()

        self.update_status('File opened.')
        self.tab2chordpro()

    def update_preview(self):
        if self.file_name:
            out_file = os.path.join(self.temp_dir, os.path.splitext(os.path.basename(self.file_name))[0])
            out_file = self.run_chordii(self.file_name, out_file, True)
            self.ui.scrollArea.load(out_file)

    def save_file(self):
        """
        Save file with current name.
        """
        if self.file_name is None:
            return self.save_file_as()
        else:
            if not self.dirty:
                return
            fname = self.file_name
            fl = codecs.open(fname, 'w', "ISO-8859-1")
            temp_text = self.ui.textEdit.toPlainText()
            if temp_text:
                fl.write(temp_text)
                fl.close()
                self.clear_dirty()
                self.update_status('Saved file')
                return True
            else:
                self.ui.statusBar.showMessage('Failed to save ...', 5000)
                return False

    def save_file_as(self):
        """
        Save file with a different name and maybe different directory.
        """
        path = self.file_name if self.file_name else os.path.dirname(self.project_file)
        fname = QFileDialog.getSaveFileName(self, self.tr("Save as..."), path, self.tr(CHORDPRO_FILTER))[0]
        if fname:
            print(fname)
            if "." not in fname:
                fname += ".cho"
            self.file_name = fname
            self.save_file()
            self.clear_dirty()

    def import_chordpro_files(self):
        working_dir = os.path.dirname(self.project_file)
        file_paths = QFileDialog.getOpenFileNames(self, self.tr("Choose files to import"),
                                                  working_dir if working_dir else QDir.homePath(),
                                                  self.tr(CHORDPRO_FILTER))[0]
        if not file_paths:
            return
        title_regex = re.compile(r'\{(?:t|title):(.+)\}')
        artist_regex = re.compile(r'\{(?:st|subtitle):(.+)\}')
        for file_path in file_paths:
            unknown = self.tr('Unknown')
            title = artist = unknown
            for line in open(file_path, 'r', encoding='ISO-8859-1'):
                match = title_regex.match(line)
                if match:
                    title = match.group(1).strip()
                match = artist_regex.match(line)
                if match:
                    artist = match.group(1).strip()
                if unknown not in (title, artist):
                    break
            self.songbook.add_song(title, artist, file_path)
        self.open_project()

    def select_project(self):
        filename = QFileDialog.getOpenFileName(self, self.tr("Open project"), QDir.homePath(),
                                               self.tr("Chordii project files (*.chproj)"))
        if filename:
            self.load_project(filename)

    def load_project(self, filename):
        self.songbook = Songbook()
        self.songbook.load(filename)
        self.open_project()
        settings.save_project_file(filename)

    def open_project(self):
        self.ui.fileWidget.clearContents()
        missing_songs = []
        for song in self.songbook.songs:
            if not os.path.isfile(song.file_path):
                missing_songs.append(song)
                self.songbook.songs.remove(song)
                continue
            self.add_song(song.title, song.artist, song.file_path)
        if missing_songs:
            message_box = QMessageBox(QMessageBox.Warning, self.tr('Missing files'),
                                      self.tr('The following project files were missing:'), QMessageBox.Ok, self)
            message_box.setInformativeText('<ul>' +
                                           ''.join(['<li>{} - {} ({})</li>'.format(song.artist, song.title,
                                                                                   song.file_path)
                                                    for song in missing_songs]) + '</ul>')
            message_box.setIcon(QMessageBox.Warning)
            message_box.show()
        self.ui.statusBar.showMessage("Project opened.", 5000)

    def add_song(self, song_title, song_artist, song_path):
        title = QTableWidgetItem(song_title)
        title.setData(Qt.UserRole, song_path)
        title.setSizeHint(QSize(0, 30))
        artist = QTableWidgetItem(song_artist)
        row = self.ui.fileWidget.rowCount()
        self.ui.fileWidget.insertRow(row)
        self.ui.fileWidget.setItem(row, 0, title)
        self.ui.fileWidget.setItem(row, 1, artist)

    def save_project(self):
        """
        Save the list of songs for later loading.
        """
        if self.project_file:
            self.songbook.save(self.project_file)
        self.ui.statusBar.showMessage("Project saved.", 5000)

    def run_chordii(self, input_file=None, output_file=None, preview=False):
        """
        Run Chordii to produce output.
        :param input_file: The name of the ChordPro file to compile. If None, all chordii files in the project will be
            compiled.
        :type input_file: str
        :param output_file: The filename of the resulting pdf. If None, a temp file will be created.
        :type output_file: str
        :param preview: Whether to compile a single song as a preview, or the whole project.
        :type preview: bool
        """

        chordii_command = which("chordii")
        if chordii_command is None:
            chordii_command = which("chordii430")
        if chordii_command is None:
            ret = QMessageBox.critical(self, self.tr(self.app_name + " - Chordii problem"),
                                       self.tr("Couldn't find a chordii executable in the PATH. \
                                       Please specify chordii's location to continue."),
                                       QMessageBox.Open | QMessageBox.Cancel, QMessageBox.Open)
            if ret == QMessageBox.Open:
                chordii_command = QFileDialog.getOpenFileName(self, self.tr("Specify the chordii executable"),
                                                              QDir.homePath())[0]
        command = [chordii_command, "-i", "-L", "-p", "1"] if not preview else [chordii_command]
        if not output_file:
            out_dir = os.path.join(os.path.dirname(self.project_file), "output")
            output_file = os.path.join(out_dir, self.songbook.name)
            if not os.path.exists(out_dir):
                os.makedirs(out_dir)
        if not input_file:
            for i in range(self.ui.fileWidget.rowCount()):
                command.append(self.ui.fileWidget.item(i, 0).data(Qt.UserRole))
        else:
            command.append(input_file)
        command.append("-o")
        command.append(output_file + '.ps')
        print('{}'.format(' '.join(map(str, command))))
        try:
            response = subprocess.check_output(command, stderr=subprocess.STDOUT).decode()
            if not preview:
                if response:
                    msg_box = WarningMessageBox()
                    msg_box.setWindowTitle(self.tr(self.app_name + " - Chordii warning"))
                    msg_box.setText(self.tr("Chordii exited with warnings."))
                    msg_box.setDetailedText(response)
                    msg_box.setIcon(QMessageBox.Warning)
                    msg_box.exec_()
                else:
                    QMessageBox.information(self, self.tr(self.app_name + " - Chordii was successful"),
                                            self.tr("Chordii compiled the songbook without warnings!"))
        except subprocess.CalledProcessError as e:
            if not preview:
                message = self.tr("Chordii crashed while compiling.")
                if e.stderr:
                    message += '<br' + self.tr("Chordii output:") + '<br><pre>' + e.stderr + '</pre>'
                else:
                    message += '<br>' + self.tr("Tip: This could be due to an incorrect chord definition.")
                QMessageBox.critical(self, self.tr(self.app_name + " - Chordii problem"), message)
        return ps2pdf(output_file)

    def tab2chordpro(self):
        notation = testTabFormat(self.ui.textEdit.toPlainText(), [enNotation])
        if notation is not None:
            res = QMessageBox.question(self, self.tr(self.app_name),
                                       self.tr("It seems this file is in the tab format.\n" +
                                               "Do you want to convert it to the ChordPro format?"),
                                       QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes)
            if res == QMessageBox.No:
                return
            self.ui.textEdit.setText(tab2ChordPro(self.ui.textEdit.toPlainText()))
Beispiel #3
0
 def load_project(self, filename):
     self.songbook = Songbook()
     self.songbook.load(filename)
     self.open_project()
     settings.save_project_file(filename)